├── .gitignore ├── .editorconfig ├── .clang-format ├── workflow-polaris-config.cmake.in ├── .github └── workflows │ └── ci.yml ├── example ├── CMakeLists.txt ├── BUILD ├── consumer_demo.cc ├── provider_demo.cc └── local_test.cc ├── test ├── BUILD └── polaris_policy_unittest.cc ├── BUILD ├── GNUmakefile ├── WORKSPACE ├── CMakeLists.txt ├── src ├── PolarisClient.h ├── PolarisManager.h ├── PolarisCluster.h ├── PolarisClient.cc ├── PolarisTask.h ├── PolarisPolicy.h ├── PolarisConfig.h ├── PolarisManager.cc ├── PolarisPolicy.cc └── PolarisTask.cc ├── README.md ├── polaris.yaml.template └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | build.cmake 2 | workflow-polaris-config.cmake 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # all files 5 | [*] 6 | indent_style = tab 7 | indent_size = 4 -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -2 5 | IndentWidth: 4 6 | ColumnLimit: 100 7 | DerivePointerAlignment: false 8 | PointerAlignment: Right 9 | SortIncludes: false 10 | ... 11 | -------------------------------------------------------------------------------- /workflow-polaris-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | set(WORKFLOW_POLARIS_VERSION "@workflow-polaris_VERSION@") 4 | set_and_check(WORKFLOW_POLARIS_INCLUDE_DIR "@PACKAGE_INCLUDE_DIR@") 5 | set_and_check(WORKFLOW_POLARIS_LIBRARIES_DIR "@PACKAGE_LIBRARIES_DIR@") 6 | 7 | check_required_components(workflow-polaris) 8 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | compile-witm-bazel: 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - uses: actions/checkout@master 14 | - name: bazel build 15 | run: bazel build ... 16 | - name: bazel test 17 | run: bazel test test:unittest 18 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | 3 | include_directories(${WORKFLOW_INCLUDE_DIR}) 4 | 5 | foreach(EXE provider_demo consumer_demo local_test) 6 | add_executable(${EXE} ${EXE}.cc) 7 | target_link_libraries(${EXE} 8 | ${LIBRARY_NAME} 9 | ${YAML_CPP_LIBRARIES} 10 | ${WORKFLOW_LIB_DIR}/libworkflow.a 11 | ssl crypto pthread 12 | ) 13 | endforeach(EXE) 14 | -------------------------------------------------------------------------------- /test/BUILD: -------------------------------------------------------------------------------- 1 | cc_test( 2 | name = "unittest", 3 | srcs = ["polaris_policy_unittest.cc"], 4 | copts = ["-Iexternal/gtest/include", "-Isrc/"], 5 | deps = [ 6 | "//:workflow-polaris", 7 | "@com_github_sogou_workflow//:http", 8 | "@com_github_sogou_workflow//:upstream", 9 | "@com_github_sogou_workflow//:workflow_hdrs", 10 | "@com_google_googletest//:gtest", 11 | "@com_google_googletest//:gtest_main", 12 | ], 13 | ) 14 | 15 | -------------------------------------------------------------------------------- /BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test", "cc_binary") 2 | 3 | cc_library( 4 | name = "workflow-polaris", 5 | hdrs = glob(["**/*.h", "**/*.hpp"]), 6 | srcs = glob(["src/*.cc"]), 7 | deps = [ 8 | "@com_github_sogou_workflow//:http", 9 | "@com_github_sogou_workflow//:upstream", 10 | "@com_github_jbeder_yaml_cpp//:yaml-cpp", 11 | ], 12 | linkopts = [ 13 | "-lpthread", 14 | "-lssl", 15 | "-lcrypto", 16 | ], 17 | visibility = ["//visibility:public"] 18 | ) 19 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | CMAKE3 := $(shell if which cmake3>/dev/null ; then echo cmake3; else echo cmake; fi;) 3 | BUILD_DIR := build.cmake 4 | 5 | .PHONY: all clean 6 | 7 | all: 8 | mkdir -p $(BUILD_DIR) 9 | 10 | ifneq ("${Workflow_DIR}x", "x") 11 | cd $(BUILD_DIR) && $(CMAKE3) -D Workflow_DIR=${Workflow_DIR} $(ROOT_DIR) 12 | else 13 | cd $(BUILD_DIR) && $(CMAKE3) $(ROOT_DIR) 14 | endif 15 | 16 | make -C $(BUILD_DIR) -f Makefile 17 | 18 | clean: 19 | rm -rf $(BUILD_DIR) 20 | -------------------------------------------------------------------------------- /example/BUILD: -------------------------------------------------------------------------------- 1 | cc_binary( 2 | name = 'consumer_demo', 3 | srcs = ['consumer_demo.cc'], 4 | copts = ['-Isrc/'], 5 | deps = [ 6 | '//:workflow-polaris', 7 | '@com_github_sogou_workflow//:http', 8 | '@com_github_sogou_workflow//:upstream', 9 | '@com_github_sogou_workflow//:workflow_hdrs', 10 | ], 11 | ) 12 | 13 | cc_binary( 14 | name = 'provider_demo', 15 | srcs = ['provider_demo.cc'], 16 | copts = ['-Isrc/'], 17 | deps = [ 18 | '//:workflow-polaris', 19 | '@com_github_sogou_workflow//:http', 20 | '@com_github_sogou_workflow//:upstream', 21 | '@com_github_sogou_workflow//:workflow_hdrs', 22 | ], 23 | ) 24 | 25 | cc_binary( 26 | name = 'local_test', 27 | srcs = ['local_test.cc'], 28 | copts = ['-Isrc/'], 29 | deps = [ 30 | '//:workflow-polaris', 31 | '@com_github_sogou_workflow//:http', 32 | '@com_github_sogou_workflow//:upstream', 33 | '@com_github_sogou_workflow//:workflow_hdrs', 34 | ], 35 | ) 36 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | workspace(name = "workflow-polaris") 2 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | 5 | git_repository( 6 | name = "com_github_sogou_workflow", 7 | remote = "https://github.com/sogou/workflow.git", 8 | tag = "v0.10.7", 9 | ) 10 | 11 | http_archive( 12 | name = "bazel_skylib", 13 | urls = [ 14 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", 15 | "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", 16 | ], 17 | sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", 18 | ) 19 | 20 | http_archive( 21 | name = "com_google_googletest", 22 | url = "https://github.com/google/googletest/archive/release-1.11.0.zip", 23 | strip_prefix = "googletest-release-1.11.0", 24 | ) 25 | 26 | git_repository( 27 | name = "com_github_jbeder_yaml_cpp", 28 | remote = "https://github.com/jbeder/yaml-cpp.git", 29 | tag = "yaml-cpp-0.7.0", 30 | ) 31 | 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | 3 | project( 4 | workflow-polaris 5 | VERSION 0.1.0 6 | LANGUAGES CXX 7 | ) 8 | 9 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 10 | set(CMAKE_SKIP_RPATH TRUE) 11 | set(CMAKE_CXX_STANDARD 11) 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) 13 | 14 | if(Workflow_DIR) 15 | find_package(Workflow REQUIRED CONFIG HINTS ${Workflow_DIR}) 16 | else() 17 | find_package(Workflow REQUIRED CONFIG) 18 | endif() 19 | message("Use workflow at " ${WORKFLOW_INCLUDE_DIR}) 20 | 21 | find_package(Yaml-cpp-static) 22 | if(Yaml-cpp-static_FOUND) 23 | message("Use yaml-cpp static library") 24 | else() 25 | find_package(Yaml-cpp REQUIRED) 26 | message("Use yaml-cpp dynamic library") 27 | endif() 28 | 29 | set(LIBRARY_NAME workflow-polaris) 30 | add_library(${LIBRARY_NAME} STATIC 31 | src/PolarisClient.cc 32 | src/PolarisConfig.cc 33 | src/PolarisManager.cc 34 | src/PolarisPolicy.cc 35 | src/PolarisTask.cc 36 | ) 37 | 38 | include_directories( 39 | ${WORKFLOW_INCLUDE_DIR} 40 | ${YAML_CPP_INCLUDE_DIR} 41 | ) 42 | 43 | target_include_directories(${LIBRARY_NAME} PUBLIC 44 | ${CMAKE_CURRENT_SOURCE_DIR}/src 45 | ) 46 | 47 | add_subdirectory(example) 48 | 49 | include(CMakePackageConfigHelpers) 50 | set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 51 | set(LIBRARIES_DIR ${CMAKE_CURRENT_BINARY_DIR}) 52 | configure_package_config_file( 53 | ${LIBRARY_NAME}-config.cmake.in 54 | ${PROJECT_SOURCE_DIR}/${LIBRARY_NAME}-config.cmake 55 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME} 56 | PATH_VARS INCLUDE_DIR LIBRARIES_DIR 57 | ) 58 | -------------------------------------------------------------------------------- /src/PolarisClient.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISCLIENT_H_ 2 | #define _POLARISCLIENT_H_ 3 | 4 | #include 5 | #include 6 | #include "PolarisTask.h" 7 | #include "PolarisCluster.h" 8 | 9 | namespace polaris { 10 | 11 | class PolarisClient { 12 | public: 13 | PolarisClient(); 14 | 15 | int init(const std::string &url); 16 | 17 | PolarisTask *create_discover_task(const std::string &service_namespace, 18 | const std::string &service_name, int retry, 19 | polaris_callback_t cb); 20 | 21 | PolarisTask *create_register_task(const std::string &service_namespace, 22 | const std::string &service_name, int retry, 23 | polaris_callback_t cb); 24 | 25 | PolarisTask *create_deregister_task(const std::string &service_namespace, 26 | const std::string &service_name, int retry, 27 | polaris_callback_t cb); 28 | 29 | PolarisTask *create_ratelimit_task(const std::string &service_namespace, 30 | const std::string &service_name, int retry, 31 | polaris_callback_t cb); 32 | 33 | PolarisTask *create_circuitbreaker_task(const std::string &service_namespace, 34 | const std::string &service_name, int retry, 35 | polaris_callback_t cb); 36 | 37 | PolarisTask *create_heartbeat_task(const std::string &service_namespace, 38 | const std::string &service_name, int retry, 39 | polaris_callback_t cb); 40 | 41 | public: 42 | virtual ~PolarisClient(); 43 | void deinit(); 44 | 45 | private: 46 | PolarisProtocol protocol; 47 | PolarisCluster *cluster; 48 | friend class PolarisTask; 49 | }; 50 | 51 | }; // namespace polaris 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/PolarisManager.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISMANAGER_H_ 2 | #define _POLARISMANAGER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "workflow/WFTask.h" 9 | #include "workflow/WFTaskFactory.h" 10 | #include "workflow/WFFacilities.h" 11 | #include "PolarisClient.h" 12 | #include "PolarisPolicy.h" 13 | 14 | namespace polaris { 15 | 16 | class Manager; 17 | 18 | class PolarisManager 19 | { 20 | public: 21 | PolarisManager(const std::string& polaris_url); 22 | PolarisManager(const std::string& polaris_url, 23 | const std::string& yaml_file); 24 | PolarisManager(const std::string& polaris_url, 25 | const std::string& platform_id, 26 | const std::string& platform_token, 27 | const std::string& yaml_file); 28 | ~PolarisManager(); 29 | 30 | int watch_service(const std::string& service_namespace, 31 | const std::string& service_name); 32 | int unwatch_service(const std::string& service_namespace, 33 | const std::string& service_name); 34 | 35 | int register_service(const std::string& service_namespace, 36 | const std::string& service_name, 37 | PolarisInstance instance); 38 | int register_service(const std::string& service_namespace, 39 | const std::string& service_name, 40 | const std::string& service_token, 41 | PolarisInstance instance); 42 | int register_service(const std::string& service_namespace, 43 | const std::string& service_name, 44 | const std::string& service_token, 45 | int heartbeat_interval, 46 | PolarisInstance instance); 47 | int deregister_service(const std::string& service_namespace, 48 | const std::string& service_name, 49 | PolarisInstance instance); 50 | int deregister_service(const std::string& service_namespace, 51 | const std::string& service_name, 52 | const std::string& service_token, 53 | PolarisInstance instance); 54 | int get_error() const; 55 | void get_watching_list(std::vector& list); 56 | void get_register_list(std::vector& list); 57 | 58 | private: 59 | Manager *ptr; 60 | }; 61 | 62 | }; // namespace polaris 63 | 64 | #endif 65 | 66 | -------------------------------------------------------------------------------- /example/consumer_demo.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "PolarisManager.h" 4 | #include "workflow/WFFacilities.h" 5 | 6 | using namespace polaris; 7 | 8 | WFFacilities::WaitGroup wait_group(1); 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | if (argc != 5 && argc != 7) { 13 | fprintf(stderr, "USAGE:\n\t%s " 14 | " [ ] \n\n" 15 | "QUERY URL FORMAT:\n" 16 | "\thttp://callee_service_namespace.callee_service_name:port" 17 | "#k1=v1&caller_service_namespace.caller_service_name\n\n" 18 | "EXAMPLE:\n\t%s http://127.0.0.1:8090 " 19 | "default workflow.polaris.service.b " 20 | "\"http://default.workflow.polaris.service.b:8080" 21 | "#k1_env=v1_base&k2_number=v2_prime&a_namespace.a\"\n\n", 22 | argv[0], argv[0]); 23 | exit(1); 24 | } 25 | 26 | std::string polaris_url = argv[1]; 27 | std::string service_namespace = argv[2]; 28 | std::string service_name = argv[3]; 29 | std::string platform_id; 30 | std::string platform_token; 31 | const char *query_url; 32 | 33 | if (argc == 5) 34 | query_url = argv[4]; 35 | else 36 | { 37 | platform_id = argv[4]; 38 | platform_token = argv[5]; 39 | query_url = argv[6]; 40 | } 41 | 42 | if (strncasecmp(argv[1], "http://", 7) != 0 && 43 | strncasecmp(argv[1], "https://", 8) != 0) { 44 | polaris_url = "http://" + polaris_url; 45 | } 46 | 47 | // 1. Construct PolarisManager. 48 | // PolarisManager mgr(polaris_url); 49 | PolarisManager mgr(polaris_url, platform_id, platform_token, ""/* .yaml */); 50 | 51 | // 2. Watch a service, will fill Workflow with instances automatically. 52 | int ret = mgr.watch_service(service_namespace, service_name); 53 | 54 | fprintf(stderr, "Watch %s %s ret=%d.\n", service_namespace.c_str(), 55 | service_name.c_str(), ret); 56 | 57 | if (ret < 0) 58 | { 59 | fprintf(stderr, "Watch failed. error=%d\n", mgr.get_error()); 60 | return 0; 61 | } 62 | 63 | // 3. Use Workflow as usual. Tasks will be send to the right instances. 64 | fprintf(stderr, "Query URL : %s\n", query_url); 65 | WFHttpTask *task = WFTaskFactory::create_http_task(query_url, 66 | 3, /* REDIRECT_MAX */ 67 | 5, /* RETRY_MAX */ 68 | [](WFHttpTask *task) { 69 | fprintf(stderr, "Query callback. state = %d error = %d\n", 70 | task->get_state(), task->get_error()); 71 | wait_group.done(); 72 | }); 73 | 74 | task->start(); 75 | wait_group.wait(); 76 | 77 | // 4. Unwatch the service when existing the progress. 78 | ret = mgr.unwatch_service(service_namespace, service_name); 79 | fprintf(stderr, "Unwatch %s %s ret=%d.\n", service_namespace.c_str(), 80 | service_name.c_str(), ret); 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /example/provider_demo.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "PolarisManager.h" 4 | #include "workflow/WFHttpServer.h" 5 | 6 | using namespace polaris; 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | if (argc < 6 || argc > 9) { 11 | fprintf(stderr, "USAGE:\n\t%s " 12 | " " 13 | "[] [] []\n\n", 14 | argv[0]); 15 | exit(1); 16 | } 17 | 18 | std::string polaris_url = argv[1]; 19 | std::string service_namespace = argv[2]; 20 | std::string service_name = argv[3]; 21 | std::string host = argv[4]; 22 | std::string port = argv[5]; 23 | std::string service_token; 24 | std::string platform_id; 25 | std::string platform_token; 26 | 27 | if (argc >= 7) 28 | service_token = argv[6]; 29 | if (argc >= 8) 30 | platform_id = argv[7]; 31 | if (argc == 9) 32 | platform_token = argv[8]; 33 | 34 | if (strncasecmp(argv[1], "http://", 7) != 0 && 35 | strncasecmp(argv[1], "https://", 8) != 0) { 36 | polaris_url = "http://" + polaris_url; 37 | } 38 | 39 | // Prepare: Start a server for test. 40 | WFHttpServer server([port](WFHttpTask *task) { 41 | fprintf(stderr, "Test server get request.\n"); 42 | task->get_resp()->append_output_body( 43 | "Response from instance 127.0.0.1:" + port); 44 | }); 45 | 46 | if (server.start(atoi(port.c_str())) != 0) { 47 | fprintf(stderr, "start server error\n"); 48 | return 0; 49 | } 50 | 51 | // 1. Construct PolarisManager with polaris_url. 52 | // PolarisManager mgr(polaris_url); 53 | PolarisManager mgr(polaris_url, platform_id, platform_token, "polaris.yaml.template"); 54 | 55 | int healthcheck_ttl = 2; 56 | int heartbeat_interval = 1; 57 | 58 | PolarisInstance instance; 59 | instance.set_host(host); 60 | instance.set_port(atoi(port.c_str())); 61 | std::map meta = {{"key1", "value1"}}; 62 | instance.set_metadata(meta); 63 | instance.set_region("north-china"); 64 | instance.set_zone("beijing"); 65 | instance.set_campus("wudaokou"); 66 | instance.set_enable_healthcheck(true); // default : false 67 | instance.set_healthcheck_ttl(healthcheck_ttl); // default : 5s 68 | 69 | // 2. Register instance. 70 | int ret = mgr.register_service(service_namespace, service_name, service_token, 71 | heartbeat_interval, instance); 72 | fprintf(stderr, "Register %s %s %s %s ret=%d.\n", 73 | service_namespace.c_str(), service_name.c_str(), 74 | host.c_str(), port.c_str(), ret); 75 | 76 | if (ret >= 0) { 77 | fprintf(stderr, "Success. Press \"Enter\" to deregister.\n"); 78 | getchar(); 79 | 80 | // 3. Deregister instance. 81 | ret = mgr.deregister_service(service_namespace, service_name, 82 | service_token, std::move(instance)); 83 | fprintf(stderr, "Deregister %s %s %s %s ret=%d.\n", 84 | service_namespace.c_str(), service_name.c_str(), 85 | host.c_str(), port.c_str(), ret); 86 | } else { 87 | fprintf(stderr, "Register failed. error=%d\n", mgr.get_error()); 88 | } 89 | 90 | server.stop(); 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /src/PolarisCluster.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISCLUSTER_H_ 2 | #define _POLARISCLUSTER_H_ 3 | 4 | namespace polaris { 5 | 6 | class PolarisCluster { 7 | public: 8 | PolarisCluster() { 9 | this->ref = new std::atomic(1); 10 | this->status = new int(0); 11 | this->mutex = new std::mutex; 12 | this->data = new struct cluster_data; 13 | } 14 | 15 | virtual ~PolarisCluster() { this->decref(); } 16 | 17 | PolarisCluster(const PolarisCluster ©) { 18 | this->ref = copy.ref; 19 | this->mutex = copy.mutex; 20 | this->status = copy.status; 21 | this->data = copy.data; 22 | this->incref(); 23 | } 24 | 25 | PolarisCluster &operator=(const PolarisCluster ©) { 26 | if (this != ©) { 27 | this->decref(); 28 | this->ref = copy.ref; 29 | this->mutex = copy.mutex; 30 | this->status = copy.status; 31 | this->data = copy.data; 32 | this->incref(); 33 | } 34 | return *this; 35 | } 36 | 37 | std::mutex *get_mutex() { return this->mutex; } 38 | int *get_status() { return this->status; } 39 | std::vector *get_server_connectors() { return &this->data->server_connectors; } 40 | std::vector *get_discover_clusters() { return &this->data->discover_clusters; } 41 | std::vector *get_healthcheck_clusters() { return &this->data->healthcheck_clusters; } 42 | std::vector *get_monitor_clusters() { return &this->data->monitor_clusters; } 43 | std::vector *get_metrics_clusters() { return &this->data->metrics_clusters; } 44 | std::map *get_revision_map() { return &this->data->revision_map; } 45 | 46 | int discover_failed() { return ++this->data->discover_failed_cnt; } 47 | void clear_discover_failed() { this->data->discover_failed_cnt = 0; } 48 | int healthcheck_failed() { return ++this->data->healthcheck_failed_cnt; } 49 | void clear_healthcheck_failed() { this->data->healthcheck_failed_cnt = 0; } 50 | 51 | private: 52 | void incref() { (*this->ref)++; } 53 | 54 | void decref() { 55 | if (--*this->ref == 0) { 56 | delete this->ref; 57 | delete this->mutex; 58 | delete this->status; 59 | delete this->data; 60 | } 61 | } 62 | 63 | private: 64 | struct cluster_data 65 | { 66 | std::vector server_connectors; 67 | std::vector discover_clusters; 68 | std::vector healthcheck_clusters; 69 | std::vector monitor_clusters; 70 | std::vector metrics_clusters; 71 | std::map revision_map; 72 | int discover_failed_cnt; 73 | int healthcheck_failed_cnt; 74 | //TODO: add other fail count when other cluster requests are supported 75 | 76 | cluster_data() 77 | { 78 | discover_failed_cnt = 0; 79 | healthcheck_failed_cnt = 0; 80 | } 81 | }; 82 | 83 | private: 84 | std::atomic *ref; 85 | int *status; 86 | std::mutex *mutex; 87 | struct cluster_data *data; 88 | }; 89 | 90 | }; // namespace polaris 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /example/local_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "PolarisManager.h" 5 | #include "workflow/WFFacilities.h" 6 | #include "workflow/WFHttpServer.h" 7 | 8 | #define RETRY_MAX 5 9 | #define REDIRECT_MAX 3 10 | #define INSTANCES_NUM 4 11 | 12 | using namespace polaris; 13 | 14 | static WFFacilities::WaitGroup wait_group(1); 15 | static const std::string service_namespace = "default"; 16 | static const std::string service_name = "workflow.polaris.service.b"; 17 | WFHttpServer *instances[INSTANCES_NUM]; 18 | 19 | void start_test_servers() 20 | { 21 | for (size_t i = 0; i < INSTANCES_NUM; i++) 22 | { 23 | std::string port = "800"; 24 | port += std::to_string(i); 25 | 26 | instances[i] = new WFHttpServer([port](WFHttpTask *task) { 27 | task->get_resp()->append_output_body( 28 | "Response from instance 127.0.0.0.1:" + port); 29 | }); 30 | 31 | if (instances[i]->start(atoi(port.c_str())) != 0) { 32 | fprintf(stderr, "Start instance 127.0.0.0.1:%s failed.\n", 33 | port.c_str()); 34 | delete instances[i]; 35 | instances[i] = NULL; 36 | } 37 | } 38 | } 39 | 40 | void stop_test_servers() 41 | { 42 | for (size_t i = 0; i < INSTANCES_NUM; i++) 43 | { 44 | if (instances[i]) 45 | { 46 | instances[i]->stop(); 47 | delete instances[i]; 48 | } 49 | } 50 | } 51 | 52 | int main(int argc, char *argv[]) 53 | { 54 | if (argc != 2) { 55 | fprintf(stderr, "USAGE: %s \n", argv[0]); 56 | exit(1); 57 | } 58 | 59 | start_test_servers(); 60 | 61 | std::string polaris_url = argv[1]; 62 | if (strncasecmp(argv[1], "http://", 7) != 0 && 63 | strncasecmp(argv[1], "https://", 8) != 0) { 64 | polaris_url = "http://" + polaris_url; 65 | } 66 | 67 | PolarisManager mgr(polaris_url, "./polaris.yaml.template"); 68 | int ret = mgr.watch_service(service_namespace, service_name); 69 | fprintf(stderr, "Watch %s %s ret=%d.\n", service_namespace.c_str(), 70 | service_name.c_str(), ret); 71 | if (ret) 72 | return 0; 73 | 74 | std::string url = "http://" + service_namespace + "." + service_name + 75 | ":8080#k1_env=v1_base&k2_number=v2_prime&a_namespace.a"; 76 | fprintf(stderr, "URL : %s\n", url.c_str()); 77 | 78 | WFHttpTask *task = WFTaskFactory::create_http_task(url, 79 | REDIRECT_MAX, 80 | RETRY_MAX, 81 | [](WFHttpTask *task) { 82 | int state = task->get_state(); 83 | int error = task->get_error(); 84 | fprintf(stderr, "Query task callback. state = %d error = %d\n", 85 | state, error); 86 | 87 | if (state == WFT_STATE_SUCCESS) { 88 | const void *body; 89 | size_t body_len; 90 | task->get_resp()->get_parsed_body(&body, &body_len); 91 | fwrite(body, 1, body_len, stdout); 92 | fflush(stdout); 93 | } 94 | 95 | wait_group.done(); 96 | }); 97 | 98 | task->start(); 99 | wait_group.wait(); 100 | 101 | bool unwatch_ret = mgr.unwatch_service(service_namespace, service_name); 102 | fprintf(stderr, "\nUnwatch %s %s ret=%d.\n", service_namespace.c_str(), 103 | service_name.c_str(), unwatch_ret); 104 | 105 | stop_test_servers(); 106 | fprintf(stderr, "Success. All test servers stoped.\n"); 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /src/PolarisClient.cc: -------------------------------------------------------------------------------- 1 | #include "PolarisClient.h" 2 | 3 | namespace polaris { 4 | 5 | PolarisClient::PolarisClient() { 6 | this->cluster = NULL; 7 | this->protocol = P_HTTP; 8 | } 9 | 10 | PolarisClient::~PolarisClient() { delete this->cluster; } 11 | 12 | int PolarisClient::init(const std::string &url) { 13 | std::string protocol; 14 | if (strncasecmp(url.c_str(), "http://", 7) == 0) { 15 | this->protocol = P_HTTP; 16 | protocol = "http://"; 17 | } else if (strncasecmp(url.c_str(), "trpc://", 7) == 0) { 18 | this->protocol = P_TRPC; 19 | protocol = "trpc://"; 20 | } else { 21 | return -1; 22 | } 23 | this->cluster = new PolarisCluster; 24 | std::string::size_type pos = url.find(','); 25 | std::string::size_type ppos = 0; 26 | if (pos == std::string::npos) { 27 | std::string host = url; 28 | if (host.find("http://") != 0 && host.find("trpc://") != 0) { 29 | host = protocol + host; 30 | } 31 | this->cluster->get_server_connectors()->emplace_back(host); 32 | } else { 33 | do { 34 | std::string host = url.substr(ppos, pos - ppos); 35 | if (host.find("http://") != 0 || host.find("trpc://") != 0) { 36 | host = protocol + host; 37 | } 38 | this->cluster->get_server_connectors()->emplace_back(host); 39 | ppos = pos + 1; 40 | pos = host.find(',', ppos); 41 | } while (pos != std::string::npos); 42 | } 43 | 44 | if (this->cluster->get_server_connectors()->empty()) { 45 | delete this->cluster; 46 | this->cluster = NULL; 47 | return -1; 48 | } 49 | return 0; 50 | } 51 | 52 | PolarisTask *PolarisClient::create_discover_task(const std::string &service_namespace, 53 | const std::string &service_name, int retry, 54 | polaris_callback_t cb) { 55 | PolarisTask *task = 56 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 57 | 58 | task->set_apitype(API_DISCOVER); 59 | task->set_protocol(this->protocol); 60 | return task; 61 | } 62 | 63 | PolarisTask *PolarisClient::create_register_task(const std::string &service_namespace, 64 | const std::string &service_name, int retry, 65 | polaris_callback_t cb) { 66 | PolarisTask *task = 67 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 68 | task->set_apitype(API_REGISTER); 69 | task->set_protocol(this->protocol); 70 | return task; 71 | } 72 | 73 | PolarisTask *PolarisClient::create_deregister_task(const std::string &service_namespace, 74 | const std::string &service_name, int retry, 75 | polaris_callback_t cb) { 76 | PolarisTask *task = 77 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 78 | task->set_apitype(API_DEREGISTER); 79 | task->set_protocol(this->protocol); 80 | return task; 81 | } 82 | 83 | PolarisTask *PolarisClient::create_ratelimit_task(const std::string &service_namespace, 84 | const std::string &service_name, int retry, 85 | polaris_callback_t cb) { 86 | PolarisTask *task = 87 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 88 | task->set_apitype(API_RATELIMIT); 89 | task->set_protocol(this->protocol); 90 | return task; 91 | } 92 | 93 | PolarisTask *PolarisClient::create_circuitbreaker_task(const std::string &service_namespace, 94 | const std::string &service_name, int retry, 95 | polaris_callback_t cb) { 96 | PolarisTask *task = 97 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 98 | task->set_apitype(API_CIRCUITBREAKER); 99 | task->set_protocol(this->protocol); 100 | return task; 101 | } 102 | 103 | PolarisTask *PolarisClient::create_heartbeat_task(const std::string &service_namespace, 104 | const std::string &service_name, int retry, 105 | polaris_callback_t cb) { 106 | PolarisTask *task = 107 | new PolarisTask(service_namespace, service_name, retry, cluster, std::move(cb)); 108 | task->set_apitype(API_HEARTBEAT); 109 | task->set_protocol(this->protocol); 110 | return task; 111 | } 112 | 113 | void PolarisClient::deinit() { 114 | delete this->cluster; 115 | this->cluster = NULL; 116 | } 117 | 118 | }; // namespace polaris 119 | 120 | -------------------------------------------------------------------------------- /src/PolarisTask.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISTASK_H_ 2 | #define _POLARISTASK_H_ 3 | 4 | #include "workflow/WFTaskFactory.h" 5 | #include "workflow/HttpMessage.h" 6 | #include "workflow/HttpUtil.h" 7 | #include "PolarisConfig.h" 8 | #include "PolarisCluster.h" 9 | #include 10 | #include 11 | 12 | namespace polaris { 13 | 14 | enum PolarisProtocol { 15 | P_UNKNOWN, 16 | P_HTTP, 17 | P_TRPC, 18 | }; 19 | 20 | enum ApiType { 21 | API_UNKNOWN = 0, 22 | API_DISCOVER, 23 | API_REGISTER, 24 | API_DEREGISTER, 25 | API_RATELIMIT, 26 | API_CIRCUITBREAKER, 27 | API_HEARTBEAT, 28 | }; 29 | 30 | enum DiscoverRequestType { 31 | UNKNOWN = 0, 32 | INSTANCE, 33 | CLUSTER, 34 | ROUTING, 35 | RATELIMIT, 36 | CIRCUITBREAKER, 37 | }; 38 | 39 | class PolarisTask; 40 | 41 | using polaris_callback_t = std::function; 42 | class PolarisTask : public WFGenericTask { 43 | public: 44 | PolarisTask(const std::string &snamespace, const std::string &sname, 45 | int retry_max, PolarisCluster *cluster, polaris_callback_t &&cb) 46 | : service_namespace(snamespace), 47 | service_name(sname), 48 | retry_max(retry_max), 49 | callback(std::move(cb)) { 50 | this->finish = false; 51 | this->apitype = API_UNKNOWN; 52 | this->protocol = P_UNKNOWN; 53 | int pos = rand() % cluster->get_server_connectors()->size(); 54 | this->url = cluster->get_server_connectors()->at(pos); 55 | this->cluster = *cluster; 56 | } 57 | 58 | void set_apitype(ApiType apitype) { this->apitype = apitype; } 59 | 60 | void set_protocol(PolarisProtocol protocol) { this->protocol = protocol; } 61 | 62 | void set_config(const PolarisConfig &config) { this->config = config; } 63 | 64 | void set_service_token(const std::string &token) { this->service_token = token; } 65 | void set_platform_id(const std::string &id) { this->platform_id = id; } 66 | void set_platform_token(const std::string &token) { this->platform_token = token; } 67 | 68 | void set_polaris_instance(const PolarisInstance &instance) { 69 | this->polaris_instance = instance; 70 | } 71 | 72 | bool get_discover_result(struct discover_result *result) const; 73 | bool get_route_result(struct route_result *result) const; 74 | bool get_ratelimit_result(struct ratelimit_result *result) const; 75 | bool get_circuitbreaker_result(struct circuitbreaker_result *result) const; 76 | 77 | protected: 78 | virtual ~PolarisTask(){}; 79 | WFHttpTask *create_discover_cluster_http_task(); 80 | WFHttpTask *create_healthcheck_cluster_http_task(); 81 | WFHttpTask *create_instances_http_task(); 82 | WFHttpTask *create_route_http_task(); 83 | WFHttpTask *create_register_http_task(); 84 | WFHttpTask *create_deregister_http_task(); 85 | WFHttpTask *create_ratelimit_http_task(); 86 | WFHttpTask *create_circuitbreaker_http_task(); 87 | WFHttpTask *create_heartbeat_http_task(); 88 | 89 | static void discover_cluster_http_callback(WFHttpTask *task); 90 | static void healthcheck_cluster_http_callback(WFHttpTask *task); 91 | static void instances_http_callback(WFHttpTask *task); 92 | static void route_http_callback(WFHttpTask *task); 93 | static void register_http_callback(WFHttpTask *task); 94 | static void ratelimit_http_callback(WFHttpTask *task); 95 | static void circuitbreaker_http_callback(WFHttpTask *task); 96 | static void heartbeat_http_callback(WFHttpTask *task); 97 | 98 | std::string create_discover_request(const struct discover_request &request); 99 | std::string create_register_request(const struct register_request &request); 100 | std::string create_deregister_request(const struct deregister_request &request); 101 | std::string create_ratelimit_request(const struct ratelimit_request &request); 102 | std::string create_circuitbreaker_request(const struct circuitbreaker_request &request); 103 | 104 | bool parse_cluster_response(const std::string &body); 105 | int parse_instances_response(const std::string &body, std::string &revision); 106 | int parse_route_response(const std::string &body, std::string &revision); 107 | int parse_register_response(const std::string &body); 108 | int parse_ratelimit_response(const std::string &body, std::string &revision); 109 | int parse_circuitbreaker_response(const std::string &body, std::string &revision); 110 | 111 | virtual void dispatch(); 112 | virtual SubTask *done(); 113 | 114 | void check_failed(); // pull cluster instances again if failed many times 115 | 116 | private: 117 | std::string service_namespace; 118 | std::string service_name; 119 | int retry_max; 120 | polaris_callback_t callback; 121 | std::string url; 122 | std::string service_token; 123 | std::string platform_id; 124 | std::string platform_token; 125 | bool finish; 126 | ApiType apitype; 127 | PolarisProtocol protocol; 128 | std::string discover_res; 129 | std::string route_res; 130 | std::string ratelimit_res; 131 | std::string circuitbreaker_res; 132 | PolarisInstance polaris_instance; 133 | PolarisConfig config; 134 | PolarisCluster cluster; 135 | }; 136 | 137 | }; // namespace polaris 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /src/PolarisPolicy.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISPOLICIES_H_ 2 | #define _POLARISPOLICIES_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "workflow/URIParser.h" 10 | #include "workflow/EndpointParams.h" 11 | #include "workflow/WFNameService.h" 12 | #include "workflow/WFServiceGovernance.h" 13 | #include "PolarisConfig.h" 14 | 15 | namespace polaris { 16 | 17 | enum MetadataFailoverType { 18 | MetadataFailoverNone, // default 19 | MetadataFailoverAll, // return all instances 20 | MetadataFailoverNotKey, 21 | }; 22 | 23 | enum NearbyMatchLevelType { 24 | NearbyMatchLevelZone, // default 25 | NearbyMatchLevelRegion, 26 | NearbyMatchLevelCampus, 27 | NearbyMatchLevelNone, 28 | }; 29 | 30 | /* 31 | struct MatchingString 32 | { 33 | enum MatchingStringType 34 | { 35 | EXACT, 36 | REGEX, 37 | }; 38 | 39 | MatchingStringType matching_type; 40 | 41 | enum ValueType 42 | { 43 | TEXT = 0; 44 | PARAMETER = 1; 45 | VARIABLE = 2; 46 | } 47 | 48 | ValueType value_type; 49 | std::string value; 50 | }; 51 | */ 52 | 53 | class PolarisPolicyConfig 54 | { 55 | private: 56 | std::string policy_name; 57 | std::string location_zone; 58 | std::string location_region; 59 | std::string location_campus; 60 | bool enable_rule_base_router; 61 | bool enable_dst_meta_router; 62 | bool enable_nearby_based_router; 63 | enum MetadataFailoverType failover_type; 64 | // for ruleBaseRouter 65 | enum NearbyMatchLevelType nearby_match_level; 66 | enum NearbyMatchLevelType nearby_max_match_level; 67 | unsigned short nearby_unhealthy_percentage; 68 | bool nearby_enable_recover_all; 69 | bool nearby_strict_nearby; 70 | 71 | public: 72 | PolarisPolicyConfig(const std::string& policy_name, 73 | const PolarisConfig& conf); 74 | 75 | void set_rule_base_router(bool enable) 76 | { 77 | this->enable_rule_base_router = enable; 78 | 79 | if (enable) 80 | this->enable_dst_meta_router = false; 81 | } 82 | 83 | void set_dst_meta_router(bool enable) 84 | { 85 | this->enable_dst_meta_router = enable; 86 | 87 | if (enable) 88 | this->enable_rule_base_router = false; 89 | } 90 | 91 | void set_nearby_based_router(bool enable, 92 | const std::string& match_level, 93 | const std::string& max_match_level, 94 | short percentage, 95 | bool enable_recover_all, 96 | bool strict_nearby); 97 | 98 | void set_failover_type(enum MetadataFailoverType type) 99 | { 100 | this->failover_type = type; 101 | } 102 | 103 | friend class PolarisPolicy; 104 | }; 105 | 106 | class PolarisInstanceParams : public PolicyAddrParams 107 | { 108 | public: 109 | int get_weight() const { return this->weight; } 110 | const std::string& get_namespace() const { return this->service_namespace; } 111 | const std::map& get_meta() const 112 | { 113 | return this->metadata; 114 | } 115 | bool get_healthy() const { return this->healthy; } 116 | const std::string& get_region() const { return this->region; } 117 | const std::string& get_zone() const { return this->zone; } 118 | const std::string& get_campus() const { return this->campus; } 119 | 120 | public: 121 | PolarisInstanceParams(const struct instance *inst, 122 | const struct AddressParams *params); 123 | 124 | private: 125 | int priority; 126 | int weight; 127 | bool enable_healthcheck; 128 | bool healthy; 129 | bool isolate; 130 | std::string logic_set; 131 | std::string service_namespace; 132 | std::string region; 133 | std::string zone; 134 | std::string campus; 135 | std::map metadata; 136 | }; 137 | 138 | class PolarisPolicy : public WFServiceGovernance 139 | { 140 | public: 141 | PolarisPolicy(const PolarisPolicyConfig *config); 142 | 143 | virtual bool select(const ParsedURI& uri, WFNSTracing *tracing, 144 | EndpointAddress **addr); 145 | 146 | void update_instances(const std::vector& instances); 147 | void update_inbounds(const std::vector& inbounds); 148 | void update_outbounds(const std::vector& outbounds); 149 | 150 | private: 151 | using BoundRulesMap = std::unordered_map>; 153 | 154 | PolarisPolicyConfig config; 155 | BoundRulesMap inbound_rules; 156 | BoundRulesMap outbound_rules; 157 | pthread_rwlock_t inbound_rwlock; 158 | pthread_rwlock_t outbound_rwlock; 159 | 160 | int total_weight; 161 | int available_weight; 162 | 163 | private: 164 | virtual void recover_one_server(const EndpointAddress *addr); 165 | virtual void fuse_one_server(const EndpointAddress *addr); 166 | virtual void add_server_locked(EndpointAddress *addr); 167 | void clear_instances_locked(); 168 | 169 | void matching_bounds(const std::string& caller_name, 170 | const std::string& caller_namespace, 171 | const std::map& meta, 172 | std::vector **dst_bounds); 173 | 174 | bool matching_subset( 175 | std::vector *dest_bounds, 176 | std::vector& matched_subset); 177 | 178 | bool matching_rules( 179 | const std::string& caller_name, 180 | const std::string& caller_namespace, 181 | const std::map& meta, 182 | const std::vector& src_bounds) const; 183 | 184 | bool matching_instances(struct destination_bound *dst_bounds, 185 | std::vector& subsets); 186 | 187 | bool matching_meta(const std::map& meta, 188 | std::vector& subset); 189 | bool matching_meta_notkey(const std::map& meta, 190 | std::vector& subset); 191 | 192 | size_t subsets_weighted_random( 193 | const std::vector& bounds, 194 | const std::vector>& subsets); 195 | 196 | EndpointAddress *get_one(std::vector& instances, 197 | WFNSTracing *tracing); 198 | bool nearby_router_filter(std::vector& instances); 199 | bool nearby_match_level(const EndpointAddress *instance, 200 | NearbyMatchLevelType level); 201 | bool nearby_match_degrade(size_t unhealth, size_t total); 202 | 203 | bool split_fragment(const char *fragment, 204 | std::string& caller_name, 205 | std::string& caller_namespace, 206 | std::map& meta); 207 | 208 | bool check_server_health(const EndpointAddress *addr); 209 | }; 210 | 211 | }; // namespace polaris 212 | 213 | #endif 214 | 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # workflow-polaris 2 | ## 介绍 3 | 4 | 本项目是基于高性能的异步调度编程范式[C++ Workflow](https://github.com/sogou/workflow)以及腾讯开源的服务发现&服务治理平台[北极星](https://polarismesh.cn/#/)上构建的,目标是将workflow的服务治理能力和北极星治理能力相结合,提供更便捷、更丰富的服务场景。另外其他同型的服务治理系统也可以通过该项目实现与北极星平台的对接。 5 | 6 | 已支持功能: 7 | 8 | * 基础功能: 9 | * 服务发现(包括根据集群要求自动定期更新服务信息) 10 | * 服务注册(包括健康检查:根据interval定期发心跳) 11 | * 流量控制: 12 | * 路由管理(包括规则路由、元数据路由、就近路由) 13 | * 负载均衡 14 | 15 | ## 编译 16 | 17 | ```sh 18 | git clone https://github.com/sogou/workflow-polaris.git 19 | cd worflow-polaris 20 | bazel build ... 21 | ``` 22 | ## 运行 23 | 24 | 在example中有示例代码[cosumer_demo.cc](/example/consumer_demo.cc)和[provider_demo.cc](/example/provider_demo.cc),编译成功后我们尝试一下运行consumer_demo: 25 | ```sh 26 | ./bazel-bin/example/consumer_demo 27 | ``` 28 | 我们会得到demo的用法介绍: 29 | ```sh 30 | USAGE: 31 | ./bazel-bin/example/consumer_demo 32 | 33 | QUERY URL FORMAT: 34 | http://callee_service_namespace.callee_service_name:port#k1=v1&caller_service_namespace.caller_service_name 35 | 36 | EXAMPLE: 37 | ./bazel-bin/example/consumer_demo http://127.0.0.1:8090 default workflow.polaris.service.b "http://default.workflow.polaris.service.b:8080#k1_env=v1_base&k2_number=v2_prime&a_namespace.a" 38 | ``` 39 | 40 | 可以看到consumer_demo需要四个参数,分别是: 41 | - polaris cluster:北极星集群地址 42 | - namespace:使用北极星时的service namespace 43 | - service_name:使用北极星时的service_name 44 | - query URL:demo会帮我们尝试发一个请求,这个是用户请求的URL 45 | 46 | 我们按照提示的信息执行consumer_demo,配上我们的北极星服务信息: 47 | ```sh 48 | ./bazel-bin/example/consumer_demo http://polaris.cluster:8080 default workflow.polaris.service.b "http://default.workflow.polaris.service.b:8080#k1_env=v1_base&k2_number=v2_prime&a_namespace.a" 49 | ``` 50 | 屏幕上会打出以下结果: 51 | 52 | ```sh 53 | Watch default workflow.polaris.service.b ret=0. 54 | URL : http://default.workflow.polaris.service.b:8080#k1_env=v1_base&k2_number=v2_prime&a_namespace.a 55 | Query task callback. state = 0 error = 0 56 | Response from instance 127.0.0.0.1:8002 57 | Unwatch default workflow.polaris.service.b ret=0. 58 | Success. Press Ctrl-C to exit. 59 | ``` 60 | 61 | 再看看provide_demo,参数里把集群地址、要上报的服务信息和地址端口填上即可: 62 | ```sh 63 | USAGE: 64 | ./bazel-bin/example/provider_demo [] 65 | ``` 66 | 67 | 如下是示例,然后屏幕上会有类似的结果: 68 | 69 | ```sh 70 | ./bazel-bin/example/provider_demo http://polaris.cluster:8080 default workflow.polaris.service.b 11.11.11.11 8002 71 | 72 | Register Production workflow.polaris.service.b 11.11.11.11 8002 ret=0. 73 | Success. Press "Enter" to deregister. 74 | 75 | Deregister Production workflow.polaris.service.b 11.11.11.11 8002 ret=0. 76 | ``` 77 | 78 | ## 使用步骤 79 | 80 | #### 1. Consumer / Client / 主调方 / Caller 81 | 82 | ```cpp 83 | // 1. 构造PolarisManager,传入集群地址,第二个参数可选传入yaml配置文件 84 | PolarisManager mgr(polaris_url, /* polaris.config.yaml */); 85 | 86 | // 2. 通过watch接口获取服务信息 87 | int watch_ret = mgr.watch_service(service_namespace, service_name); 88 | 89 | // 3. watch完毕就可以发送请求,workflow的本地命名服务会自动帮选取 90 | WFHttpTask *task = WFTaskFactory::create_http_task(query_url, 91 | 3, /* REDIRECT_MAX */ 92 | 5, /* RETRY_MAX */ 93 | [](WFHttpTask *task) { 94 | fprintf(stderr, "Query callback. state = %d error = %d\n", 95 | task->get_state(), task->get_error()); 96 | }); 97 | 98 | task->start(); 99 | ... 100 | 101 | // 4. 不使用的时候,可以调用unwatch,但不是必须的 102 | // 只有在watch成功的service才可以正确调用unwatch 103 | bool unwatch_ret = mgr.unwatch_service(service_namespace, service_name); 104 | ... 105 | 106 | ``` 107 | 108 | #### 2. Provider / Server / 被调方 / Callee 109 | ```cpp 110 | // 1. 构造PolarisManager 111 | PolarisManager mgr(polaris_url); 112 | 113 | // 2. 设置好instance的属性,并通过register接口把instance注册上去 114 | // 其中有些服务注册时需要service_token,请参考北极星注册平台中的描述 115 | // heartbeat_interval是需要健康检查时的发送心跳间隔,如果不需要健康检查,此参数不生效 116 | PolarisInstance instance; 117 | instance.set_host(host); 118 | instance.set_port(port); 119 | int register_ret = mgr.register_service(service_namespace, service_name, service_token, heartbeat_interval, instance); 120 | ... 121 | 122 | // 3. 需要退出之前可以调用deregister进行反注册 123 | // 只有在register成功之后才可以调用deregister 124 | bool deregister_ret = mgr.deregister_service(service_namespace, service_name, service_token, instance); 125 | ... 126 | 127 | ``` 128 | 129 | ## 格式说明 130 | 131 | 被调方请求的拼接格式: 132 | 133 | ```sh 134 | http://CALLEE_SERVICE_NAMESPACE.CALLEE_SERVICE_NAME:PORT#ROUTE_INFO&CALLER_SERVICE_NAMESPACE.CALLER_SERVICE_NAME 135 | ``` 136 | 137 | fragment里的ROUTE_INFO,是我们被调方的路由信息,路由信息有两种类型: 138 | - 规则路由 139 | - 元数据路由 140 | 141 | 这两种路由信息是不能同时启用的,只能启用其中一个,或者都不启用,由consumer/client/主调方在配置文件中指定,默认启用规则路由。 142 | 143 | 在规则路由中:`#k1=v1&k2=v2&caller_namespace.caller_name`,我们会得到 144 | 145 | 而在元数据路由中:`#meta.k1=v1&meta.k2=v2`,我们会得到 146 | 147 | caller_namespace和caller_name对于规则路由有效,但对于元数据路由无效。 148 | 149 | 如果我们consumer/client/主调方在配置文件中配置了规则路由而非元数据路由,则`meta.k1=v1&meta.k2=v2`就会得到。 150 | 151 | 对于主调方则简单很多,只需要通过接口把meta设置到用来注册的instance上即可被匹配: 152 | ```cpp 153 | PolarisInstance instance; 154 | std::map meta = {{"k1", "v1"}}; 155 | instance.set_metadata(meta); 156 | ``` 157 | 158 | ## 就近访问 159 | 160 | 如果需要就近访问,Consumer/Client/主调方需要把自己的地域信息,配到yaml配置文件中的如下三个域: 161 | 162 | ```yaml 163 | global: 164 | api: 165 | location: 166 | region: north-china 167 | zone: beijing 168 | campus: wudaokou 169 | ``` 170 | 171 | 对应的,如果Provider/Server/被调方希望自己的地域被注册到平台上,以方便client做匹配,可以把信息设置到instance上: 172 | 173 | ```cpp 174 | PolarisInstance instance; 175 | instance.set_region("north-china"); 176 | instance.set_zone("beijing"); 177 | instance.set_campus("wudaokou"); 178 | ``` 179 | 180 | ## 健康检查 181 | 182 | 被调方注册时,是可以开启健康检查功能的。 183 | 184 | register、healthcheck、deregister的关系是: 185 | 186 | 1. 如果register的instance不打开healthcheck,那么注册成功后就是健康状态; 187 | 2. healthcheck目前只有一种实现方式,就是心跳,如果开启了,需要带上心跳的ttl,表示"没心跳多久之后被认为不健康"; 188 | 3. 如果打开了healthcheck,则按注册的时候填的heartbeat_interval参数定期发送心跳; 189 | 4. register的同步接口中包含了register网络请求,如果开启了healthcheck则还会包含第一次心跳请求。register接口返回错误的情况为:register失败、或者心跳请求拿到的错误码为不支持心跳(那么后续就不会定期发送心跳);而如果第一次心跳仅为网络错误,后续依然会定期发送心跳。 190 | 5. deregister会在北极星平台彻底删掉这个instance,如果已开启healthcheck的话,会停止后台的定期心跳流程。 191 | 192 | 心跳示例代码大致如下: 193 | 194 | ```cpp 195 | PolarisInstance instance; 196 | instance.set_enable_healthcheck(true); // 默认不开启 197 | instance.set_healthcheck_ttl(10); // 单位秒,默认5秒 198 | 199 | ret = mgr.register_service(namespace, service_name, token, 2 /* 2秒发一次心跳 */, instance); 200 | ``` 201 | ## 平台接入 202 | 203 | 部分私有化部署的北极星集群,除了平台服务可以观察到我们上报的instances以外,还有平台接入:用以拉取我们可以做服务发现/心跳上报等的北极星自己的服务器列表,service_name分别对应为polaris.discover和polaris.healthcheck。我们需要先从某个平台接入拉取这些列表,才能进行后续的上报/注册/发现等行为。 204 | 205 | 如果有平台接入,需要根据平台接入协议,填入Platform-Id和Platform-Token,让平台鉴权我们可以拉取服务器列表。因此这两个值可以填在PolarisManager的构造函数中: 206 | 207 | ```cpp 208 | PolarisManager mgr(polaris_url, platform_id, platform_token, "polaris.yaml.template"); 209 | ``` 210 | 最后一个参数为北极星配置文件,不需要的话可以填空。 211 | 212 | platform_id和platform_token:在Manager刚启动的时候、或者失败次数到达20次(默认),就会带上这两个值去平台接入重新拉取服务器。 213 | 214 | -------------------------------------------------------------------------------- /polaris.yaml.template: -------------------------------------------------------------------------------- 1 | #配置模板,不要直接使用 2 | #不传配置文件或传空的配置文件,SDK会使用默认配置初始化,根据需要传入配置覆盖默认配置项 3 | #描述:全局配置 4 | global: 5 | #描述系统相关配置 6 | system: 7 | #服务发现集群 8 | discoverCluster: 9 | namespace: Polaris 10 | service: polaris.discover 11 | #可选:服务刷新间隔 12 | refreshInterval: 10m 13 | #健康检查集群 14 | healthCheckCluster: 15 | namespace: Polaris 16 | service: polaris.healthcheck 17 | #可选:服务刷新间隔 18 | refreshInterval: 10m 19 | #监控上报集群 20 | monitorCluster: 21 | namespace: Polaris 22 | service: polaris.monitor 23 | #可选:服务刷新间隔 24 | refreshInterval: 10m 25 | # 限流统计集群 26 | metricCluster: 27 | namespace: Polaris 28 | service: polaris.metric 29 | refreshInterval: 10m 30 | #描述:对外API相关配置 31 | api: 32 | #描述:SDK绑定的网卡地址, 33 | #类型:string 34 | bindIf: eth1 35 | #描述:SDK绑定的IP地址, 36 | #类型:string 37 | #特殊说明:假如不传,则SDK通过bindIf的网卡名取地址,假如bindIf为空,则通过tcp连接的localAddress来获取地址 38 | bindIP: 192.168.1.2 39 | #描述:api超时时间 40 | #类型:string 41 | #格式:^\d+(ms|s|m|h)$ 42 | #范围:[1ms:...] 43 | #默认值:1s 44 | timeout: 1s 45 | #描述:API因为网络原因调用失败后的重试次数 46 | #类型:int 47 | #范围:[0:...] 48 | #默认值: 3 49 | maxRetryTimes: 3 50 | #描述:重试间隔 51 | #类型:string 52 | #格式:^\d+(ms|s|m|h)$ 53 | #范围:[1s:...] 54 | #默认值:1s 55 | retryInterval: 1s 56 | #描述:SDK的离线地域信息,假如server没有返回正确的地域信息,则使用离线地域信息 57 | #location: 58 | #描述:大区 59 | #类型:string 60 | #region: south-china 61 | #描述:区域 62 | #类型:string 63 | #zone: shenzhen 64 | #描述:园区 65 | #类型:string 66 | #campus: longgang 67 | #描述:对接polaris server的相关配置 68 | #serverConnector: 69 | #描述:server列表,由于SDK在运行过程中可以通过接口定时拉取最新server列表,因此这里填的是初始的地址 70 | #类型:list 71 | #默认值:代理模式,默认为127.0.0.1:8888(本地agent地址);直连模式,无默认值 72 | #addresses: 73 | #- 127.0.0.1:8081 # 埋点地址 74 | #描述:访问server的连接协议,SDK会根据协议名称会加载对应的插件 75 | #类型:string 76 | #范围:已注册的连接器插件名 77 | #默认值:grpc 78 | #protocol: grpc 79 | #描述:发起连接后的连接超时时间 80 | #类型:string 81 | #格式:^\d+(ms|s|m|h)$ 82 | #范围:[1ms:...] 83 | #默认值:200ms 84 | #connectTimeout: 200ms 85 | #描述:连接空闲时间,长连接模式下,当连接空闲超过一定时间后,SDK会主动释放连接 86 | #类型:string 87 | #格式:^\d+(ms|s|m|h)$ 88 | #范围:[1ms:...] 89 | #默认值:500ms 90 | #connectionIdleTimeout: 500ms 91 | #描述:远程请求超时时间 92 | #类型:string 93 | #格式:^\d+(ms|s|m|h)$ 94 | #范围:[1ms:...] 95 | #默认值:1s 96 | #messageTimeout: 1s 97 | #描述:首次请求的任务队列长度,当用户发起首次服务访问请求时,SDK会对任务进行队列调度并连接server,当积压的任务数超过队列长度后,SDK会直接拒绝首次请求的发起。 98 | #类型:int 99 | #范围:[0:...] 100 | #默认值:1000 101 | #requestQueueSize: 1000 102 | #描述:server节点的切换周期,为了使得server的压力能够均衡,SDK会定期针对最新的节点列表进行重新计算自己当前应该连接的节点,假如和当前不一致,则进行切换 103 | #类型:string 104 | #格式:^\d+(ms|s|m|h)$ 105 | #范围:[1m:...] 106 | #默认值:10m 107 | #serverSwitchInterval: 10m 108 | #统计上报设置 109 | statReporter: 110 | #描述:是否将统计信息上报至monitor 111 | #类型:bool 112 | #默认值:true 113 | enable: true 114 | #描述:启用的统计上报插件类型 115 | #类型:list 116 | #范围:已经注册的统计上报插件的名字 117 | #默认值:stat2Monitor(将信息上报至monitor服务) 118 | chain: 119 | - stat2Monitor 120 | #描述:统计上报插件配置 121 | plugin: 122 | stat2Monitor: 123 | #描述:每次上报多长一段时间的统计信息 124 | #类型:string 125 | #格式:^\d+(ms|s|m|h)$ 126 | #范围:[1m:...] 127 | metricsReportWindow: 1m 128 | #描述:将一段时间内的统计信息分为多少个基本单元收集 129 | #类型:int 130 | #范围:[1:...] 131 | #默认值:12 132 | metricsNumBuckets: 12 133 | #描述:主调端配置 134 | consumer: 135 | #描述:本地缓存相关配置 136 | localCache: 137 | #描述:缓存类型 138 | #类型:string 139 | #范围:已注册的本地缓存插件名 140 | #默认值:inmemory(基于本机内存的缓存策略) 141 | type: inmemory 142 | #描述:服务定期刷新周期 143 | #类型:string 144 | #格式:^\d+(ms|s|m|h)$ 145 | #范围:[1s:...] 146 | #默认值:2s 147 | serviceRefreshInterval: 2s 148 | #描述:服务过期淘汰时间 149 | #类型:string 150 | #格式:^\d+(ms|s|m|h)$ 151 | #范围:[1m:...] 152 | #默认值:24h 153 | serviceExpireTime: 24h 154 | #描述:服务缓存持久化目录,SDK在实例数据更新后,按照服务维度将数据持久化到磁盘 155 | #类型:string 156 | #格式:本机磁盘目录路径,支持$HOME变量 157 | #默认值:$HOME/polaris/backup 158 | persistDir: $HOME/polaris/backup 159 | #描述:缓存写盘失败的最大重试次数 160 | #类型:int 161 | #范围:[1:...] 162 | #默认值:5 163 | persistMaxWriteRetry: 5 164 | #描述:缓存从磁盘读取失败的最大重试次数 165 | #类型:int 166 | #范围:[1:...] 167 | #默认值:1 168 | persistMaxReadRetry: 1 169 | #描述:缓存读写磁盘的重试间隔 170 | #类型:string 171 | #格式:^\d+(ms|s|m|h)$ 172 | #范围:[1ms:...] 173 | #默认值:1s 174 | persistRetryInterval: 1s 175 | #描述:节点熔断相关配置 176 | circuitBreaker: 177 | #描述:是否启用节点熔断功能 178 | #类型:bool 179 | #默认值:true 180 | enable: true 181 | #描述:实例定时熔断检测周期 182 | #类型:string 183 | #格式:^\d+(ms|s|m|h)$ 184 | #范围:[100ms:...] 185 | #默认值:500ms 186 | checkPeriod: 500ms 187 | #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件 188 | #类型:list 189 | #范围:已注册的熔断器插件名 190 | #默认值:基于周期连续错误数熔断(errorCount)、以及基于周期错误率的熔断策略(errorRate) 191 | chain: 192 | - errorCount 193 | - errorRate 194 | #描述:熔断插件配置 195 | plugin: 196 | #描述:基于周期连续错误数熔断策略配置 197 | errCount: 198 | #描述:触发连续错误熔断的阈值 199 | #类型:int 200 | #范围:[1:...] 201 | #默认值:10 202 | continuousErrorThreshold: 10 203 | #描述:连续失败的统计周期 204 | #类型:string 205 | #格式:^\d+(ms|s|m|h)$ 206 | #范围:[10ms:...] 207 | #默认值:1m 208 | metricStatTimeWindow: 1m 209 | #描述:熔断器半开后最大允许的请求数 210 | #类型:int 211 | #范围:[3:...] 212 | #默认值:3 213 | requestCountAfterHalfOpen: 3 214 | #描述:熔断器打开后,多久后转换为半开状态 215 | #类型:string 216 | #格式:^\d+(ms|s|m|h)$ 217 | #范围:[1s:...] 218 | #默认值:3s 219 | sleepWindow: 5s 220 | #描述:熔断器半开到关闭所必须的最少成功请求数 221 | #类型:int 222 | #范围:[1:requestCountAfterHalfOpen] 223 | #默认值:2 224 | successCountAfterHalfOpen: 2 225 | #描述:基于周期错误率的熔断策略配置 226 | errRate: 227 | #描述:触发错误率熔断的最低请求阈值 228 | #类型:int 229 | #范围:(0:...] 230 | #默认值:10 231 | requestVolumeThreshold: 10 232 | #描述:触发错误率熔断的阈值 233 | #类型:double 234 | #范围:(0:1] 235 | #默认值:0.5 236 | errorRateThreshold: 0.5 237 | #描述:错误率熔断的统计周期 238 | #类型:string 239 | #格式:^\d+(ms|s|m|h)$ 240 | #范围:[1s:...] 241 | #默认值:1m 242 | metricStatTimeWindow: 1m 243 | #描述:错误率熔断的最小统计单元数量 244 | #类型:int 245 | #范围:[1:...] 246 | #默认值:12 247 | metricNumBuckets: 12 248 | #描述:熔断器半开后最大允许的请求数 249 | #类型:int 250 | #范围:[3:...] 251 | #默认值:3 252 | requestCountAfterHalfOpen: 3 253 | #描述:熔断器打开后,多久后转换为半开状态 254 | #类型:string 255 | #格式:^\d+(ms|s|m|h)$ 256 | #范围:[1s:...] 257 | #默认值:3s 258 | sleepWindow: 5s 259 | #描述:熔断器半开到关闭所必须的最少成功请求数 260 | #类型:int 261 | #范围:[1:requestCountAfterHalfOpen] 262 | #默认值:2 263 | successCountAfterHalfOpen: 2 264 | #描述: set(集群)熔断相关配置 265 | setCircuitBreaker: 266 | enable: false 267 | #描述:故障探测相关配置 268 | outlierDetection: 269 | #描述:是否启用故障探测功能 270 | #类型:bool 271 | #默认值:true 272 | enable: true 273 | #描述:定时故障探测周期 274 | #类型:string 275 | #格式:^\d+(ms|s|m|h)$ 276 | #范围:[1s:...] 277 | #默认值:10s 278 | checkPeriod: 10s 279 | #描述:故障探测策略,SDK会根据策略名称加载对应的探测器插件 280 | #类型:list 281 | #范围:已注册的探测器插件名 282 | #默认值:基于tcp协议的探测器 283 | chain: 284 | - tcp 285 | #描述:故障探测插件配置 286 | plugin: 287 | #描述:基于TCP的故障探测策略 288 | tcp: 289 | #描述:探测超时时间 290 | #类型:string 291 | #格式:^\d+(ms|s|m|h)$ 292 | #范围:[10ms:100ms] 293 | #默认值:100ms 294 | timeout: 100ms 295 | #描述:探测失败重试次数 296 | #类型:int 297 | #范围:[0:...] 298 | #默认值:0 299 | retry: 0 300 | #描述:tcp发送的探测包,可选字段,假如不配置,则默认只做连接探测 301 | #类型:string 302 | #格式:^0x[1-9A-Fa-f]+$ 303 | send: 0xEEEEEEEE 304 | #描述:期望接收的TCP回复包,可选字段,假如不配置,则默认只做连接或发包探测 305 | #类型:string 306 | #格式:^0x[1-9A-Fa-f]+$ 307 | receive: 0xFFFFFFF 308 | #描述:基于TCP的故障探测策略 309 | udp: 310 | #描述:探测超时时间 311 | #类型:string 312 | #格式:^\d+(ms|s|m|h)$ 313 | #范围:[10ms:100ms] 314 | #默认值:100ms 315 | timeout: 100ms 316 | #描述:探测失败重试次数 317 | #类型:int 318 | #范围:[0:...] 319 | #默认值:0 320 | retry: 0 321 | #描述:udp发送的探测包,必选字段,假如不配置,则不启动UDP探测 322 | #类型:string 323 | #格式:^0x[1-9A-Fa-f]+$ 324 | send: 0xEEEEEEEE 325 | #描述:期望接收的UDP回复包,必选字段,假如不配置,则不启动UDP探测 326 | #类型:string 327 | #格式:^0x[1-9A-Fa-f]+$ 328 | receive: 0xFFFFFFF 329 | http: 330 | #描述:探测超时时间 331 | #类型:string 332 | #格式:^\d+(ms|s|m|h)$ 333 | #范围:[10ms:100ms] 334 | #默认值:100ms 335 | timeout: 100ms 336 | #描述:http探测路径,必选字段,假如不配置,则不启用http探测 337 | #类型:string 338 | #范围:^/.+$ 339 | path: /ping 340 | #描述:负载均衡相关配置 341 | loadBalancer: 342 | #描述:负载均衡类型 343 | #范围:已注册的负载均衡插件名 344 | #默认值:权重随机负载均衡 345 | type: weightedRandom 346 | #描述:服务路由相关配置 347 | serviceRouter: 348 | # 服务路由链 349 | chain: 350 | # 基于主调和被调服务规则的路由策略(默认的路由策略) 351 | - ruleBasedRouter 352 | # 就近路由策略 353 | - nearbyBasedRouter 354 | #描述:服务路由插件的配置 355 | plugin: 356 | nearbyBasedRouter: 357 | #描述:就近路由的最小匹配级别 358 | #类型:string 359 | #范围:region(大区)、zone(区域)、campus(园区) 360 | #默认值:zone 361 | matchLevel: zone 362 | # 就近路由最大的匹配级别 363 | maxMatchLevel: none 364 | # 是否启用按服务不健康实例比例进行降级 365 | enableDegradeByUnhealthyPercent: true 366 | # 需要进行降级的实例比例,不健康实例达到百分之多少才进行降级。值(0, 100]。 367 | # 默认100,即全部不健康才进行切换。 368 | unhealthyPercentToDegrade: 100 369 | # 允许全死全活 370 | enableRecoverAll: true 371 | rateLimiter: 372 | # global:默认值,该模式表示会使用分布式限流,必须填写rateLimitCluster。 373 | # local:表示只会使用本地限流,不会使用分布式限流,可不填rateLimitCluster 374 | mode: local 375 | # global模式必填,分布式限流时用于发现限流服务器 376 | rateLimitCluster: 377 | # 限流服务器集群所在命名空间 378 | namespace: Polaris 379 | # 限流服务器集群名字 380 | service: poalris.metirc.xxx.yyy 381 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /test/polaris_policy_unittest.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "PolarisPolicy.h" 6 | 7 | #include "workflow/UpstreamManager.h" 8 | #include "workflow/WFHttpServer.h" 9 | #include "workflow/WFTaskFactory.h" 10 | #include "workflow/WFFacilities.h" 11 | 12 | using namespace polaris; 13 | 14 | PolarisConfig config; 15 | static PolarisPolicyConfig conf("b", config); 16 | 17 | void init() 18 | { 19 | } 20 | 21 | void deinit() 22 | { 23 | } 24 | 25 | void fill_inbounds_a_b(std::vector& bounds) 26 | { 27 | /* 28 | "outbounds": [ 29 | { "source": [ 30 | { "service": "a", 31 | "namespace": "a_namespace", 32 | "metadata": {"k1_env" : "v1_base", "k2_number" : "v2_prime"} 33 | } ], 34 | "destination": [ 35 | { "service": "b", 36 | "namespace": "b_namespace", 37 | "metadata": {"k1_for_inst_env": "v1_for_inst_base"}, "priority": 1 }, 38 | { "service": "b", 39 | "namespace": "b_namespace", 40 | "metadata": {"k1_for_inst_env": "v1_for_inst_grey"}, "priority": 1 }, 41 | { "service": "b", 42 | "namespace": "b_namespace", 43 | "metadata": {"k1": "v1"}, {{"k2": "v2"}}, "priority": 2}] 44 | } 45 | ] 46 | */ 47 | struct meta_label label_1, label_2; 48 | label_1.type = "EXACT"; 49 | label_1.value = "v1_base"; 50 | label_2.type = "EXACT"; 51 | label_2.value = "v2_prime"; 52 | 53 | std::map meta; 54 | meta["k1_env"] = label_1; 55 | meta["k2_number"] = label_2; 56 | 57 | struct source_bound src_a_b_1; 58 | src_a_b_1.service = "a"; 59 | src_a_b_1.service_namespace = "a_namespace"; 60 | src_a_b_1.metadata = meta; 61 | 62 | struct routing_bound element0; 63 | element0.source_bounds.push_back(src_a_b_1); 64 | 65 | struct destination_bound dst_a_b_1, dst_a_b_2, dst_a_b_3; 66 | dst_a_b_1.service = "b"; 67 | dst_a_b_1.priority = 1; 68 | dst_a_b_1.service_namespace = "b_namespace"; 69 | dst_a_b_2 = dst_a_b_1; 70 | dst_a_b_3 = dst_a_b_1; 71 | 72 | meta.clear(); 73 | label_1.value = "v1_for_inst_base"; 74 | meta["k1_for_inst_env"] = label_1; 75 | dst_a_b_1.metadata = meta; 76 | element0.destination_bounds.push_back(dst_a_b_1); 77 | 78 | meta.clear(); 79 | label_2.value = "v1_for_inst_grey"; 80 | meta["k1_for_inst_env"] = label_2; 81 | dst_a_b_2.metadata = meta; 82 | element0.destination_bounds.push_back(dst_a_b_2); 83 | 84 | meta.clear(); 85 | label_1.value = "v1"; 86 | label_2.value = "v2"; 87 | meta["k1"] = label_1; 88 | meta["k2"] = label_2; 89 | dst_a_b_3.metadata = meta; 90 | element0.destination_bounds.push_back(dst_a_b_3); 91 | 92 | bounds.push_back(element0); 93 | } 94 | 95 | void fill_outbounds_a_b(std::vector &bounds) 96 | { 97 | /* 98 | "inbounds": [ 99 | { "source": [ 100 | { "service": "a", 101 | "namespace": "a_namespace", 102 | "metadata": {"k1_env" : "v1_base", "k2_number" : "v2_composite"} 103 | } ], 104 | "destination": [ 105 | { "service": "b", "metadata": {"k1_for_inst_env": "v1_for_inst_base"}, "weight": 1 }, 106 | { "service": "b", "metadata": {"k1_for_inst_env": "v1_for_inst_grey"}, "weight": 99 }] 107 | } 108 | ] 109 | */ 110 | struct meta_label label_1, label_2; 111 | label_1.type = "EXACT"; 112 | label_1.value = "v1_base"; 113 | label_2.type = "EXACT"; 114 | label_2.value = "v2_composite"; 115 | 116 | std::map meta; 117 | meta["k1_env"] = label_1; 118 | meta["k2_number"] = label_2; 119 | 120 | struct source_bound src_a_b_1; 121 | src_a_b_1.service = "a"; 122 | src_a_b_1.service_namespace = "a_namespace"; 123 | src_a_b_1.metadata = meta; 124 | 125 | struct routing_bound element0; 126 | element0.source_bounds.push_back(src_a_b_1); 127 | 128 | struct destination_bound dst_a_b_1, dst_a_b_2, dst_a_b_3; 129 | dst_a_b_1.service = "b"; 130 | dst_a_b_1.service_namespace = "b_namespace"; 131 | dst_a_b_2 = dst_a_b_1; 132 | 133 | meta.clear(); 134 | label_1.value = "v1_for_inst_base"; 135 | meta["k1_for_inst_env"] = label_1; 136 | dst_a_b_1.metadata = meta; 137 | dst_a_b_1.weight = 1; 138 | element0.destination_bounds.push_back(dst_a_b_1); 139 | 140 | meta.clear(); 141 | label_2.value = "v1_for_inst_grey"; 142 | meta["k1_for_inst_env"] = label_2; 143 | dst_a_b_2.metadata = meta; 144 | dst_a_b_2.weight = 99; 145 | element0.destination_bounds.push_back(dst_a_b_2); 146 | 147 | bounds.push_back(element0); 148 | } 149 | 150 | void fill_inbounds_c_b(std::vector& bounds) 151 | { 152 | /* 153 | "outbounds": [ 154 | { "source": [ 155 | { "service": "c", 156 | "metadata": {"k1_cb" : "v1_cb", "k2_cb" : "v2_cb"} 157 | } ], 158 | "destination": [ 159 | { "service": "b", 160 | "metadata": {"k1_cb": "v1_cb"}, "priority": 1 }, 161 | { "service": "b", 162 | "metadata": {"k2_cb": "v2_cb"}, "priority": 1 }, 163 | { "service": "b", 164 | "metadata": {"k1_cb": "v1_cb"}, {{"k2_cb": "v2_cb"}}, 165 | }] 166 | } 167 | ] 168 | */ 169 | struct meta_label label_1, label_2; 170 | label_1.type = "EXACT"; 171 | label_1.value = "v1_cb"; 172 | label_2.type = "EXACT"; 173 | label_2.value = "v2_cb"; 174 | 175 | std::map meta; 176 | meta["k1_cb"] = label_1; 177 | meta["k2_cb"] = label_2; 178 | 179 | struct source_bound src_c_b_1; 180 | src_c_b_1.service = "c"; 181 | src_c_b_1.metadata = meta; 182 | 183 | struct routing_bound element0; 184 | element0.source_bounds.push_back(src_c_b_1); 185 | 186 | struct destination_bound dst_c_b_1, dst_c_b_2, dst_c_b_3; 187 | dst_c_b_1.service = "b"; 188 | dst_c_b_1.priority = 1; 189 | dst_c_b_2 = dst_c_b_1; 190 | dst_c_b_3 = dst_c_b_1; 191 | 192 | meta.clear(); 193 | label_1.value = "v1_cb"; 194 | meta["k1_cb"] = label_1; 195 | dst_c_b_1.metadata = meta; 196 | element0.destination_bounds.push_back(dst_c_b_1); 197 | 198 | meta.clear(); 199 | label_2.value = "v1_cb"; 200 | meta["k1_cb"] = label_2; 201 | dst_c_b_2.metadata = meta; 202 | element0.destination_bounds.push_back(dst_c_b_2); 203 | 204 | meta.clear(); 205 | label_1.value = "v1_cb"; 206 | label_2.value = "v2_cb"; 207 | meta["k1_cb"] = label_1; 208 | meta["k2_cb"] = label_2; 209 | dst_c_b_3.metadata = meta; 210 | dst_c_b_3.priority = 0; 211 | element0.destination_bounds.push_back(dst_c_b_3); 212 | 213 | bounds.push_back(element0); 214 | } 215 | 216 | void fill_instances(std::vector& instances) 217 | { 218 | /* 219 | instances: [ 220 | {id: "instance_0", host: "b", port: 8000, weight: 10, service_namespace: "b_namespace", 221 | meta : {"k1_for_inst_env": "v1_for_inst_base"}}, 222 | {id: "instance_1", host: "b", port: 8001, weight: 1, service_namespace: "b_namespace", 223 | meta : {"k1_for_inst_env": "v1_for_inst_grey"}, }, 224 | {id: "instance_2", host: "b", port: 8002, weight: 10000, service_namespace: "b_namespace", 225 | meta : {"k1_for_inst_env": "v1_for_inst_grey"}, {"k1": "v1"} }, 226 | {id: "instance_3", host: "b", port: 8003, priority: 9, 227 | meta : {"k1": "v1"}, {"k2": "v2"} }, 228 | ]; 229 | */ 230 | struct instance inst0, inst1, inst2, inst3; 231 | inst0.host = "b"; 232 | inst0.service_namespace = "b_namespace"; 233 | inst1 = inst0; 234 | inst2 = inst0; 235 | 236 | inst0.id = "instance_0"; 237 | inst0.port = 8000; 238 | inst0.weight = 10; 239 | inst0.metadata["k1_for_inst_env"] = "v1_for_inst_base"; 240 | instances.push_back(inst0); 241 | 242 | inst1.id = "instance_1"; 243 | inst1.port = 8001; 244 | inst1.weight = 1; 245 | inst1.metadata["k1_for_inst_env"] = "v1_for_inst_grey"; 246 | instances.push_back(inst1); 247 | 248 | inst2.id = "instance_2"; 249 | inst2.port = 8002; 250 | inst2.weight = 10000; 251 | inst2.metadata["k1"] = "v1"; 252 | inst2.metadata["k1_for_inst_env"] = "v1_for_inst_grey"; 253 | instances.push_back(inst2); 254 | 255 | inst3.host = "b"; 256 | inst3.id = "instance_3"; 257 | inst3.port = 8003; 258 | inst3.priority = 9; 259 | inst3.metadata["k1"] = "v1"; 260 | inst3.metadata["k2"] = "v2"; 261 | instances.push_back(inst3); 262 | } 263 | 264 | /* 265 | void check_inbounds_a_b(PolarisPolicy::BoundRulesMap& bound_map) 266 | { 267 | std::vector& bounds = bound_map["a"]; 268 | EXPECT_EQ(bounds[0].destination_bounds.size(), 3); 269 | EXPECT_TRUE(bounds[0].source_bounds[0].service == "a"); 270 | EXPECT_TRUE(bounds[0].source_bounds[0].service_namespace == "a_namespace"); 271 | EXPECT_TRUE(bounds[0].destination_bounds[0].service == "b"); 272 | EXPECT_EQ(bounds[0].destination_bounds[0].metadata.size(), 1); 273 | EXPECT_EQ(bounds[0].destination_bounds[1].metadata.size(), 1); 274 | EXPECT_EQ(bounds[0].destination_bounds[2].metadata.size(), 2); 275 | EXPECT_TRUE(bounds[0].destination_bounds[1].metadata["k1_for_inst_env"].value == "v1_for_inst_grey"); 276 | EXPECT_EQ(bounds[0].destination_bounds[2].priority, 1); 277 | } 278 | 279 | void check_outbounds_a_b(PolarisPolicy::BoundRulesMap& bound_map) 280 | { 281 | std::vector& bounds = bound_map["a"]; 282 | EXPECT_EQ(bounds.size(), 1); 283 | EXPECT_EQ(bounds[0].destination_bounds.size(), 2); 284 | EXPECT_TRUE(bounds[0].source_bounds[0].service == "a"); 285 | EXPECT_TRUE(bounds[0].source_bounds[0].metadata["k2_number"].value == "v2_composite"); 286 | EXPECT_TRUE(bounds[0].destination_bounds[0].service_namespace == "b_namespace"); 287 | EXPECT_TRUE(bounds[0].destination_bounds[1].metadata["k1_for_inst_env"].value == "v1_for_inst_grey"); 288 | EXPECT_EQ(bounds[0].destination_bounds[1].weight, 99); 289 | } 290 | 291 | void check_inbounds_c_b(PolarisPolicy::BoundRulesMap& bound_map) 292 | { 293 | std::vector& bounds = bound_map["c"]; 294 | EXPECT_EQ(bounds[0].destination_bounds.size(), 3); 295 | EXPECT_TRUE(bounds[0].source_bounds[0].service == "c"); 296 | EXPECT_TRUE(bounds[0].destination_bounds[0].service == "b"); 297 | EXPECT_TRUE(bounds[0].destination_bounds[1].metadata["k1_cb"].value == "v1_cb"); 298 | EXPECT_TRUE(bounds[0].destination_bounds[2].metadata["k2_cb"].value == "v2_cb"); 299 | } 300 | 301 | TEST(polaris_policy_unittest, update_bounds) 302 | { 303 | std::vector routing_inbounds; 304 | std::vector routing_outbounds; 305 | 306 | fill_inbounds_a_b(routing_inbounds); 307 | fill_outbounds_a_b(routing_outbounds); 308 | 309 | PolarisPolicy pp(&conf); 310 | 311 | pp.update_inbounds(routing_inbounds); 312 | pp.update_outbounds(routing_outbounds); 313 | check_inbounds_a_b(pp.inbound_rules); 314 | check_outbounds_a_b(pp.outbound_rules); 315 | 316 | pp.update_inbounds(routing_inbounds); 317 | EXPECT_EQ(pp.inbound_rules.size(), 1); 318 | EXPECT_EQ(pp.outbound_rules.size(), 1); 319 | check_inbounds_a_b(pp.inbound_rules); 320 | check_outbounds_a_b(pp.outbound_rules); 321 | } 322 | 323 | TEST(polaris_policy_unittest, update_mulitple_bounds) 324 | { 325 | std::vector routing_inbounds; 326 | 327 | PolarisPolicy pp(&conf); 328 | fill_inbounds_a_b(routing_inbounds); 329 | pp.update_inbounds(routing_inbounds); 330 | fill_inbounds_c_b(routing_inbounds); 331 | pp.update_inbounds(routing_inbounds); 332 | EXPECT_EQ(pp.inbound_rules.size(), 2); 333 | check_inbounds_a_b(pp.inbound_rules); 334 | check_inbounds_c_b(pp.inbound_rules); 335 | } 336 | 337 | TEST(polaris_policy_unittest, update_instances) 338 | { 339 | std::vector instances; 340 | 341 | fill_instances(instances); 342 | 343 | PolarisPolicy pp(&conf); 344 | pp.update_instances(instances); 345 | pp.update_instances(instances); 346 | 347 | EXPECT_EQ(instances.size(), 4); 348 | EXPECT_TRUE(instances[0].id == "instance_0"); 349 | EXPECT_TRUE(instances[1].metadata["k1_for_inst_env"] == "v1_for_inst_grey"); 350 | EXPECT_TRUE(instances[2].metadata["k1"] == "v1"); 351 | EXPECT_EQ(instances[3].priority, 9); 352 | } 353 | 354 | TEST(polaris_policy_unittest, matching_bounds) 355 | { 356 | std::vector routing_inbounds; 357 | fill_inbounds_a_b(routing_inbounds); 358 | 359 | PolarisPolicy pp(&conf); 360 | pp.update_inbounds(routing_inbounds); 361 | 362 | const char *caller = "a"; 363 | std::map meta; 364 | std::vector *dst_bounds = NULL; 365 | 366 | // 1. "metadata": {"k1_env" : "v1_base", "k2_number" : "v2_prime"} 367 | meta["k1_env"] = "v1_base"; 368 | meta["k2_number"] = "v2_prime"; 369 | pp.matching_bounds(caller, meta, &dst_bounds); 370 | EXPECT_EQ(dst_bounds->size(), 3); 371 | 372 | // 2. caller : "c" 373 | dst_bounds = NULL; 374 | pp.matching_bounds("c", meta, &dst_bounds); 375 | EXPECT_TRUE(dst_bounds == NULL); 376 | 377 | // 3. namespace: "not_a_namespace" 378 | dst_bounds = NULL; 379 | meta["namespace"] = "not_a_namespace"; 380 | pp.matching_bounds(caller, meta, &dst_bounds); 381 | EXPECT_TRUE(dst_bounds == NULL); 382 | } 383 | 384 | TEST(polaris_policy_unittest, matching_instances) 385 | { 386 | std::vector routing_inbounds; 387 | fill_inbounds_a_b(routing_inbounds); 388 | 389 | std::vector instances; 390 | fill_instances(instances); 391 | 392 | PolarisPolicy pp(&conf); 393 | pp.update_instances(instances); 394 | pp.update_inbounds(routing_inbounds); 395 | 396 | struct destination_bound *dst_bound; 397 | dst_bound = &(routing_inbounds[0].destination_bounds[1]); 398 | 399 | std::vector subsets; 400 | pp.matching_instances(dst_bound, subsets); 401 | 402 | EXPECT_EQ(subsets.size(), 2); 403 | 404 | if (atoi(subsets[0]->port.c_str()) == 8001) 405 | EXPECT_EQ(atoi(subsets[1]->port.c_str()), 8002); 406 | else 407 | { 408 | EXPECT_EQ(atoi(subsets[0]->port.c_str()), 8002); 409 | EXPECT_EQ(atoi(subsets[1]->port.c_str()), 8001); 410 | } 411 | } 412 | 413 | TEST(polaris_policy_unittest, matching_subset) 414 | { 415 | std::vector routing_inbounds; 416 | fill_inbounds_a_b(routing_inbounds); 417 | 418 | std::vector instances; 419 | fill_instances(instances); 420 | 421 | PolarisPolicy pp(&conf); 422 | pp.update_instances(instances); 423 | pp.update_inbounds(routing_inbounds); 424 | 425 | std::vector *dst_bounds; 426 | dst_bounds = &routing_inbounds[0].destination_bounds; 427 | 428 | std::vector subsets; 429 | pp.matching_subset(dst_bounds, subsets); 430 | 431 | EXPECT_EQ(subsets.size(), 2); 432 | for (const auto& server : subsets) 433 | { 434 | unsigned short port = atoi(server->port.c_str()); 435 | if (port == 8001 || port == 8002) 436 | EXPECT_TRUE(true); 437 | else 438 | EXPECT_TRUE(false); 439 | } 440 | } 441 | 442 | TEST(polaris_policy_unittest, split_fragment) 443 | { 444 | PolarisPolicy pp(&conf); 445 | 446 | std::string caller_name; 447 | std::string caller_namespace; 448 | std::map meta; 449 | const char *fragment = "k1=v1&k2=v2&caller_namespace.caller_name"; 450 | 451 | EXPECT_TRUE(pp.split_fragment(fragment, meta, caller_name, caller_namespace)); 452 | EXPECT_EQ(meta.size(), 2); 453 | EXPECT_TRUE(caller_name == "caller_name"); 454 | EXPECT_TRUE(caller_namespace == "caller_namespace"); 455 | 456 | fragment = "k1=v1&caller_namespace."; 457 | EXPECT_FALSE(pp.split_fragment(fragment, meta, caller_name, caller_namespace)); 458 | 459 | fragment = "k1=v1&."; 460 | EXPECT_FALSE(pp.split_fragment(fragment, meta, caller_name, caller_namespace)); 461 | 462 | fragment = "k1=v1&k2=&caller_namespace.caller_name"; 463 | EXPECT_FALSE(pp.split_fragment(fragment, meta, caller_name, caller_namespace)); 464 | 465 | caller_name.clear(); 466 | caller_namespace.clear(); 467 | meta.clear(); 468 | fragment = "*.*"; 469 | EXPECT_TRUE(pp.split_fragment(fragment, meta, caller_name, caller_namespace)); 470 | EXPECT_TRUE(caller_name == "*"); 471 | EXPECT_TRUE(caller_namespace == "*"); 472 | } 473 | */ 474 | 475 | TEST(polaris_policy_unittest, select) 476 | { 477 | std::vector routing_inbounds; 478 | fill_inbounds_a_b(routing_inbounds); 479 | 480 | std::vector instances; 481 | fill_instances(instances); 482 | 483 | PolarisPolicy pp(&conf); 484 | pp.update_instances(instances); 485 | pp.update_inbounds(routing_inbounds); 486 | 487 | EndpointAddress *addr; 488 | ParsedURI uri; 489 | 490 | std::string url = "http://b_namespace.b:8080#k1_env=v1_base&k2_number=v2_prime&a_namespace.a"; 491 | EXPECT_EQ(URIParser::parse(url, uri), 0); 492 | 493 | pp.select(uri, NULL, &addr); 494 | EXPECT_EQ(atoi(addr->port.c_str()), 8002); 495 | } 496 | 497 | TEST(polaris_policy_unittest, meta_router) 498 | { 499 | std::vector routing_inbounds; 500 | fill_inbounds_a_b(routing_inbounds); 501 | 502 | std::vector instances; 503 | fill_instances(instances); 504 | 505 | conf.set_dst_meta_router(true); 506 | PolarisPolicy pp(&conf); 507 | pp.update_instances(instances); 508 | pp.update_inbounds(routing_inbounds); 509 | 510 | EndpointAddress *addr; 511 | ParsedURI uri; 512 | std::string url = "http://b_namespace.b:8080#meta.k1=v1&meta.k2=v2"; 513 | EXPECT_EQ(URIParser::parse(url, uri), 0); 514 | 515 | pp.select(uri, NULL, &addr); 516 | EXPECT_EQ(atoi(addr->port.c_str()), 8003); 517 | 518 | url = "http://b_namespace.b:8080#meta.k1=v1"; 519 | EXPECT_EQ(URIParser::parse(url, uri), 0); 520 | 521 | pp.select(uri, NULL, &addr); 522 | EXPECT_EQ(atoi(addr->port.c_str()), 8002); 523 | conf.set_rule_base_router(true); 524 | } 525 | 526 | int main(int argc, char* argv[]) 527 | { 528 | ::testing::InitGoogleTest(&argc, argv); 529 | 530 | init(); 531 | 532 | EXPECT_EQ(RUN_ALL_TESTS(), 0); 533 | 534 | deinit(); 535 | 536 | return 0; 537 | } 538 | 539 | -------------------------------------------------------------------------------- /src/PolarisConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef _POLARISCONFIG_H_ 2 | #define _POLARISCONFIG_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace polaris { 10 | 11 | #define POLARIS_STATE_ERROR 70 12 | 13 | enum { 14 | POLARIS_ERR_SYS_ERROR = 1, // WFT_STATE_SYS_ERROR 15 | POLARIS_ERR_SSL_ERROR = 65, // WFT_STATE_SSL_ERROR 16 | POLARIS_ERR_DNS_ERROR = 66, // WFT_STATE_DNS_ERROR 17 | POLARIS_ERR_TASK_ERROR = 67, // WFT_STATE_TASK_ERROR 18 | 19 | POLARIS_ERR_UNKNOWN_ERROR = 1000, // kReturnUnknownError 20 | POLARIS_ERR_INIT_FAILED = 1005, // kReturnInvalidState 21 | POLARIS_ERR_SERVER_PARSE = 1006, // kReturnServerError 22 | POLARIS_ERR_NO_INSTANCE = 1010, // kReturnInstanceNotFound 23 | POLARIS_ERR_INVALID_ROUTE_RULE = 1011, // kReturnInvalidRouteRule 24 | POLARIS_ERR_SERVICE_NOT_FOUND = 1015, // kReturnServiceNotFound 25 | POLARIS_ERR_DOUBLE_OPERATION = 1200, // kReturnExistedResource 26 | POLARIS_ERR_HEARTBEAT_DISABLE = 1202, // kReturnHealthyCheckDisable 27 | POLARIS_ERR_EXISTED_POLICY = 1300, // kReturnSystemServiceNotConfigured 28 | }; 29 | 30 | struct polaris_config { 31 | // polaris global config 32 | // global/system 33 | std::string discover_namespace; 34 | std::string discover_name; 35 | uint64_t discover_refresh_interval; 36 | std::string healthcheck_namespace; 37 | std::string healthcheck_name; 38 | uint64_t healthcheck_refresh_interval; 39 | std::string monitor_namespace; 40 | std::string monitor_name; 41 | uint64_t monitor_refresh_interval; 42 | std::string metric_namespace; 43 | std::string metric_name; 44 | uint64_t metric_refresh_interval; 45 | // global/api 46 | std::string api_bindIf; 47 | std::string api_bindIP; 48 | std::string api_location_zone; 49 | std::string api_location_region; 50 | std::string api_location_campus; 51 | uint64_t api_timeout_milliseconds; 52 | int api_retry_max; 53 | uint64_t api_retry_milliseconds; 54 | // global/serverConnector Deprecated, use client url 55 | // polaris server address 56 | //std::vector server_connector_hosts; 57 | // polaris server protocol: http, grpc, trpc 58 | //std::string server_connector_protocol; 59 | // connect timeout eg. 100ms 60 | //uint64_t server_connect_timeout; 61 | // global/stateReport's config 62 | bool state_report_enable; 63 | std::vector state_report_chain; 64 | uint64_t state_report_window; 65 | int state_report_buckets; 66 | 67 | // polaris consumer config 68 | // consumer/localCache 69 | // 服务定期刷新周期 70 | uint64_t service_refresh_interval; 71 | uint64_t service_expire_time; 72 | // consumer/circuitBreaker: 熔断 73 | // 是否启用节点熔断功能 74 | bool circuit_breaker_enable; 75 | // 实例定时熔断检测周期 76 | uint64_t circuit_breaker_check_period; 77 | // 熔断插件名 78 | std::vector circuit_breaker_chain; 79 | // circuitBreaker plugin: errorCount 80 | // 是否启用基于周期连续错误数熔断策略配置 81 | // 触发连续错误熔断的阈值 82 | int error_count_request_threshold; 83 | // 连续失败的统计周期 84 | uint64_t error_count_stat_time_window; 85 | // 熔断器打开后,多久后转换为半开状态 86 | uint64_t error_count_sleep_window; 87 | // 熔断器半开后最大允许的请求数 88 | int error_count_max_request_halfopen; 89 | // 熔断器半开到关闭所必须的最少成功请求数 90 | int error_count_least_success_halfopen; 91 | // circuitBreaker plugin: errorRate 92 | // 基于周期错误率的熔断策略配置 93 | // 触发错误率熔断的最低请求阈值 94 | int error_rate_request_threshold; 95 | // 触发错误率熔断的阈值 96 | double error_rate_threshold; 97 | // 错误率熔断的统计周期 98 | uint64_t error_rate_stat_time_window; 99 | // 错误率熔断的最小统计单元数量 100 | int error_rate_num_buckets; 101 | // 熔断器打开后,多久后转换为半开状态 102 | uint64_t error_rate_sleep_window; 103 | // 熔断器半开后最大允许的请求数 104 | int error_rate_max_request_halfopen; 105 | // 熔断器半开到关闭所必须的最少成功请求数 106 | int error_rate_least_success_halfopen; 107 | // set集群熔断开关 108 | bool setcluster_circuit_breaker_enable; 109 | // consumer/healthCheck: 故障探测 110 | // 是否启用故障探测功能 111 | bool health_check_enable; 112 | // 定时故障探测周期 113 | uint64_t health_check_period; 114 | // 故障探测策略 115 | std::vector health_check_chain; 116 | // 基于TCP的故障探测策略 117 | // 探测超时时间 118 | uint64_t plugin_tcp_timeout; 119 | // 探测失败重试次数 120 | int plugin_tcp_retry; 121 | // tcp发送的探测包,可选字段,假如不配置,则默认只做连接探测 122 | std::string plugin_tcp_send; 123 | // 期望接收的TCP回复包,可选字段,假如不配置,则默认只做连接或发包探测 124 | std::string plugin_tcp_receive; 125 | // 基于UDP的故障探测策略 126 | // UDP探测超时时间 127 | uint64_t plugin_udp_timeout; 128 | // UDP探测失败重试次数 129 | int plugin_udp_retry; 130 | // udp发送的探测包,必选字段,假如不配置,则不启动UDP探测 131 | std::string plugin_udp_send; 132 | // 期望接收的UDP回复包,必选字段,假如不配置,则不启动UDP探测 133 | std::string plugin_udp_receive; 134 | // 基于HTTP的故障探测策略 135 | // HTTP探测超时时间 136 | uint64_t plugin_http_timeout; 137 | // http探测路径,必选字段,假如不配置,则不启用http探测 138 | std::string plugin_http_path; 139 | std::string load_balancer_type; 140 | // consumer/router: 路由 141 | // 基于主调和被调服务规则的路由策略 142 | // 就近路由策略 143 | std::vector service_router_chain; 144 | // 就近路由的最小匹配级别: region(大区)、zone(区域)、campus(园区) 145 | std::string nearby_match_level; 146 | // 就近路由最大的匹配级别 147 | std::string nearby_max_match_level; 148 | // 是否启用按服务不健康实例比例进行降级 149 | bool nearby_unhealthy_degrade; 150 | // 需要进行降级的实例比例,不健康实例达到百分之多少才进行降级 151 | int nearby_unhealthy_degrade_percent; 152 | // 允许全死全活 153 | bool nearby_enable_recover_all; 154 | // 严格就近配置 155 | bool nearby_strict_nearby; 156 | 157 | // polaris rateLimiter config 158 | std::string rate_limit_mode; 159 | std::string rate_limit_cluster_namespace; 160 | std::string rate_limit_cluster_name; 161 | }; 162 | 163 | struct discover_request { 164 | int type; 165 | std::string service_name; 166 | std::string service_namespace; 167 | std::string revision; 168 | }; 169 | 170 | struct instance { 171 | std::string id; 172 | std::string service; 173 | std::string service_namespace; 174 | std::string vpc_id; 175 | std::string host; 176 | int port; 177 | std::string protocol; 178 | std::string version; 179 | int priority; 180 | int weight; 181 | bool enable_healthcheck; 182 | std::string healthcheck_type; 183 | int healthcheck_ttl; 184 | bool healthy; 185 | bool isolate; 186 | std::string logic_set; 187 | std::string mtime; 188 | std::string revision; 189 | std::string region; 190 | std::string zone; 191 | std::string campus; 192 | std::map metadata; 193 | }; 194 | 195 | struct cluster_result { 196 | int code; 197 | std::string info; 198 | int amount; 199 | int size; 200 | // instances 201 | std::vector instances; 202 | }; 203 | 204 | struct discover_result { 205 | int code; 206 | std::string info; 207 | std::string type; 208 | // service data 209 | std::string service_namespace; 210 | std::string service_name; 211 | std::string service_revision; 212 | std::map service_metadata; 213 | std::string service_ports; 214 | std::string service_business; 215 | std::string service_department; 216 | std::string service_cmdbmod1; 217 | std::string service_cmdbmod2; 218 | std::string service_cmdbmod3; 219 | std::string service_comment; 220 | std::string service_owners; 221 | std::string service_ctime; 222 | std::string service_mtime; 223 | std::string service_platform_id; 224 | // instances 225 | std::vector instances; 226 | }; 227 | 228 | struct meta_label { 229 | std::string type; 230 | std::string value_type; 231 | std::string value; 232 | }; 233 | 234 | struct source_bound { 235 | std::string service; 236 | std::string service_namespace; 237 | std::map metadata; 238 | }; 239 | 240 | struct destination_bound { 241 | std::string service; 242 | std::string service_namespace; 243 | std::map metadata; 244 | int priority; 245 | int weight; 246 | }; 247 | 248 | struct routing_bound { 249 | std::vector source_bounds; 250 | std::vector destination_bounds; 251 | }; 252 | 253 | struct route_result { 254 | int code; 255 | std::string info; 256 | std::string type; 257 | std::string service_name; 258 | std::string service_namespace; 259 | std::string routing_service; 260 | std::string routing_namespace; 261 | std::vector routing_inbounds; 262 | std::vector routing_outbounds; 263 | std::string routing_ctime; 264 | std::string routing_mtime; 265 | std::string routing_revision; 266 | }; 267 | 268 | struct register_request { 269 | std::string service; 270 | std::string service_namespace; 271 | std::string service_token; 272 | struct instance inst; 273 | }; 274 | 275 | struct deregister_request { 276 | std::string id; 277 | std::string service; 278 | std::string service_namespace; 279 | std::string service_token; 280 | std::string host; 281 | int port; 282 | }; 283 | 284 | struct ratelimit_request { 285 | int type; 286 | std::string service_name; 287 | std::string service_namespace; 288 | std::string revision; 289 | }; 290 | 291 | struct ratelimit_amount { 292 | int max_amount; 293 | std::string valid_duration; 294 | }; 295 | 296 | struct ratelimit_rule { 297 | std::string id; 298 | std::string service; 299 | std::string service_namespace; 300 | int priority; 301 | std::string type; 302 | std::map meta_labels; 303 | std::vector ratelimit_amounts; 304 | std::string action; 305 | bool disable; 306 | std::string ctime; 307 | std::string mtime; 308 | std::string revision; 309 | }; 310 | 311 | struct ratelimit_result { 312 | int code; 313 | std::string info; 314 | std::string type; 315 | std::string service_name; 316 | std::string service_namespace; 317 | std::string service_revision; 318 | std::vector ratelimit_rules; 319 | std::string ratelimit_revision; 320 | }; 321 | 322 | struct circuitbreaker_source { 323 | std::string service; 324 | std::string service_namespace; 325 | std::map meta_labels; 326 | }; 327 | 328 | struct recover_config { 329 | std::string sleep_window; 330 | std::vector request_rate_after_halfopen; 331 | }; 332 | 333 | struct circuitbreaker_policy { 334 | struct error_rate_config {}; 335 | struct error_rate_config error_rate; 336 | struct slow_rate_config {}; 337 | struct slow_rate_config slow_rate; 338 | }; 339 | 340 | struct circuitbreaker_destination { 341 | std::string service; 342 | std::string service_namespace; 343 | std::map meta_labels; 344 | int resource; 345 | int type; 346 | int scope; 347 | int metric_precision; 348 | std::string metric_window; 349 | std::string update_interval; 350 | struct recover_config recover; 351 | struct circuitbreaker_policy policy; 352 | }; 353 | 354 | struct circuitbreaker_rule { 355 | std::vector circuitbreaker_sources; 356 | std::vector circuitbreaker_destinations; 357 | }; 358 | 359 | struct circuitbreaker { 360 | std::string id; 361 | std::string version; 362 | std::string circuitbreaker_name; 363 | std::string circuitbreaker_namespace; 364 | std::string service_name; 365 | std::string service_namespace; 366 | std::vector circuitbreaker_inbounds; 367 | std::vector circuitbreaker_outbounds; 368 | std::string revision; 369 | }; 370 | 371 | struct circuitbreaker_request { 372 | int type; 373 | std::string service_name; 374 | std::string service_namespace; 375 | std::string revision; 376 | }; 377 | 378 | struct circuitbreaker_result { 379 | int code; 380 | std::string info; 381 | std::string type; 382 | std::string service_name; 383 | std::string service_namespace; 384 | std::string service_revision; 385 | struct circuitbreaker data; 386 | std::string revision; 387 | }; 388 | 389 | class PolarisInstance { 390 | public: 391 | PolarisInstance() { 392 | this->ref = new std::atomic(1); 393 | this->inst = new instance; 394 | instance_init(); 395 | } 396 | 397 | virtual ~PolarisInstance() { this->decref(); } 398 | 399 | PolarisInstance(const PolarisInstance ©) { 400 | this->ref = copy.ref; 401 | this->inst = copy.inst; 402 | this->incref(); 403 | } 404 | 405 | PolarisInstance(PolarisInstance &&move) { 406 | this->ref = move.ref; 407 | this->inst = move.inst; 408 | move.ref = new std::atomic(1); 409 | move.inst = new instance; 410 | move.instance_init(); 411 | } 412 | 413 | PolarisInstance &operator=(const PolarisInstance ©) { 414 | if (this != ©) { 415 | this->decref(); 416 | this->ref = copy.ref; 417 | this->inst = copy.inst; 418 | this->incref(); 419 | } 420 | return *this; 421 | } 422 | 423 | PolarisInstance &operator=(PolarisInstance &&move) { 424 | if (this != &move) { 425 | this->decref(); 426 | this->ref = move.ref; 427 | this->inst = move.inst; 428 | move.ref = new std::atomic(1); 429 | move.inst = new instance; 430 | move.instance_init(); 431 | } 432 | return *this; 433 | } 434 | 435 | private: 436 | void incref() { (*this->ref)++; } 437 | 438 | void decref() { 439 | if (--*this->ref == 0) { 440 | delete this->ref; 441 | delete this->inst; 442 | } 443 | } 444 | 445 | void instance_init(); 446 | 447 | public: 448 | void set_id(const std::string &id) { this->inst->id = id; } 449 | void set_service(const std::string &service) 450 | { this->inst->service = service; } 451 | void set_service_namespace(const std::string &ns) 452 | { this->inst->service_namespace = ns; } 453 | void set_host(const std::string &host) { this->inst->host = host; } 454 | void set_port(int port) { this->inst->port = port; } 455 | void set_protocol(const std::string &protocol) 456 | { this->inst->protocol = protocol; } 457 | void set_version(const std::string &version) 458 | { this->inst->version = version; } 459 | void set_region(const std::string ®ion) { this->inst->region = region; } 460 | void set_zone(const std::string &zone) { this->inst->zone = zone; } 461 | void set_campus(const std::string &campus) { this->inst->campus = campus; } 462 | void set_weight(int weight) { 463 | if (weight >= 0 && weight <= 100) 464 | this->inst->weight = weight; 465 | } 466 | void set_enable_healthcheck(bool enable) 467 | { this->inst->enable_healthcheck = enable; } 468 | void set_healthcheck_ttl(int ttl) { this->inst->healthcheck_ttl = ttl; } 469 | void set_isolate(bool isolate) { this->inst->isolate = isolate; } 470 | void set_healthy(bool healthy) { this->inst->healthy = healthy; } 471 | void set_logic_set(const std::string &logic_set) 472 | { this->inst->logic_set = logic_set; } 473 | void set_metadata(const std::map &metadata) { 474 | this->inst->metadata = metadata; 475 | } 476 | 477 | const struct instance *get_instance() const { return this->inst; } 478 | 479 | int get_healthcheck_ttl() const { return this->inst->healthcheck_ttl; } 480 | bool get_enable_healthcheck() const { return this->inst->enable_healthcheck; } 481 | const std::string& get_host() const { return this->inst->host; } 482 | int get_port() const { return this->inst->port; } 483 | 484 | private: 485 | std::atomic *ref; 486 | struct instance *inst; 487 | }; 488 | 489 | class PolarisConfig { 490 | public: 491 | PolarisConfig() { 492 | this->ptr = new polaris_config; 493 | this->ref = new std::atomic(1); 494 | polaris_config_init(); 495 | } 496 | 497 | virtual ~PolarisConfig() { this->decref(); } 498 | 499 | PolarisConfig(PolarisConfig &&move) { 500 | this->ptr = move.ptr; 501 | this->ref = move.ref; 502 | move.ref = new std::atomic(1); 503 | move.ptr = new polaris_config; 504 | move.polaris_config_init(); 505 | } 506 | 507 | PolarisConfig &operator=(const PolarisConfig ©) { 508 | if (this != ©) { 509 | this->decref(); 510 | this->ptr = copy.ptr; 511 | this->ref = copy.ref; 512 | this->incref(); 513 | } 514 | return *this; 515 | } 516 | 517 | PolarisConfig &operator=(PolarisConfig &&move) { 518 | if (this != &move) { 519 | this->decref(); 520 | this->ptr = move.ptr; 521 | this->ref = move.ref; 522 | move.ref = new std::atomic(1); 523 | move.ptr = new polaris_config; 524 | move.polaris_config_init(); 525 | } 526 | return *this; 527 | } 528 | 529 | private: 530 | void incref() { (*this->ref)++; } 531 | void decref() { 532 | if (--*this->ref == 0) { 533 | delete this->ptr; 534 | delete this->ref; 535 | } 536 | } 537 | 538 | public: 539 | int init_from_yaml(const std::string &yaml_file); 540 | 541 | std::string get_discover_namespace() const { 542 | return this->ptr->discover_namespace; 543 | } 544 | std::string get_discover_name() const { 545 | return this->ptr->discover_name; 546 | } 547 | uint64_t get_discover_refresh_interval() const { 548 | return this->ptr->discover_refresh_interval; 549 | } 550 | std::string get_healthcheck_namespace() const { 551 | return this->ptr->healthcheck_namespace; 552 | } 553 | std::string get_healthcheck_name() const { 554 | return this->ptr->healthcheck_name; 555 | } 556 | uint64_t get_healthcheck_refresh_interval() const { 557 | return this->ptr->healthcheck_refresh_interval; 558 | } 559 | std::string get_monitor_namespace() const { 560 | return this->ptr->monitor_namespace; 561 | } 562 | std::string get_monitor_name() const { return this->ptr->monitor_name; } 563 | uint64_t get_monitor_refresh_interval() const { 564 | return this->ptr->monitor_refresh_interval; 565 | } 566 | std::string get_metric_namespace() const { 567 | return this->ptr->metric_namespace; 568 | } 569 | std::string get_metric_name() const { return this->ptr->metric_name; } 570 | uint64_t get_metric_refresh_interval() const { 571 | return this->ptr->metric_refresh_interval; 572 | } 573 | std::string get_api_bindIf() const { return this->ptr->api_bindIf; } 574 | std::string get_api_bindIP() const { return this->ptr->api_bindIP; } 575 | std::string get_api_location_zone() const { 576 | return this->ptr->api_location_zone; 577 | } 578 | std::string get_api_location_region() const { 579 | return this->ptr->api_location_region; 580 | } 581 | std::string get_api_location_campus() const { 582 | return this->ptr->api_location_campus; 583 | } 584 | uint64_t get_api_timeout_milliseconds() const { 585 | return this->ptr->api_timeout_milliseconds; 586 | } 587 | int get_api_retry_max() const { return this->ptr->api_retry_max; } 588 | uint64_t get_api_retry_milliseconds() const { 589 | return this->ptr->api_retry_milliseconds; 590 | } 591 | bool get_state_report_enable() const { 592 | return this->ptr->state_report_enable; 593 | } 594 | std::vector get_state_report_chain() const { 595 | return this->ptr->state_report_chain; 596 | } 597 | uint64_t get_state_report_window() const { 598 | return this->ptr->state_report_window; 599 | } 600 | int get_state_report_buckets() const { 601 | return this->ptr->state_report_buckets; 602 | } 603 | uint64_t get_service_refresh_interval() const { 604 | return this->ptr->service_refresh_interval; 605 | } 606 | uint64_t get_service_expire_time() const { 607 | return this->ptr->service_expire_time; 608 | } 609 | bool get_circuit_breaker_enable() const { 610 | return this->ptr->circuit_breaker_enable; 611 | } 612 | uint64_t get_circuit_breaker_check_period() const { 613 | return this->ptr->circuit_breaker_check_period; 614 | } 615 | std::vector get_circuit_breaker_chain() const { 616 | return this->ptr->circuit_breaker_chain; 617 | } 618 | int get_error_count_request_threshold() const { 619 | return this->ptr->error_count_request_threshold; 620 | } 621 | uint64_t get_error_count_stat_time_window() const { 622 | return this->ptr->error_count_stat_time_window; 623 | } 624 | uint64_t get_error_count_sleep_window() const { 625 | return this->ptr->error_count_sleep_window; 626 | } 627 | int get_error_count_max_request_halfopen() const { 628 | return this->ptr->error_count_max_request_halfopen; 629 | } 630 | int get_error_count_least_success_halfopen() const { 631 | return this->ptr->error_count_least_success_halfopen; 632 | } 633 | int get_error_rate_request_threshold() const { 634 | return this->ptr->error_rate_request_threshold; 635 | } 636 | double get_error_rate_threshold() const { 637 | return this->ptr->error_rate_threshold; 638 | } 639 | uint64_t get_error_rate_stat_time_window() const { 640 | return this->ptr->error_rate_stat_time_window; 641 | } 642 | int get_error_rate_num_buckets() const { 643 | return this->ptr->error_rate_num_buckets; 644 | } 645 | uint64_t get_error_rate_sleep_window() const { 646 | return this->ptr->error_rate_sleep_window; 647 | } 648 | int get_error_rate_max_request_halfopen() const { 649 | return this->ptr->error_rate_max_request_halfopen; 650 | } 651 | int get_error_rate_least_success_halfopen() const { 652 | return this->ptr->error_rate_least_success_halfopen; 653 | } 654 | 655 | bool get_setcluster_circuit_breaker_enable() const { 656 | return this->ptr->setcluster_circuit_breaker_enable; 657 | } 658 | bool get_health_check_enable() const { 659 | return this->ptr->health_check_enable; 660 | } 661 | uint64_t get_health_check_period() const { 662 | return this->ptr->health_check_period; 663 | } 664 | std::vector get_health_check_chain() const { 665 | return this->ptr->health_check_chain; 666 | } 667 | uint64_t get_plugin_tcp_timeout() const { 668 | return this->ptr->plugin_tcp_timeout; 669 | } 670 | int get_plugin_tcp_retry() const { return this->ptr->plugin_tcp_retry; } 671 | std::string get_plugin_tcp_send() const { 672 | return this->ptr->plugin_tcp_send; 673 | } 674 | std::string get_plugin_tcp_receive() const { 675 | return this->ptr->plugin_tcp_receive; 676 | } 677 | uint64_t get_plugin_udp_timeout() const { 678 | return this->ptr->plugin_udp_timeout; 679 | } 680 | int get_plugin_udp_retry() const { return this->ptr->plugin_udp_retry; } 681 | std::string get_plugin_udp_send() const { 682 | return this->ptr->plugin_udp_send; 683 | } 684 | std::string get_plugin_udp_receive() const { 685 | return this->ptr->plugin_udp_receive; 686 | } 687 | uint64_t get_plugin_http_timeout() const { 688 | return this->ptr->plugin_http_timeout; 689 | } 690 | std::string get_plugin_http_path() const { 691 | return this->ptr->plugin_http_path; 692 | } 693 | std::string get_load_balancer_type() const { 694 | return this->ptr->load_balancer_type; 695 | } 696 | std::vector get_service_router_chain() const { 697 | return this->ptr->service_router_chain; 698 | } 699 | std::string get_nearby_match_level() const { 700 | return this->ptr->nearby_match_level; 701 | } 702 | const std::string get_nearby_max_match_level() const { 703 | return this->ptr->nearby_max_match_level; 704 | } 705 | bool get_nearby_unhealthy_degrade() const { 706 | return this->ptr->nearby_unhealthy_degrade; 707 | } 708 | int get_nearby_unhealthy_degrade_percent() const { 709 | return this->ptr->nearby_unhealthy_degrade_percent; 710 | } 711 | bool get_nearby_enable_recover_all() const { 712 | return this->ptr->nearby_enable_recover_all; 713 | } 714 | bool get_nearby_strict_nearby() const { 715 | return this->ptr->nearby_strict_nearby; 716 | } 717 | std::string get_rate_limit_mode() const { 718 | return this->ptr->rate_limit_mode; 719 | } 720 | std::string get_rate_limit_cluster_namespace() const { 721 | return this->ptr->rate_limit_cluster_namespace; 722 | } 723 | std::string get_rate_limit_cluster_name() const { 724 | return this->ptr->rate_limit_cluster_name; 725 | } 726 | // get all polaris_config 727 | const struct polaris_config *get_polaris_config() const { 728 | return this->ptr; 729 | } 730 | 731 | private: 732 | void polaris_config_init() { 733 | polaris_config_init_global(); 734 | polaris_config_init_consumer(); 735 | polaris_config_init_ratelimiter(); 736 | } 737 | 738 | void polaris_config_init_global(); 739 | void polaris_config_init_consumer(); 740 | void polaris_config_init_ratelimiter(); 741 | 742 | private: 743 | std::atomic *ref; 744 | struct polaris_config *ptr; 745 | }; 746 | 747 | }; // namespace polaris 748 | 749 | #endif 750 | -------------------------------------------------------------------------------- /src/PolarisManager.cc: -------------------------------------------------------------------------------- 1 | #include "PolarisManager.h" 2 | 3 | namespace polaris { 4 | 5 | #define RETRY_MAX 2 6 | 7 | class Manager 8 | { 9 | public: 10 | Manager(const std::string& polaris_url, const std::string& platform_id, 11 | const std::string& platform_token, PolarisConfig config); 12 | ~Manager(); 13 | 14 | int watch_service(const std::string& service_namespace, 15 | const std::string& service_name); 16 | int unwatch_service(const std::string& service_namespace, 17 | const std::string& service_name); 18 | 19 | int register_service(const std::string& service_namespace, 20 | const std::string& service_name, 21 | const std::string& service_token, 22 | int heartbeat_interval, 23 | PolarisInstance instance); 24 | int deregister_service(const std::string& service_namespace, 25 | const std::string& service_name, 26 | const std::string& service_token, 27 | PolarisInstance instance); 28 | 29 | int get_error() const { return this->error; } 30 | void get_watching_list(std::vector& list); 31 | void get_register_list(std::vector& list); 32 | 33 | public: 34 | void exit_locked(); 35 | void incref() { ++this->ref; } 36 | void decref() 37 | { 38 | if (--this->ref == 0) 39 | delete this; 40 | } 41 | 42 | private: 43 | std::atomic ref; 44 | int retry_max; 45 | int error; 46 | std::string polaris_url; 47 | std::string platform_id; 48 | std::string platform_token; 49 | PolarisConfig config; 50 | PolarisClient client; 51 | 52 | struct watch_info 53 | { 54 | bool watching; 55 | std::string service_revision; 56 | std::string routing_revision; 57 | std::condition_variable cond; 58 | }; 59 | struct register_info 60 | { 61 | bool heartbeating; 62 | std::condition_variable cond; 63 | }; 64 | std::mutex mutex; 65 | std::unordered_map watch_status; 66 | std::unordered_map unwatch_policies; 67 | std::unordered_map register_status; 68 | 69 | enum 70 | { 71 | INIT_SUCCESS = 0, 72 | INIT_FAILED = 1, 73 | MANAGER_EXITED = 2, 74 | }; 75 | int status; 76 | 77 | std::function discover_cb; 78 | std::function discover_timer_cb; 79 | std::function register_cb; 80 | std::function deregister_cb; 81 | std::function heartbeat_cb; 82 | std::function heartbeat_timer_cb; 83 | 84 | private: 85 | void set_error(int state, int error); 86 | bool update_policy_locked(const std::string& policy_name, 87 | struct discover_result *discover, 88 | struct route_result *route, 89 | bool is_user_request, 90 | bool update_instance, 91 | bool update_routing); 92 | bool update_heartbeat_locked(const std::string& instance_name, 93 | bool is_user_request); 94 | 95 | void discover_callback(PolarisTask *task); 96 | void register_callback(PolarisTask *task); 97 | void deregister_callback(PolarisTask *task); 98 | void heartbeat_callback(PolarisTask *task); 99 | void discover_timer_callback(WFTimerTask *task); 100 | void heartbeat_timer_callback(WFTimerTask *task); 101 | }; 102 | 103 | struct consumer_context 104 | { 105 | std::string service_namespace; 106 | std::string service_name; 107 | Manager *mgr; 108 | }; 109 | 110 | struct provider_context 111 | { 112 | std::string service_namespace; 113 | std::string service_name; 114 | std::string service_token; 115 | int heartbeat_interval; 116 | PolarisInstance instance; 117 | Manager *mgr; 118 | }; 119 | 120 | struct deregister_context 121 | { 122 | deregister_context() : wait_group(1) { } 123 | 124 | WFFacilities::WaitGroup wait_group; 125 | std::string instance_name; 126 | }; 127 | 128 | PolarisManager::PolarisManager(const std::string& polaris_url) 129 | { 130 | PolarisConfig config; 131 | this->ptr = new Manager(polaris_url, "", "", std::move(config)); 132 | } 133 | 134 | PolarisManager::PolarisManager(const std::string& polaris_url, 135 | const std::string& yaml_file) 136 | { 137 | PolarisConfig config; 138 | config.init_from_yaml(yaml_file); 139 | this->ptr = new Manager(polaris_url, "", "", std::move(config)); 140 | } 141 | 142 | PolarisManager::PolarisManager(const std::string& polaris_url, 143 | const std::string& platform_id, 144 | const std::string& platform_token, 145 | const std::string& yaml_file) 146 | { 147 | PolarisConfig config; 148 | config.init_from_yaml(yaml_file); 149 | this->ptr = new Manager(polaris_url, platform_id, 150 | platform_token, std::move(config)); 151 | } 152 | 153 | PolarisManager::~PolarisManager() 154 | { 155 | this->ptr->exit_locked(); 156 | } 157 | 158 | int PolarisManager::get_error() const 159 | { 160 | return this->ptr->get_error(); 161 | } 162 | 163 | void Manager::exit_locked() 164 | { 165 | bool flag = false; 166 | 167 | this->mutex.lock(); 168 | if (--this->ref == 0) 169 | flag = true; 170 | else 171 | this->status = MANAGER_EXITED; 172 | this->mutex.unlock(); 173 | 174 | if (flag) 175 | delete this; 176 | } 177 | 178 | int PolarisManager::watch_service(const std::string& service_namespace, 179 | const std::string& service_name) 180 | { 181 | return this->ptr->watch_service(service_namespace, service_name); 182 | } 183 | 184 | int PolarisManager::unwatch_service(const std::string& service_namespace, 185 | const std::string& service_name) 186 | { 187 | return this->ptr->unwatch_service(service_namespace, service_name); 188 | } 189 | 190 | int PolarisManager::register_service(const std::string& service_namespace, 191 | const std::string& service_name, 192 | PolarisInstance instance) 193 | { 194 | return this->ptr->register_service(service_namespace, service_name, 195 | "" , 0, std::move(instance)); 196 | } 197 | 198 | int PolarisManager::register_service(const std::string& service_namespace, 199 | const std::string& service_name, 200 | const std::string& service_token, 201 | PolarisInstance instance) 202 | { 203 | return this->ptr->register_service(service_namespace, service_name, 204 | service_token, 0, std::move(instance)); 205 | } 206 | 207 | int PolarisManager::register_service(const std::string& service_namespace, 208 | const std::string& service_name, 209 | const std::string& service_token, 210 | int heartbeat_interval, 211 | PolarisInstance instance) 212 | { 213 | return this->ptr->register_service(service_namespace, service_name, 214 | service_token, heartbeat_interval, 215 | std::move(instance)); 216 | } 217 | 218 | int PolarisManager::deregister_service(const std::string& service_namespace, 219 | const std::string& service_name, 220 | PolarisInstance instance) 221 | { 222 | return this->ptr->deregister_service(service_namespace, service_name, 223 | "", std::move(instance)); 224 | } 225 | 226 | int PolarisManager::deregister_service(const std::string& service_namespace, 227 | const std::string& service_name, 228 | const std::string& service_token, 229 | PolarisInstance instance) 230 | { 231 | return this->ptr->deregister_service(service_namespace, service_name, 232 | service_token, std::move(instance)); 233 | } 234 | 235 | void PolarisManager::get_watching_list(std::vector& list) 236 | { 237 | this->ptr->get_watching_list(list); 238 | } 239 | 240 | void PolarisManager::get_register_list(std::vector& list) 241 | { 242 | this->ptr->get_register_list(list); 243 | } 244 | 245 | Manager::Manager(const std::string& polaris_url, 246 | const std::string& platform_id, 247 | const std::string& platform_token, 248 | PolarisConfig config) : 249 | ref(1), 250 | error(0), 251 | polaris_url(polaris_url), 252 | platform_id(platform_id), 253 | platform_token(platform_token), 254 | config(std::move(config)) 255 | { 256 | if (client.init(polaris_url) == 0) 257 | this->status = INIT_SUCCESS; 258 | else 259 | { 260 | this->status = INIT_FAILED; 261 | this->error = POLARIS_ERR_INIT_FAILED; 262 | } 263 | 264 | this->retry_max = RETRY_MAX; 265 | 266 | this->discover_cb = std::bind(&Manager::discover_callback, 267 | this, std::placeholders::_1); 268 | this->discover_timer_cb = std::bind(&Manager::discover_timer_callback, 269 | this, std::placeholders::_1); 270 | this->register_cb = std::bind(&Manager::register_callback, 271 | this, std::placeholders::_1); 272 | this->deregister_cb = std::bind(&Manager::deregister_callback, 273 | this, std::placeholders::_1); 274 | this->heartbeat_cb = std::bind(&Manager::heartbeat_callback, 275 | this, std::placeholders::_1); 276 | this->heartbeat_timer_cb = std::bind(&Manager::heartbeat_timer_callback, 277 | this, std::placeholders::_1); 278 | } 279 | 280 | Manager::~Manager() 281 | { 282 | if (this->status != INIT_FAILED) { 283 | this->client.deinit(); 284 | 285 | for (const auto &i : unwatch_policies) 286 | delete i.second; 287 | 288 | unwatch_policies.clear(); 289 | } 290 | } 291 | 292 | int Manager::watch_service(const std::string& service_namespace, 293 | const std::string& service_name) 294 | { 295 | if (this->status == INIT_FAILED) 296 | return -1; 297 | 298 | PolarisTask *task; 299 | task = this->client.create_discover_task(service_namespace.c_str(), 300 | service_name.c_str(), 301 | this->retry_max, 302 | this->discover_cb); 303 | 304 | WFFacilities::WaitGroup wait_group(1); 305 | task->user_data = &wait_group; 306 | task->set_config(this->config); 307 | if (!this->platform_id.empty() && !this->platform_token.empty()) 308 | { 309 | task->set_platform_id(platform_id); 310 | task->set_platform_token(platform_token); 311 | } 312 | 313 | struct consumer_context *ctx = new consumer_context(); 314 | ctx->service_namespace = service_namespace; 315 | ctx->service_name = service_name; 316 | ctx->mgr = this; 317 | this->incref(); 318 | 319 | SeriesWork *series = Workflow::create_series_work(task, 320 | [](const SeriesWork *series) { 321 | struct consumer_context *ctx; 322 | ctx = (struct consumer_context *)series->get_context(); 323 | ctx->mgr->decref(); 324 | delete ctx; 325 | }); 326 | 327 | series->set_context(ctx); 328 | series->start(); 329 | wait_group.wait(); 330 | 331 | return this->error == 0 ? 0 : -1; 332 | } 333 | 334 | int Manager::unwatch_service(const std::string& service_namespace, 335 | const std::string& service_name) 336 | { 337 | if (this->status == INIT_FAILED) 338 | return -1; 339 | 340 | std::string policy_name = service_namespace + "." + service_name; 341 | 342 | std::unique_lock lock(this->mutex); 343 | auto iter = this->watch_status.find(policy_name); 344 | 345 | if (iter == this->watch_status.end()) 346 | { 347 | this->error = POLARIS_ERR_SERVICE_NOT_FOUND; 348 | return -1; 349 | } 350 | 351 | if (iter->second.watching == true) 352 | { 353 | iter->second.watching = false; 354 | iter->second.cond.wait(lock); 355 | } 356 | this->watch_status.erase(iter); 357 | 358 | PolarisPolicy *pp; 359 | pp = (PolarisPolicy *)WFGlobal::get_name_service()->del_policy(policy_name.c_str()); 360 | this->unwatch_policies.emplace(policy_name, pp); 361 | 362 | return 0; 363 | } 364 | 365 | int Manager::register_service(const std::string& service_namespace, 366 | const std::string& service_name, 367 | const std::string& service_token, 368 | int heartbeat_interval, 369 | PolarisInstance instance) 370 | { 371 | if (this->status == INIT_FAILED) 372 | return -1; 373 | 374 | PolarisTask *task; 375 | task = this->client.create_register_task(service_namespace.c_str(), 376 | service_name.c_str(), 377 | this->retry_max, 378 | this->register_cb); 379 | if (!service_token.empty()) 380 | task->set_service_token(service_token); 381 | if (!this->platform_id.empty() && !this->platform_token.empty()) 382 | { 383 | task->set_platform_id(platform_id); 384 | task->set_platform_token(platform_token); 385 | } 386 | 387 | WFFacilities::WaitGroup wait_group(1); 388 | task->user_data = &wait_group; 389 | task->set_config(this->config); 390 | task->set_polaris_instance(instance); 391 | 392 | struct provider_context *ctx = new provider_context(); 393 | ctx->service_namespace = service_namespace; 394 | ctx->service_name = service_name; 395 | ctx->service_token = service_token; 396 | ctx->heartbeat_interval = heartbeat_interval; 397 | ctx->instance = std::move(instance); 398 | ctx->mgr = this; 399 | this->incref(); 400 | 401 | SeriesWork *series = Workflow::create_series_work(task, 402 | [](const SeriesWork *series) { 403 | struct provider_context *ctx; 404 | ctx = (struct provider_context *)series->get_context(); 405 | ctx->mgr->decref(); 406 | delete ctx; 407 | }); 408 | 409 | series->set_context(ctx); 410 | series->start(); 411 | wait_group.wait(); 412 | 413 | return this->error == 0 ? 0 : -1; 414 | } 415 | 416 | int Manager::deregister_service(const std::string& service_namespace, 417 | const std::string& service_name, 418 | const std::string& service_token, 419 | PolarisInstance instance) 420 | { 421 | if (this->status == INIT_FAILED) 422 | return -1; 423 | 424 | std::string inst = instance.get_host() + ":" + 425 | std::to_string(instance.get_port()); 426 | 427 | this->mutex.lock(); 428 | if (this->register_status.find(inst) == this->register_status.end()) 429 | { 430 | this->error = POLARIS_ERR_SERVICE_NOT_FOUND; 431 | this->mutex.unlock(); 432 | return -1; 433 | } 434 | this->mutex.unlock(); 435 | 436 | PolarisTask *task; 437 | task = this->client.create_deregister_task(service_namespace.c_str(), 438 | service_name.c_str(), 439 | this->retry_max, 440 | this->deregister_cb); 441 | if (!service_token.empty()) 442 | task->set_service_token(service_token); 443 | 444 | struct deregister_context *ctx = new deregister_context(); 445 | ctx->instance_name = std::move(inst); 446 | 447 | task->user_data = ctx; 448 | task->set_config(this->config); 449 | task->set_polaris_instance(std::move(instance)); 450 | task->start(); 451 | ctx->wait_group.wait(); 452 | 453 | return this->error == 0 ? 0 : -1; 454 | } 455 | 456 | void Manager::get_watching_list(std::vector& list) 457 | { 458 | this->mutex.lock(); 459 | for (const auto &kv : this->watch_status) 460 | list.push_back(kv.first); 461 | this->mutex.unlock(); 462 | } 463 | 464 | void Manager::get_register_list(std::vector& list) 465 | { 466 | this->mutex.lock(); 467 | for (const auto &kv : this->register_status) 468 | list.push_back(kv.first); 469 | this->mutex.unlock(); 470 | } 471 | 472 | void Manager::set_error(int state, int error) 473 | { 474 | switch (state) 475 | { 476 | case POLARIS_STATE_ERROR: 477 | this->error = error; 478 | break; 479 | case WFT_STATE_SYS_ERROR: 480 | this->error = POLARIS_ERR_SYS_ERROR; 481 | errno = error; 482 | break; 483 | case WFT_STATE_SSL_ERROR: 484 | this->error = POLARIS_ERR_SSL_ERROR; 485 | break; 486 | case WFT_STATE_DNS_ERROR: 487 | this->error = POLARIS_ERR_DNS_ERROR; 488 | break; 489 | case WFT_STATE_TASK_ERROR: 490 | this->error = POLARIS_ERR_TASK_ERROR; 491 | break; 492 | default: 493 | this->error = POLARIS_ERR_UNKNOWN_ERROR; 494 | break; 495 | } 496 | } 497 | 498 | bool Manager::update_policy_locked(const std::string& policy_name, 499 | struct discover_result *discover, 500 | struct route_result *route, 501 | bool is_user_request, 502 | bool update_instance, 503 | bool update_routing) 504 | { 505 | auto iter = this->watch_status.find(policy_name); 506 | 507 | if (iter != this->watch_status.end()) 508 | { 509 | if (is_user_request) 510 | { 511 | this->error = POLARIS_ERR_DOUBLE_OPERATION; 512 | return false; 513 | } 514 | 515 | if (!iter->second.watching) 516 | { 517 | iter->second.cond.notify_one(); 518 | return false; 519 | } 520 | 521 | if (update_instance) 522 | update_instance = (iter->second.service_revision != 523 | discover->service_revision); 524 | if (update_routing) 525 | update_routing = (iter->second.routing_revision != 526 | route->routing_revision); 527 | } 528 | 529 | WFNameService *ns = WFGlobal::get_name_service(); 530 | PolarisPolicy *pp; 531 | 532 | pp = dynamic_cast(ns->get_policy(policy_name.c_str())); 533 | if (pp == NULL) 534 | { 535 | if (is_user_request) 536 | { 537 | auto iter = this->unwatch_policies.find(policy_name); 538 | if (iter == this->unwatch_policies.end()) 539 | { 540 | PolarisPolicyConfig conf(policy_name, this->config); 541 | pp = new PolarisPolicy(&conf); 542 | } 543 | else 544 | { 545 | pp = iter->second; 546 | this->unwatch_policies.erase(iter); 547 | } 548 | 549 | ns->add_policy(policy_name.c_str(), pp); 550 | } 551 | else 552 | { 553 | this->error = POLARIS_ERR_EXISTED_POLICY; 554 | return false; 555 | } 556 | } 557 | 558 | if (update_instance) 559 | { 560 | pp->update_instances(discover->instances); 561 | this->watch_status[policy_name].service_revision = discover->service_revision; 562 | } 563 | 564 | if (update_routing) 565 | { 566 | pp->update_inbounds(route->routing_inbounds); 567 | pp->update_outbounds(route->routing_outbounds); 568 | this->watch_status[policy_name].routing_revision = route->routing_revision; 569 | } 570 | 571 | this->watch_status[policy_name].watching = false; 572 | return true; 573 | } 574 | 575 | void Manager::discover_callback(PolarisTask *task) 576 | { 577 | if (this->status == MANAGER_EXITED) 578 | return; 579 | 580 | int state = task->get_state(); 581 | int error = task->get_error(); 582 | 583 | struct discover_result discover; 584 | struct route_result route; 585 | struct consumer_context *ctx; 586 | bool update_instance = false; 587 | bool update_routing = false; 588 | bool ret; 589 | 590 | if (state == WFT_STATE_SUCCESS) 591 | { 592 | update_instance = task->get_discover_result(&discover); 593 | update_routing = task->get_route_result(&route); 594 | } 595 | 596 | if (task->user_data) 597 | { 598 | if (state != WFT_STATE_SUCCESS) 599 | this->set_error(state, error); 600 | else 601 | { 602 | if (!update_instance) 603 | this->error = POLARIS_ERR_NO_INSTANCE; 604 | else if (!update_routing) 605 | this->error = POLARIS_ERR_INVALID_ROUTE_RULE; 606 | } 607 | 608 | if (this->error != 0) 609 | { 610 | ((WFFacilities::WaitGroup *)task->user_data)->done(); 611 | return; 612 | } 613 | } 614 | 615 | ctx = (struct consumer_context *)series_of(task)->get_context(); 616 | std::string policy_name = ctx->service_namespace + 617 | "." + ctx->service_name; 618 | 619 | this->mutex.lock(); 620 | ret = this->update_policy_locked(policy_name, &discover, &route, 621 | task->user_data ? true : false, 622 | update_instance, update_routing); 623 | this->mutex.unlock(); 624 | 625 | if (ret == true) 626 | { 627 | WFTimerTask *timer_task; 628 | unsigned int us = this->config.get_discover_refresh_interval() * 1000; 629 | timer_task = WFTaskFactory::create_timer_task(us, this->discover_timer_cb); 630 | series_of(task)->push_back(timer_task); 631 | } 632 | 633 | if (task->user_data) 634 | ((WFFacilities::WaitGroup *)task->user_data)->done(); 635 | 636 | return; 637 | } 638 | 639 | void Manager::discover_timer_callback(WFTimerTask *task) 640 | { 641 | struct consumer_context *ctx; 642 | ctx =(struct consumer_context *)series_of(task)->get_context(); 643 | std::string policy_name = ctx->service_namespace + "." + ctx->service_name; 644 | 645 | if (this->status == MANAGER_EXITED) 646 | return; 647 | 648 | this->mutex.lock(); 649 | auto iter = this->watch_status.find(policy_name); 650 | if (iter == this->watch_status.end()) 651 | { 652 | this->mutex.unlock(); 653 | return; 654 | } 655 | 656 | iter->second.watching = true; 657 | this->mutex.unlock(); 658 | 659 | PolarisTask *discover_task; 660 | discover_task = this->client.create_discover_task(ctx->service_namespace.c_str(), 661 | ctx->service_name.c_str(), 662 | this->retry_max, 663 | this->discover_cb); 664 | discover_task->set_config(this->config); 665 | if (!this->platform_id.empty() && !this->platform_token.empty()) 666 | { 667 | discover_task->set_platform_id(platform_id); 668 | discover_task->set_platform_token(platform_token); 669 | } 670 | 671 | series_of(task)->push_back(discover_task); 672 | } 673 | 674 | void Manager::register_callback(PolarisTask *task) 675 | { 676 | int state = task->get_state(); 677 | int error = task->get_error(); 678 | 679 | PolarisTask *heartbeat_task; 680 | struct provider_context *ctx; 681 | 682 | ctx = (struct provider_context *)series_of(task)->get_context(); 683 | 684 | if (state != WFT_STATE_SUCCESS) 685 | { 686 | this->set_error(state, error); 687 | ((WFFacilities::WaitGroup *)task->user_data)->done(); 688 | return; 689 | } 690 | 691 | if (ctx->instance.get_enable_healthcheck() && ctx->heartbeat_interval != 0) 692 | { 693 | heartbeat_task = this->client.create_heartbeat_task( 694 | ctx->service_namespace.c_str(), 695 | ctx->service_name.c_str(), 696 | this->retry_max, 697 | this->heartbeat_cb); 698 | 699 | heartbeat_task->set_config(this->config); 700 | heartbeat_task->set_polaris_instance(ctx->instance); 701 | heartbeat_task->set_service_token(ctx->service_token); 702 | if (!this->platform_id.empty() && !this->platform_token.empty()) 703 | { 704 | heartbeat_task->set_platform_id(platform_id); 705 | heartbeat_task->set_platform_token(platform_token); 706 | } 707 | 708 | heartbeat_task->user_data = task->user_data; 709 | series_of(task)->push_back(heartbeat_task); 710 | } 711 | else 712 | { 713 | std::string instance = ctx->instance.get_host() + ":" + 714 | std::to_string(ctx->instance.get_port()); 715 | 716 | this->mutex.lock(); 717 | this->register_status[instance].heartbeating = false; 718 | this->mutex.unlock(); 719 | ((WFFacilities::WaitGroup *)task->user_data)->done(); 720 | } 721 | 722 | return; 723 | } 724 | 725 | void Manager::deregister_callback(PolarisTask *task) 726 | { 727 | struct deregister_context *ctx = (struct deregister_context *)task->user_data; 728 | std::unique_lock lock(this->mutex); 729 | 730 | auto iter = this->register_status.find(ctx->instance_name); 731 | 732 | if (iter != this->register_status.end()) 733 | { 734 | if (iter->second.heartbeating == true) 735 | { 736 | iter->second.heartbeating = false; 737 | iter->second.cond.wait(lock); 738 | } 739 | this->register_status.erase(iter); 740 | } 741 | 742 | ctx->wait_group.done(); 743 | delete ctx; 744 | 745 | return; 746 | } 747 | 748 | void Manager::heartbeat_callback(PolarisTask *task) 749 | { 750 | if (this->status == MANAGER_EXITED) 751 | return; 752 | 753 | int state = task->get_state(); 754 | int error = task->get_error(); 755 | 756 | WFTimerTask *timer_task; 757 | struct provider_context *ctx; 758 | bool ret = true; 759 | 760 | if (task->user_data && 761 | state != WFT_STATE_SUCCESS && 762 | error == POLARIS_ERR_HEARTBEAT_DISABLE) 763 | { 764 | // will continue even if the first heartbeat network failed 765 | this->set_error(state, error); 766 | ret = false; 767 | } 768 | 769 | if (ret == true) 770 | { 771 | ctx = (struct provider_context *)series_of(task)->get_context(); 772 | std::string instance = ctx->instance.get_host() + ":" + 773 | std::to_string(ctx->instance.get_port()); 774 | 775 | this->mutex.lock(); 776 | ret = this->update_heartbeat_locked(instance, task->user_data ? true : false); 777 | this->mutex.unlock(); 778 | 779 | if (ret == true) 780 | { 781 | timer_task = WFTaskFactory::create_timer_task(ctx->heartbeat_interval, 0, 782 | this->heartbeat_timer_cb); 783 | series_of(task)->push_back(timer_task); 784 | } 785 | } 786 | 787 | if (task->user_data) 788 | ((WFFacilities::WaitGroup *)task->user_data)->done(); 789 | 790 | return; 791 | } 792 | 793 | bool Manager::update_heartbeat_locked(const std::string& instance_name, 794 | bool is_user_request) 795 | { 796 | auto iter = this->register_status.find(instance_name); 797 | 798 | if (iter != this->register_status.end()) 799 | { 800 | if (is_user_request) 801 | { 802 | this->error = POLARIS_ERR_DOUBLE_OPERATION; 803 | return false; 804 | } 805 | 806 | if (!iter->second.heartbeating) // some one calling deregister() 807 | { 808 | iter->second.cond.notify_one(); 809 | return false; 810 | } 811 | } 812 | 813 | this->register_status[instance_name].heartbeating = false; 814 | return true; 815 | } 816 | 817 | void Manager::heartbeat_timer_callback(WFTimerTask *task) 818 | { 819 | struct provider_context *ctx; 820 | ctx =(struct provider_context *)series_of(task)->get_context(); 821 | std::string instance = ctx->instance.get_host() + ":" + 822 | std::to_string(ctx->instance.get_port()); 823 | 824 | if (this->status == MANAGER_EXITED) 825 | return; 826 | 827 | this->mutex.lock(); 828 | auto iter = this->register_status.find(instance); 829 | if (iter == this->register_status.end()) 830 | { 831 | this->mutex.unlock(); 832 | return; 833 | } 834 | 835 | iter->second.heartbeating = true; 836 | this->mutex.unlock(); 837 | 838 | PolarisTask *heartbeat_task; 839 | heartbeat_task = this->client.create_heartbeat_task( 840 | ctx->service_namespace.c_str(), 841 | ctx->service_name.c_str(), 842 | this->retry_max, 843 | this->heartbeat_cb); 844 | 845 | heartbeat_task->set_config(this->config); 846 | heartbeat_task->set_service_token(ctx->service_token); 847 | heartbeat_task->set_polaris_instance(ctx->instance); 848 | if (!this->platform_id.empty() && !this->platform_token.empty()) 849 | { 850 | heartbeat_task->set_platform_id(platform_id); 851 | heartbeat_task->set_platform_token(platform_token); 852 | } 853 | series_of(task)->push_back(heartbeat_task); 854 | } 855 | 856 | }; // namespace polaris 857 | 858 | -------------------------------------------------------------------------------- /src/PolarisPolicy.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "workflow/StringUtil.h" 4 | #include "PolarisPolicy.h" 5 | 6 | namespace polaris { 7 | 8 | static constexpr char const *META_LABLE_EXACT = "EXACT"; 9 | static constexpr char const *META_LABLE_REGEX = "REGEX"; 10 | 11 | static inline bool meta_lable_equal(const struct meta_label& meta, 12 | const std::string& str) 13 | { 14 | if (meta.value == str) 15 | return true; 16 | 17 | //if (meta.type == META_LABLE_REGEX) 18 | //TODO support REGEX 19 | 20 | return false; 21 | } 22 | 23 | PolarisPolicyConfig::PolarisPolicyConfig(const std::string& policy_name, 24 | const PolarisConfig& conf) : 25 | policy_name(policy_name) 26 | { 27 | this->enable_rule_base_router = false; 28 | this->enable_dst_meta_router = false; 29 | this->enable_nearby_based_router = false; 30 | this->failover_type = MetadataFailoverNone; 31 | 32 | std::vector router_chain = conf.get_service_router_chain(); 33 | for (auto router : router_chain) 34 | { 35 | if (router == "ruleBasedRouter") 36 | this->set_rule_base_router(true); 37 | else if (router == "dstMetaRouter") 38 | this->set_dst_meta_router(true); 39 | else if (router == "nearbyBasedRouter") 40 | { 41 | this->set_nearby_based_router(true, 42 | conf.get_nearby_match_level(), 43 | conf.get_nearby_max_match_level(), 44 | conf.get_nearby_unhealthy_degrade() == true ? 45 | conf.get_nearby_unhealthy_degrade_percent() : 100, 46 | conf.get_nearby_enable_recover_all(), 47 | conf.get_nearby_strict_nearby()); 48 | } 49 | } 50 | 51 | if (conf.get_api_location_region() != "unknown") 52 | this->location_region = conf.get_api_location_region(); 53 | if (conf.get_api_location_zone() != "unknown") 54 | this->location_zone = conf.get_api_location_zone(); 55 | if (conf.get_api_location_campus() != "unknown") 56 | this->location_campus = conf.get_api_location_campus(); 57 | } 58 | 59 | void PolarisPolicyConfig::set_nearby_based_router(bool enable, 60 | const std::string& match_level, 61 | const std::string& max_match_level, 62 | short percentage, 63 | bool enable_recover_all, 64 | bool strict_nearby) 65 | { 66 | this->enable_nearby_based_router = enable; 67 | 68 | if (match_level == "zone") 69 | this->nearby_match_level = NearbyMatchLevelZone; 70 | else if (match_level == "campus") 71 | this->nearby_match_level = NearbyMatchLevelCampus; 72 | else if (match_level == "region") 73 | this->nearby_match_level = NearbyMatchLevelRegion; 74 | else 75 | this->nearby_match_level = NearbyMatchLevelNone; 76 | 77 | if (max_match_level == "zone") 78 | this->nearby_max_match_level = NearbyMatchLevelZone; 79 | if (max_match_level == "campus") 80 | this->nearby_max_match_level = NearbyMatchLevelCampus; 81 | else if (max_match_level == "region") 82 | this->nearby_max_match_level = NearbyMatchLevelRegion; 83 | else 84 | this->nearby_max_match_level = NearbyMatchLevelNone; 85 | 86 | this->nearby_unhealthy_percentage = percentage; 87 | this->nearby_enable_recover_all = enable_recover_all; 88 | this->nearby_strict_nearby = strict_nearby; 89 | } 90 | 91 | PolarisInstanceParams::PolarisInstanceParams(const struct instance *inst, 92 | const AddressParams *params) : 93 | PolicyAddrParams(params), 94 | logic_set(inst->logic_set), 95 | service_namespace(inst->service_namespace), 96 | metadata(inst->metadata) 97 | { 98 | this->priority = inst->priority; 99 | this->enable_healthcheck = inst->enable_healthcheck; 100 | this->healthy = inst->healthy; 101 | this->isolate = inst->isolate; 102 | this->region = inst->region; 103 | this->zone = inst->zone; 104 | this->campus = inst->campus; 105 | 106 | this->weight = inst->weight; 107 | // params.weight will not affect here 108 | // this->weight == 0 has special meaning 109 | } 110 | 111 | PolarisPolicy::PolarisPolicy(const PolarisPolicyConfig *config) : 112 | config(*config), 113 | inbound_rwlock(PTHREAD_RWLOCK_INITIALIZER), 114 | outbound_rwlock(PTHREAD_RWLOCK_INITIALIZER) 115 | { 116 | this->total_weight = 0; 117 | this->available_weight = 0; 118 | } 119 | 120 | void PolarisPolicy::update_instances(const std::vector& instances) 121 | { 122 | std::vector addrs; 123 | EndpointAddress *addr; 124 | std::string name; 125 | 126 | AddressParams params = ADDRESS_PARAMS_DEFAULT; 127 | 128 | for (size_t i = 0; i < instances.size(); i++) 129 | { 130 | name = instances[i].host + ":" + std::to_string(instances[i].port); 131 | addr = new EndpointAddress(name, 132 | new PolarisInstanceParams(&instances[i], ¶ms)); 133 | addrs.push_back(addr); 134 | } 135 | 136 | pthread_rwlock_wrlock(&this->rwlock); 137 | this->clear_instances_locked(); 138 | 139 | for (size_t i = 0; i < addrs.size(); i++) 140 | this->add_server_locked(addrs[i]); 141 | pthread_rwlock_unlock(&this->rwlock); 142 | } 143 | 144 | void PolarisPolicy::clear_instances_locked() 145 | { 146 | for (EndpointAddress *addr : this->servers) 147 | { 148 | if (--addr->ref == 0) 149 | { 150 | this->pre_delete_server(addr); 151 | delete addr; 152 | } 153 | } 154 | 155 | this->servers.clear(); 156 | this->server_map.clear(); 157 | this->nalives = 0; 158 | this->total_weight = 0; 159 | this->available_weight = 0; 160 | } 161 | 162 | void PolarisPolicy::add_server_locked(EndpointAddress *addr) 163 | { 164 | this->server_map[addr->address].push_back(addr); 165 | this->servers.push_back(addr); 166 | this->recover_one_server(addr); 167 | 168 | PolarisInstanceParams *params = static_cast(addr->params); 169 | this->total_weight += params->get_weight(); 170 | // TODO: depends on what we need in select() 171 | } 172 | 173 | void PolarisPolicy::recover_one_server(const EndpointAddress *addr) 174 | { 175 | this->nalives++; 176 | PolarisInstanceParams *params = static_cast(addr->params); 177 | this->available_weight += params->get_weight(); 178 | } 179 | 180 | void PolarisPolicy::fuse_one_server(const EndpointAddress *addr) 181 | { 182 | this->nalives--; 183 | PolarisInstanceParams *params = static_cast(addr->params); 184 | this->available_weight -= params->get_weight(); 185 | } 186 | 187 | void PolarisPolicy::update_inbounds(const std::vector& inbounds) 188 | { 189 | std::set cleared_set; 190 | BoundRulesMap::iterator it; 191 | 192 | pthread_rwlock_wrlock(&this->inbound_rwlock); 193 | for (size_t i = 0; i < inbounds.size(); i++) 194 | { 195 | std::string src_name; 196 | 197 | if (inbounds[i].source_bounds.size()) 198 | src_name = inbounds[i].source_bounds[0].service; 199 | 200 | // clear the previous 201 | if (cleared_set.find(src_name) == cleared_set.end()) 202 | { 203 | it = this->inbound_rules.find(src_name); 204 | 205 | if (it != this->inbound_rules.end()) 206 | it->second.clear(); 207 | else 208 | this->inbound_rules[src_name]; // construct vector 209 | 210 | cleared_set.insert(src_name); 211 | } 212 | 213 | this->inbound_rules[src_name].push_back(inbounds[i]); 214 | } 215 | 216 | pthread_rwlock_unlock(&this->inbound_rwlock); 217 | } 218 | 219 | void PolarisPolicy::update_outbounds(const std::vector& outbounds) 220 | { 221 | std::set cleared_set; 222 | BoundRulesMap::iterator it; 223 | 224 | pthread_rwlock_wrlock(&this->outbound_rwlock); 225 | for (size_t i = 0; i < outbounds.size(); i++) 226 | { 227 | std::string src_name; 228 | 229 | if (outbounds[i].source_bounds.size()) 230 | src_name = outbounds[i].source_bounds[0].service; 231 | 232 | // clear the previous 233 | if (cleared_set.find(src_name) == cleared_set.end()) 234 | { 235 | it = this->outbound_rules.find(src_name); 236 | 237 | if (it != this->inbound_rules.end()) 238 | it->second.clear(); 239 | else 240 | this->outbound_rules[src_name]; // construct vector 241 | 242 | cleared_set.insert(src_name); 243 | } 244 | 245 | this->outbound_rules[src_name].push_back(outbounds[i]); 246 | } 247 | 248 | pthread_rwlock_unlock(&this->outbound_rwlock); 249 | } 250 | 251 | bool PolarisPolicy::select(const ParsedURI& uri, WFNSTracing *tracing, 252 | EndpointAddress **addr) 253 | { 254 | std::vector *dst_bounds = NULL; 255 | std::vector matched_subset; 256 | std::string caller_name; 257 | std::string caller_namespace; 258 | std::map meta; 259 | bool ret = true; 260 | 261 | this->check_breaker(); 262 | 263 | if (!this->split_fragment(uri.fragment, caller_name, caller_namespace, meta)) 264 | return false; 265 | 266 | if (meta.size()) 267 | { 268 | // will be refactored as chain mode 269 | if (this->config.enable_rule_base_router) 270 | { 271 | this->matching_bounds(caller_name, caller_namespace, meta, &dst_bounds); 272 | if (dst_bounds && dst_bounds->size()) 273 | { 274 | pthread_rwlock_rdlock(&this->rwlock); 275 | ret = this->matching_subset(dst_bounds, matched_subset); 276 | pthread_rwlock_unlock(&this->rwlock); 277 | } 278 | } 279 | else if (this->config.enable_dst_meta_router) 280 | { 281 | pthread_rwlock_rdlock(&this->rwlock); 282 | ret = this->matching_meta(meta, matched_subset); 283 | pthread_rwlock_unlock(&this->rwlock); 284 | } 285 | } 286 | 287 | if (ret) 288 | { 289 | pthread_rwlock_rdlock(&this->rwlock); 290 | if (matched_subset.size()) 291 | { 292 | EndpointAddress *one = this->get_one(matched_subset, tracing); 293 | 294 | for (size_t i = 0; i < matched_subset.size(); i++) 295 | { 296 | if (one != matched_subset[i]) 297 | { 298 | if (--matched_subset[i]->ref == 0) 299 | { 300 | this->pre_delete_server(matched_subset[i]); 301 | delete matched_subset[i]; 302 | } 303 | } 304 | } 305 | 306 | if (one) 307 | *addr = one; 308 | else 309 | ret = false; 310 | } 311 | else if (this->servers.size()) 312 | { 313 | EndpointAddress *one = this->get_one(this->servers, tracing); 314 | if (one) 315 | { 316 | *addr = one; 317 | ++(*addr)->ref; 318 | } 319 | else 320 | ret = false; 321 | } 322 | else 323 | ret = false; 324 | 325 | pthread_rwlock_unlock(&this->rwlock); 326 | } 327 | 328 | return ret; 329 | } 330 | 331 | /* 332 | * One "caller->callee" pair may has multiple in/out routing_bound. 333 | * One routing_bound guarantees to consist of one src in the vector. 334 | * Here will get the first matched src`s dst vector. 335 | * If the chosen dsts` subsets are all unhealthy, maching_bounds doesn`t care. 336 | */ 337 | void PolarisPolicy::matching_bounds( 338 | const std::string& caller_name, 339 | const std::string& caller_namespace, 340 | const std::map& meta, 341 | std::vector **dst_bounds) 342 | { 343 | std::vector *dst = NULL; 344 | 345 | pthread_rwlock_t *lock = &this->inbound_rwlock; 346 | pthread_rwlock_rdlock(lock); 347 | 348 | BoundRulesMap& rules = this->inbound_rules; 349 | BoundRulesMap::iterator iter = this->inbound_rules.find(caller_name); 350 | 351 | if (iter == this->inbound_rules.end() && 352 | this->inbound_rules.find("*") == this->inbound_rules.end()) 353 | { 354 | pthread_rwlock_unlock(lock); 355 | lock = &this->outbound_rwlock; 356 | pthread_rwlock_rdlock(lock); 357 | rules = this->outbound_rules; 358 | } 359 | 360 | iter = rules.find(caller_name); 361 | if (iter == rules.end()) 362 | iter = rules.find("*"); 363 | 364 | if (iter != rules.end()) 365 | { 366 | for (struct routing_bound& rule : iter->second) 367 | { 368 | if (this->matching_rules(caller_name, caller_namespace, 369 | meta, rule.source_bounds)) 370 | { 371 | dst = &rule.destination_bounds; 372 | break; 373 | } 374 | } 375 | 376 | if (dst) 377 | *dst_bounds = dst; 378 | } 379 | 380 | pthread_rwlock_unlock(lock); 381 | } 382 | 383 | /* 384 | * One subset vector is the instances matched one dst_bound. 385 | * Return the healthy subset whose matched dst_bound has top priority. 386 | * If multiple healthy dst_bounds have the same priority, 387 | * select one bound randomly according to their weight and return its subset. 388 | * If no instancs is matched by any dst_bounds, return false. 389 | */ 390 | bool PolarisPolicy::matching_subset( 391 | std::vector *dst_bounds, 392 | std::vector& matched_subset) 393 | { 394 | std::map> bound_map; 395 | std::map>::iterator it; 396 | std::vector> top_subsets; 397 | std::vector> cur_subsets; 398 | std::vector>& subsets = top_subsets; 399 | bool found = false; 400 | size_t i; 401 | 402 | for (i = 0; i < dst_bounds->size(); i++) 403 | bound_map[(*dst_bounds)[i].priority].push_back(&((*dst_bounds)[i])); 404 | 405 | for (it = bound_map.begin(); it != bound_map.end() && !found; it++) 406 | { 407 | if (top_subsets.size()) 408 | { 409 | cur_subsets.clear(); 410 | subsets = cur_subsets; 411 | } 412 | 413 | subsets.resize(it->second.size()); 414 | for (i = 0; i < it->second.size(); i++) 415 | { 416 | if (this->matching_instances(it->second[i], subsets[i])) 417 | found = true; 418 | } 419 | 420 | if (found == true) 421 | break; 422 | } 423 | 424 | if (found == false) 425 | { 426 | if (top_subsets.size()) 427 | { 428 | subsets = top_subsets; 429 | it = bound_map.begin(); 430 | } 431 | else 432 | return false; 433 | } 434 | 435 | if (subsets.size() == 1) 436 | i = 0; 437 | else // should move 438 | i = this->subsets_weighted_random(it->second, subsets); 439 | 440 | for (size_t j = 0; j < subsets[i].size(); j++) 441 | { 442 | ++subsets[i][j]->ref; 443 | matched_subset.push_back(subsets[i][j]); 444 | } 445 | 446 | return true; 447 | } 448 | 449 | size_t PolarisPolicy::subsets_weighted_random( 450 | const std::vector& bounds, 451 | const std::vector>& subsets) 452 | { 453 | int x, s = 0; 454 | int total_weight = 0; 455 | int available_bounds_count = 0; 456 | size_t i; 457 | 458 | for (i = 0; i < bounds.size(); i++) 459 | { 460 | if (subsets[i].size()) 461 | { 462 | total_weight += bounds[i]->weight; 463 | available_bounds_count++; 464 | } 465 | } 466 | 467 | if (total_weight == 0) 468 | total_weight = available_bounds_count; 469 | 470 | x = rand() % total_weight; 471 | 472 | for (i = 0; i < bounds.size(); i++) 473 | { 474 | if (subsets[i].size() == 0) 475 | continue; 476 | 477 | s += bounds[i]->weight; 478 | if (s > x) 479 | break; 480 | } 481 | 482 | if (i == bounds.size()) 483 | { 484 | do { 485 | i--; 486 | } while (subsets[i].size() == 0); 487 | } 488 | 489 | return i; 490 | } 491 | 492 | bool PolarisPolicy::matching_instances(struct destination_bound *dst_bounds, 493 | std::vector& subset) 494 | { 495 | // fill all servers which match all the meta in dst_bounds 496 | // no matter they are heathy or not 497 | // if no healty instances : return false; else : return true; 498 | PolarisInstanceParams *params; 499 | bool flag; 500 | 501 | for (size_t i = 0; i < this->servers.size(); i++) 502 | { 503 | params = static_cast(this->servers[i]->params); 504 | 505 | if (dst_bounds->service_namespace != params->get_namespace()) 506 | continue; 507 | 508 | const std::map& inst_meta = params->get_meta(); 509 | flag = true; 510 | 511 | for (const auto &bound_meta : dst_bounds->metadata) 512 | { 513 | const auto inst_meta_it = inst_meta.find(bound_meta.first); 514 | 515 | if (inst_meta_it == inst_meta.end() || 516 | (bound_meta.second.value != "*" && 517 | !meta_lable_equal(bound_meta.second, inst_meta_it->second))) 518 | { 519 | flag = false; 520 | break; 521 | } 522 | } 523 | 524 | if (flag == true) 525 | subset.push_back(this->servers[i]); 526 | } 527 | 528 | return true; 529 | } 530 | 531 | bool PolarisPolicy::matching_rules( 532 | const std::string& caller_name, 533 | const std::string& caller_namespace, 534 | const std::map& meta, 535 | const std::vector& src_bounds) const 536 | { 537 | const struct source_bound& src = src_bounds[0]; // make sure there`s only one src 538 | 539 | if ((caller_name != src.service && 540 | caller_name != "*" && src.service != "*") || 541 | (caller_namespace != src.service_namespace && 542 | caller_namespace != "*" && src.service_namespace != "*")) 543 | { 544 | return false; 545 | } 546 | 547 | for (const auto &m : meta) 548 | { 549 | const auto label_it = src.metadata.find(m.first); 550 | if (label_it == src.metadata.end() || 551 | (label_it->second.value != "*" && 552 | !meta_lable_equal(label_it->second, m.second))) 553 | { 554 | return false; 555 | } 556 | } 557 | 558 | return true; 559 | } 560 | 561 | bool inline PolarisPolicy::nearby_match_level(const EndpointAddress *instance, 562 | NearbyMatchLevelType level) 563 | { 564 | PolarisInstanceParams *params; 565 | params = static_cast(instance->params); 566 | 567 | switch (level) 568 | { 569 | case NearbyMatchLevelZone: 570 | if (strcasecmp(this->config.location_zone.c_str(), 571 | params->get_zone().c_str()) == 0 && 572 | strcasecmp(this->config.location_region.c_str(), 573 | params->get_region().c_str()) == 0) 574 | return true; 575 | 576 | case NearbyMatchLevelCampus: 577 | if (strcasecmp(this->config.location_campus.c_str(), 578 | params->get_campus().c_str()) == 0 && 579 | strcasecmp(this->config.location_zone.c_str(), 580 | params->get_zone().c_str()) == 0 && 581 | strcasecmp(this->config.location_region.c_str(), 582 | params->get_region().c_str()) == 0) 583 | return true; 584 | 585 | case NearbyMatchLevelRegion: 586 | if (strcasecmp(this->config.location_region.c_str(), 587 | params->get_region().c_str()) == 0) 588 | return true; 589 | 590 | default: 591 | break; 592 | } 593 | 594 | return false; 595 | } 596 | 597 | bool inline PolarisPolicy::nearby_match_degrade(size_t unhealthy, size_t total) 598 | { 599 | return this->config.nearby_max_match_level != NearbyMatchLevelNone && 600 | !this->config.nearby_strict_nearby && 601 | (total == 0 || 602 | unhealthy * 100 / total > this->config.nearby_unhealthy_percentage); 603 | } 604 | 605 | bool PolarisPolicy::nearby_router_filter( 606 | std::vector& instances) 607 | { 608 | std::vector nearby_inst; 609 | size_t unhealthy_count = 0; 610 | 611 | if (this->config.nearby_strict_nearby && 612 | this->config.location_region.empty() && 613 | this->config.location_zone.empty() && 614 | this->config.location_campus.empty()) 615 | { 616 | return false; 617 | } 618 | 619 | for (EndpointAddress *inst : instances) 620 | { 621 | if (this->nearby_match_level(inst, this->config.nearby_match_level)) 622 | { 623 | nearby_inst.push_back(inst); 624 | unhealthy_count += !this->check_server_health(inst) ? 1 : 0; 625 | } 626 | } 627 | 628 | if (this->nearby_match_degrade(unhealthy_count, nearby_inst.size())) 629 | { 630 | nearby_inst.clear(); 631 | for (EndpointAddress *inst : instances) 632 | { 633 | if (this->nearby_match_level(inst, 634 | this->config.nearby_max_match_level)) 635 | { 636 | nearby_inst.push_back(inst); 637 | } 638 | } 639 | } 640 | 641 | if (nearby_inst.size() == 0) 642 | { 643 | return this->config.nearby_enable_recover_all && 644 | !this->config.nearby_strict_nearby; 645 | } 646 | 647 | instances.assign(nearby_inst.begin(), nearby_inst.end()); 648 | return true; 649 | } 650 | 651 | EndpointAddress *PolarisPolicy::get_one( 652 | std::vector& instances, 653 | WFNSTracing *tracing) 654 | { 655 | int x, s = 0; 656 | int total_weight = 0; 657 | size_t i; 658 | PolarisInstanceParams *params; 659 | 660 | if (this->config.enable_nearby_based_router) 661 | { 662 | if (!this->nearby_router_filter(instances)) 663 | return NULL; 664 | } 665 | 666 | for (i = 0; i < instances.size(); i++) 667 | { 668 | if (instances[i]->fail_count < instances[i]->params->max_fails) 669 | { 670 | params = static_cast(instances[i]->params); 671 | total_weight += params->get_weight(); 672 | } 673 | } 674 | 675 | if (total_weight == 0) // no healthy servers in the top priority subset 676 | return instances[rand() % instances.size()]; 677 | 678 | if (total_weight > 0) 679 | x = rand() % total_weight; 680 | 681 | for (i = 0; i < instances.size(); i++) 682 | { 683 | if (this->check_server_health(instances[i]) == false) 684 | continue; 685 | 686 | params = static_cast(instances[i]->params); 687 | s += params->get_weight(); 688 | if (s > x) 689 | break; 690 | } 691 | 692 | if (i == instances.size()) 693 | i--; 694 | 695 | return instances[i]; 696 | } 697 | 698 | /* 699 | * fragment format: #k1=v1&k2=v2&caller_namespace.caller_name 700 | * 701 | * if kv pair is for meta router, add "meta" as prefix of each key: 702 | * #meta.k1=v1&meta.k2=v2&caller_namespace.caller_name 703 | */ 704 | bool PolarisPolicy::split_fragment(const char *fragment, 705 | std::string& caller_name, 706 | std::string& caller_namespace, 707 | std::map& meta) 708 | { 709 | if (fragment == NULL) 710 | return false; 711 | 712 | std::string caller_info = fragment; 713 | std::vector arr = StringUtil::split(caller_info, '&'); 714 | std::size_t pos; 715 | 716 | if (!arr.empty()) 717 | { 718 | for (const auto& ele : arr) 719 | { 720 | if (ele.empty()) 721 | continue; 722 | 723 | std::vector kv = StringUtil::split(ele, '='); 724 | 725 | if (kv.size() == 1) 726 | { 727 | caller_info = ele; 728 | continue; 729 | } 730 | 731 | if (kv[0].empty() || kv[1].empty()) 732 | return false; 733 | 734 | if (meta.count(kv[0]) > 0) 735 | continue; 736 | 737 | // If rule_base_router enable, key "meta.xxx" means "meta.xxx". 738 | // If dst_meta_router enable, key "meta.xxx" means "xxx". 739 | if (this->config.enable_dst_meta_router) 740 | { 741 | pos = kv[0].find("meta."); 742 | if (pos == std::string::npos) 743 | continue; 744 | else 745 | meta.emplace(kv[0].substr(pos + 5), std::move(kv[1])); 746 | } 747 | else 748 | meta.emplace(std::move(kv[0]), std::move(kv[1])); 749 | } 750 | } 751 | 752 | if (this->config.enable_rule_base_router) 753 | { 754 | pos = caller_info.find("."); 755 | if (pos == std::string::npos) 756 | return false; 757 | 758 | caller_namespace = caller_info.substr(0, pos); 759 | caller_name = caller_info.substr(pos + 1); 760 | } 761 | 762 | return true; 763 | } 764 | 765 | bool PolarisPolicy::check_server_health(const EndpointAddress *addr) 766 | { 767 | //i PolarisInstanceParams *params = static_cast(addr->params); 768 | 769 | // instance->healthy should have a default value. 770 | // if (params->get_healthy() == false || addr->fail_count > params->max_fails) 771 | if (addr->fail_count > addr->params->max_fails) 772 | return false; 773 | 774 | return true; 775 | } 776 | 777 | /* 778 | * Match instance by meta. 779 | * 1. if some instances are healthy, return them; 780 | * 2. else if all instances are unheathy, return them, too; 781 | * 3. else use failover strategy. 782 | */ 783 | bool PolarisPolicy::matching_meta(const std::map& meta, 784 | std::vector& subset) 785 | { 786 | PolarisInstanceParams *params; 787 | bool flag; 788 | std::vector unhealthy; 789 | 790 | for (size_t i = 0; i < this->servers.size(); i++) 791 | { 792 | params = static_cast(this->servers[i]->params); 793 | const std::map& inst_meta = params->get_meta(); 794 | flag = true; 795 | 796 | for (const auto &kv : meta) 797 | { 798 | const auto inst_meta_it = inst_meta.find(kv.first); 799 | 800 | if (inst_meta_it == inst_meta.end() || 801 | kv.second != inst_meta_it->second) 802 | { 803 | flag = false; 804 | break; 805 | } 806 | } 807 | 808 | if (flag == true) 809 | { 810 | ++this->servers[i]->ref; 811 | 812 | if (this->check_server_health(this->servers[i])) 813 | subset.push_back(this->servers[i]); 814 | else 815 | unhealthy.push_back(this->servers[i]); 816 | } 817 | } 818 | 819 | if (subset.size()) 820 | return true; 821 | 822 | if (unhealthy.size()) 823 | { 824 | subset.swap(unhealthy); 825 | return true; 826 | } 827 | 828 | switch (this->config.failover_type) 829 | { 830 | case MetadataFailoverAll: 831 | subset = this->servers; 832 | return true; 833 | case MetadataFailoverNotKey: 834 | return this->matching_meta_notkey(meta, subset); 835 | default: 836 | return false; 837 | } 838 | } 839 | 840 | // find instances which don`t contain any keys in meta 841 | bool PolarisPolicy::matching_meta_notkey(const std::map& meta, 842 | std::vector& subset) 843 | { 844 | PolarisInstanceParams *params; 845 | bool flag; 846 | std::vector unhealthy; 847 | 848 | for (size_t i = 0; i < this->servers.size(); i++) 849 | { 850 | params = static_cast(this->servers[i]->params); 851 | const std::map& inst_meta = params->get_meta(); 852 | flag = true; 853 | 854 | for (const auto &kv : meta) 855 | { 856 | if (inst_meta.find(kv.first) != inst_meta.end()) 857 | { 858 | flag = false; 859 | break; 860 | } 861 | } 862 | 863 | if (flag == true) 864 | { 865 | ++this->servers[i]->ref; 866 | if (this->check_server_health(this->servers[i])) 867 | subset.push_back(this->servers[i]); 868 | else 869 | unhealthy.push_back(this->servers[i]); 870 | } 871 | } 872 | 873 | if (subset.size()) 874 | return true; 875 | 876 | if (unhealthy.size()) 877 | { 878 | subset.swap(unhealthy); 879 | return true; 880 | } 881 | 882 | return false; 883 | } 884 | 885 | }; // namespace polaris 886 | 887 | -------------------------------------------------------------------------------- /src/PolarisTask.cc: -------------------------------------------------------------------------------- 1 | #include "PolarisTask.h" 2 | #include "PolarisClient.h" 3 | #include "json.hpp" 4 | 5 | using nlohmann::json; 6 | 7 | namespace polaris { 8 | 9 | #define REDIRECT_MAX 5 10 | #define CLUSTER_FAILED_MAX 20 11 | 12 | #define CLUSTER_STATE_DISCOVER 1 13 | #define CLUSTER_STATE_HEALTHCHECK (1 << 1) 14 | 15 | void to_json(json &j, const struct discover_request &request); 16 | void to_json(json &j, const struct register_request &request); 17 | void to_json(json &j, const struct deregister_request &request); 18 | void to_json(json &j, const struct ratelimit_request &request); 19 | void to_json(json &j, const struct circuitbreaker_request &request); 20 | void from_json(const json &j, struct cluster_result &response); 21 | void from_json(const json &j, struct discover_result &response); 22 | void from_json(const json &j, struct route_result &response); 23 | void from_json(const json &j, struct ratelimit_result &response); 24 | void from_json(const json &j, struct circuitbreaker_result &response); 25 | 26 | void PolarisTask::dispatch() { 27 | if (this->finish) { 28 | this->check_failed(); 29 | this->subtask_done(); 30 | return; 31 | } 32 | SubTask *task; 33 | this->cluster.get_mutex()->lock(); 34 | // todo: set cluster ttl for update 35 | 36 | if (this->platform_id.empty() && this->platform_token.empty()) { 37 | this->cluster.get_discover_clusters()->emplace_back(this->url); 38 | this->cluster.get_healthcheck_clusters()->emplace_back(this->url); 39 | *this->cluster.get_status() |= CLUSTER_STATE_DISCOVER; 40 | *this->cluster.get_status() |= CLUSTER_STATE_HEALTHCHECK; 41 | } 42 | 43 | if (!(*this->cluster.get_status() & CLUSTER_STATE_DISCOVER)) { 44 | if (this->protocol == P_HTTP) { 45 | task = create_discover_cluster_http_task(); 46 | } else { 47 | task = WFTaskFactory::create_empty_task(); 48 | } 49 | } 50 | else if (!(*this->cluster.get_status() & CLUSTER_STATE_HEALTHCHECK)) { 51 | if (this->protocol == P_HTTP) { 52 | task = create_healthcheck_cluster_http_task(); 53 | } else { 54 | task = WFTaskFactory::create_empty_task(); 55 | } 56 | } 57 | else { 58 | if (this->protocol == P_UNKNOWN) { 59 | task = WFTaskFactory::create_empty_task(); 60 | } else { 61 | switch (this->apitype) { 62 | case API_DISCOVER: 63 | if (this->protocol == P_HTTP) { 64 | task = create_instances_http_task(); 65 | break; 66 | } 67 | case API_REGISTER: 68 | if (this->protocol == P_HTTP) { 69 | task = create_register_http_task(); 70 | break; 71 | } 72 | case API_DEREGISTER: 73 | if (this->protocol == P_HTTP) { 74 | task = create_deregister_http_task(); 75 | break; 76 | } 77 | case API_RATELIMIT: 78 | if (this->protocol == P_HTTP) { 79 | task = create_ratelimit_http_task(); 80 | break; 81 | } 82 | case API_CIRCUITBREAKER: 83 | if (this->protocol == P_HTTP) { 84 | task = create_circuitbreaker_http_task(); 85 | break; 86 | } 87 | case API_HEARTBEAT: 88 | if (this->protocol == P_HTTP) { 89 | task = create_heartbeat_http_task(); 90 | break; 91 | } 92 | default: 93 | task = WFTaskFactory::create_empty_task(); 94 | break; 95 | } 96 | } 97 | } 98 | this->cluster.get_mutex()->unlock(); 99 | series_of(this)->push_front(task); 100 | this->subtask_done(); 101 | } 102 | 103 | SubTask *PolarisTask::done() { 104 | SeriesWork *series = series_of(this); 105 | if (this->finish) { 106 | if (this->callback) { 107 | this->callback(this); 108 | } 109 | 110 | delete this; 111 | } 112 | return series->pop(); 113 | } 114 | 115 | WFHttpTask *PolarisTask::create_healthcheck_cluster_http_task() { 116 | std::string url = this->url + 117 | "/naming/v1/instances?service=polaris.healthcheck&namespace=Polaris"; 118 | auto *task = WFTaskFactory::create_http_task(url, REDIRECT_MAX, this->retry_max, 119 | healthcheck_cluster_http_callback); 120 | task->user_data = this; 121 | 122 | protocol::HttpRequest *req = task->get_req(); 123 | req->set_method(HttpMethodGet); 124 | if (!this->platform_id.empty() && !this->platform_token.empty()) 125 | { 126 | req->add_header_pair("Platform-Id", this->platform_id.data()); 127 | req->add_header_pair("Platform-Token", this->platform_token.data()); 128 | } 129 | 130 | series_of(this)->push_front(this); 131 | return task; 132 | } 133 | 134 | WFHttpTask *PolarisTask::create_discover_cluster_http_task() { 135 | std::string url = this->url + 136 | "/naming/v1/instances?service=polaris.discover&namespace=Polaris"; 137 | auto *task = WFTaskFactory::create_http_task(url, 138 | REDIRECT_MAX, 139 | this->retry_max, 140 | discover_cluster_http_callback); 141 | task->user_data = this; 142 | 143 | protocol::HttpRequest *req = task->get_req(); 144 | req->set_method(HttpMethodGet); 145 | if (!this->platform_id.empty() && !this->platform_token.empty()) 146 | { 147 | req->add_header_pair("Platform-Id", this->platform_id.data()); 148 | req->add_header_pair("Platform-Token", this->platform_token.data()); 149 | } 150 | 151 | series_of(this)->push_front(this); 152 | return task; 153 | } 154 | 155 | WFHttpTask *PolarisTask::create_instances_http_task() { 156 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 157 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 158 | "/v1/Discover"; 159 | 160 | auto *task = WFTaskFactory::create_http_task(url, 161 | REDIRECT_MAX, 162 | this->retry_max, 163 | instances_http_callback); 164 | protocol::HttpRequest *req = task->get_req(); 165 | task->user_data = this; 166 | req->set_method(HttpMethodPost); 167 | req->add_header_pair("Content-Type", "application/json"); 168 | std::string servicekey = this->service_name + "." + 169 | this->service_namespace; 170 | std::string revision = this->cluster.get_revision_map()->count(servicekey) 171 | ? (*this->cluster.get_revision_map())[servicekey] 172 | : "0"; 173 | struct discover_request request { 174 | .type = INSTANCE, 175 | .service_name = this->service_name, 176 | .service_namespace = this->service_namespace, 177 | .revision = revision, 178 | }; 179 | std::string output = create_discover_request(request); 180 | req->append_output_body(output.c_str(), output.length()); 181 | series_of(this)->push_front(this); 182 | return task; 183 | } 184 | 185 | WFHttpTask *PolarisTask::create_route_http_task() { 186 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 187 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 188 | "/v1/Discover"; 189 | auto *task = WFTaskFactory::create_http_task(url, 190 | REDIRECT_MAX, 191 | this->retry_max, 192 | route_http_callback); 193 | task->user_data = this; 194 | protocol::HttpRequest *req = task->get_req(); 195 | req->set_method(HttpMethodPost); 196 | req->add_header_pair("Content-Type", "application/json"); 197 | // todo: use route revision instead of 0 198 | struct discover_request request { 199 | .type = ROUTING, .service_name = this->service_name, 200 | .service_namespace = this->service_namespace, .revision = "0", 201 | }; 202 | std::string output = create_discover_request(request); 203 | req->append_output_body(output.c_str(), output.length()); 204 | return task; 205 | } 206 | 207 | WFHttpTask *PolarisTask::create_register_http_task() { 208 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 209 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 210 | "/v1/RegisterInstance"; 211 | auto *task = WFTaskFactory::create_http_task(url, 212 | REDIRECT_MAX, 213 | this->retry_max, 214 | register_http_callback); 215 | protocol::HttpRequest *req = task->get_req(); 216 | task->user_data = this; 217 | req->set_method(HttpMethodPost); 218 | req->add_header_pair("Content-Type", "application/json"); 219 | struct register_request request { 220 | .service = this->service_name, 221 | .service_namespace = this->service_namespace 222 | }; 223 | request.inst = *this->polaris_instance.get_instance(); 224 | if (!this->service_token.empty()) { 225 | request.service_token = this->service_token; 226 | } 227 | std::string output = create_register_request(request); 228 | req->append_output_body(output.c_str(), output.length()); 229 | series_of(this)->push_front(this); 230 | return task; 231 | } 232 | 233 | WFHttpTask *PolarisTask::create_deregister_http_task() { 234 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 235 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 236 | "/v1/DeregisterInstance"; 237 | auto *task = WFTaskFactory::create_http_task(url, 238 | REDIRECT_MAX, 239 | this->retry_max, 240 | register_http_callback); 241 | protocol::HttpRequest *req = task->get_req(); 242 | task->user_data = this; 243 | req->set_method(HttpMethodPost); 244 | req->add_header_pair("Content-Type", "application/json"); 245 | struct deregister_request request; 246 | if (!this->polaris_instance.get_instance()->id.empty()) { 247 | request.id = this->polaris_instance.get_instance()->id; 248 | } else { 249 | request.service = this->service_name; 250 | request.service_namespace = this->service_namespace; 251 | request.host = this->polaris_instance.get_instance()->host; 252 | request.port = this->polaris_instance.get_instance()->port; 253 | } 254 | if (!this->service_token.empty()) { 255 | request.service_token = this->service_token; 256 | } 257 | std::string output = create_deregister_request(request); 258 | req->append_output_body(output.c_str(), output.length()); 259 | series_of(this)->push_front(this); 260 | return task; 261 | } 262 | 263 | WFHttpTask *PolarisTask::create_ratelimit_http_task() { 264 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 265 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 266 | "/v1/Discover"; 267 | auto *task = WFTaskFactory::create_http_task(url, 268 | REDIRECT_MAX, 269 | this->retry_max, 270 | ratelimit_http_callback); 271 | protocol::HttpRequest *req = task->get_req(); 272 | task->user_data = this; 273 | req->set_method(HttpMethodPost); 274 | req->add_header_pair("Content-Type", "application/json"); 275 | struct ratelimit_request request { 276 | .type = RATELIMIT, .service_name = this->service_name, 277 | .service_namespace = this->service_namespace, .revision = 0 278 | }; 279 | std::string output = create_ratelimit_request(request); 280 | req->append_output_body(output.c_str(), output.length()); 281 | series_of(this)->push_front(this); 282 | return task; 283 | } 284 | 285 | WFHttpTask *PolarisTask::create_circuitbreaker_http_task() { 286 | int pos = rand() % this->cluster.get_discover_clusters()->size(); 287 | std::string url = this->cluster.get_discover_clusters()->at(pos) + 288 | "/v1/Discover"; 289 | auto *task = WFTaskFactory::create_http_task(url, 290 | REDIRECT_MAX, 291 | this->retry_max, 292 | circuitbreaker_http_callback); 293 | protocol::HttpRequest *req = task->get_req(); 294 | task->user_data = this; 295 | req->set_method(HttpMethodPost); 296 | req->add_header_pair("Content-Type", "application/json"); 297 | struct circuitbreaker_request request { 298 | .type = CIRCUITBREAKER, .service_name = this->service_name, 299 | .service_namespace = this->service_namespace, .revision = 0 300 | }; 301 | std::string output = create_circuitbreaker_request(request); 302 | req->append_output_body(output.c_str(), output.length()); 303 | series_of(this)->push_front(this); 304 | return task; 305 | } 306 | 307 | // the request is the same as deregister 308 | // the response is the same as register/deregister 309 | WFHttpTask *PolarisTask::create_heartbeat_http_task() { 310 | int pos = rand() % this->cluster.get_healthcheck_clusters()->size(); 311 | std::string url = this->cluster.get_healthcheck_clusters()->at(pos) + 312 | "/v1/Heartbeat"; 313 | auto *task = WFTaskFactory::create_http_task(url, 314 | REDIRECT_MAX, 315 | this->retry_max, 316 | register_http_callback); 317 | protocol::HttpRequest *req = task->get_req(); 318 | task->user_data = this; 319 | req->set_method(HttpMethodPost); 320 | req->add_header_pair("Content-Type", "application/json"); 321 | struct deregister_request request; 322 | if (!this->polaris_instance.get_instance()->id.empty()) { 323 | request.id = this->polaris_instance.get_instance()->id; 324 | } else { 325 | request.service = this->service_name; 326 | request.service_namespace = this->service_namespace; 327 | request.host = this->polaris_instance.get_instance()->host; 328 | request.port = this->polaris_instance.get_instance()->port; 329 | } 330 | if (!this->service_token.empty()) { 331 | request.service_token = this->service_token; 332 | } 333 | std::string output = create_deregister_request(request); 334 | req->append_output_body(output.c_str(), output.length()); 335 | series_of(this)->push_front(this); 336 | return task; 337 | } 338 | 339 | void PolarisTask::healthcheck_cluster_http_callback(WFHttpTask *task) { 340 | PolarisTask *t = (PolarisTask *)task->user_data; 341 | t->cluster.get_mutex()->lock(); 342 | if (task->get_state() == WFT_STATE_SUCCESS) { 343 | protocol::HttpResponse *resp = task->get_resp(); 344 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 345 | if (!t->parse_cluster_response(body)) { 346 | t->state = POLARIS_STATE_ERROR; 347 | t->error = POLARIS_ERR_SERVER_PARSE; 348 | t->finish = true; 349 | } else { 350 | *t->cluster.get_status() |= CLUSTER_STATE_HEALTHCHECK; 351 | std::string servicekey = t->config.get_healthcheck_namespace() + 352 | "." + t->config.get_healthcheck_name(); 353 | } 354 | } else { 355 | t->state = task->get_state(); 356 | t->error = task->get_error(); 357 | t->finish = true; 358 | } 359 | t->cluster.get_mutex()->unlock(); 360 | } 361 | 362 | void PolarisTask::discover_cluster_http_callback(WFHttpTask *task) { 363 | PolarisTask *t = (PolarisTask *)task->user_data; 364 | t->cluster.get_mutex()->lock(); 365 | if (task->get_state() == WFT_STATE_SUCCESS) { 366 | protocol::HttpResponse *resp = task->get_resp(); 367 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 368 | if (!t->parse_cluster_response(body)) { 369 | t->state = POLARIS_STATE_ERROR; 370 | t->error = POLARIS_ERR_SERVER_PARSE; 371 | t->finish = true; 372 | } else { 373 | *t->cluster.get_status() |= CLUSTER_STATE_DISCOVER; 374 | std::string servicekey = t->config.get_discover_namespace() + 375 | "." + t->config.get_discover_name(); 376 | } 377 | } else { 378 | t->state = task->get_state(); 379 | t->error = task->get_error(); 380 | t->finish = true; 381 | } 382 | t->cluster.get_mutex()->unlock(); 383 | } 384 | 385 | void PolarisTask::instances_http_callback(WFHttpTask *task) { 386 | PolarisTask *t = (PolarisTask *)task->user_data; 387 | t->cluster.get_mutex()->lock(); 388 | if (task->get_state() == WFT_STATE_SUCCESS) { 389 | protocol::HttpResponse *resp = task->get_resp(); 390 | std::string revision; 391 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 392 | int error = t->parse_instances_response(body, revision); 393 | if (error) { 394 | t->state = POLARIS_STATE_ERROR; 395 | t->error = error; 396 | t->finish = true; 397 | } else { 398 | std::string servicekey = t->service_namespace + "." + 399 | t->service_name; 400 | (*t->cluster.get_revision_map())[servicekey] = revision; 401 | auto *task = t->create_route_http_task(); 402 | series_of(t)->push_front(task); 403 | } 404 | } else { 405 | t->state = task->get_state(); 406 | t->error = task->get_error(); 407 | t->finish = true; 408 | } 409 | t->cluster.get_mutex()->unlock(); 410 | } 411 | 412 | void PolarisTask::route_http_callback(WFHttpTask *task) { 413 | PolarisTask *t = (PolarisTask *)task->user_data; 414 | if (task->get_state() == WFT_STATE_SUCCESS) { 415 | protocol::HttpResponse *resp = task->get_resp(); 416 | std::string revision; //todo: unsued var revision 417 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 418 | int error = t->parse_route_response(body, revision); 419 | if (error) { 420 | t->state = POLARIS_STATE_ERROR; 421 | t->error = error; 422 | } else { 423 | t->state = task->get_state(); 424 | } 425 | } else { 426 | t->state = task->get_state(); 427 | t->error = task->get_error(); 428 | } 429 | t->finish = true; 430 | } 431 | 432 | void PolarisTask::register_http_callback(WFHttpTask *task) { 433 | PolarisTask *t = (PolarisTask *)task->user_data; 434 | if (task->get_state() == WFT_STATE_SUCCESS) { 435 | protocol::HttpResponse *resp = task->get_resp(); 436 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 437 | int error = t->parse_register_response(body); 438 | if (error) { 439 | t->state = POLARIS_STATE_ERROR; 440 | t->error = error; 441 | } else { 442 | t->state = task->get_state(); 443 | } 444 | } else { 445 | t->state = task->get_state(); 446 | t->error = task->get_error(); 447 | } 448 | 449 | t->finish = true; 450 | } 451 | 452 | void PolarisTask::ratelimit_http_callback(WFHttpTask *task) { 453 | PolarisTask *t = (PolarisTask *)task->user_data; 454 | if (task->get_state() == WFT_STATE_SUCCESS) { 455 | std::string revision; 456 | protocol::HttpResponse *resp = task->get_resp(); 457 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 458 | if (!t->parse_ratelimit_response(body, revision)) { 459 | t->state = POLARIS_STATE_ERROR; 460 | t->error = POLARIS_ERR_SERVER_PARSE; 461 | } else { 462 | t->state = task->get_state(); 463 | } 464 | } else { 465 | t->state = task->get_state(); 466 | t->error = task->get_error(); 467 | } 468 | t->finish = true; 469 | } 470 | 471 | void PolarisTask::circuitbreaker_http_callback(WFHttpTask *task) { 472 | PolarisTask *t = (PolarisTask *)task->user_data; 473 | if (task->get_state() == WFT_STATE_SUCCESS) { 474 | std::string revision; 475 | protocol::HttpResponse *resp = task->get_resp(); 476 | std::string body = protocol::HttpUtil::decode_chunked_body(resp); 477 | int error = t->parse_circuitbreaker_response(body, revision); 478 | if (error) { 479 | t->state = POLARIS_STATE_ERROR; 480 | t->error = error; 481 | } else { 482 | t->state = task->get_state(); 483 | } 484 | } else { 485 | t->state = task->get_state(); 486 | t->error = task->get_error(); 487 | } 488 | t->finish = true; 489 | } 490 | 491 | std::string PolarisTask::create_discover_request(const struct discover_request &request) { 492 | const json j = request; 493 | return j.dump(); 494 | } 495 | 496 | std::string PolarisTask::create_register_request(const struct register_request &request) { 497 | const json j = request; 498 | return j.dump(); 499 | } 500 | 501 | std::string PolarisTask::create_deregister_request(const struct deregister_request &request) { 502 | const json j = request; 503 | return j.dump(); 504 | } 505 | 506 | std::string PolarisTask::create_ratelimit_request(const struct ratelimit_request &request) { 507 | const json j = request; 508 | return j.dump(); 509 | } 510 | 511 | std::string PolarisTask::create_circuitbreaker_request( 512 | const struct circuitbreaker_request &request) { 513 | const json j = request; 514 | return j.dump(); 515 | } 516 | 517 | // we will only get one type of clusters here. 518 | bool PolarisTask::parse_cluster_response(const std::string &body) { 519 | json j = json::parse(body, nullptr, false); 520 | if (j.is_discarded()) { 521 | return false; 522 | } 523 | 524 | struct cluster_result response = j; 525 | if (response.instances.empty()) { 526 | return false; 527 | } else { 528 | if (response.code != 200001) { 529 | std::vector *cluster; 530 | if (response.instances[0].service.compare("polaris.discover") == 0) 531 | cluster = this->cluster.get_discover_clusters(); 532 | else if (response.instances[0].service.compare("polaris.healthcheck") == 0) 533 | cluster = this->cluster.get_healthcheck_clusters(); 534 | else 535 | return false; 536 | 537 | cluster->clear(); 538 | auto iter = response.instances.begin(); 539 | for (; iter != response.instances.end(); iter++) { 540 | if (strcmp((iter->protocol).c_str(), "http") == 0) { 541 | std::string url = "http://" + iter->host + ":" + std::to_string(iter->port); 542 | cluster->emplace_back(url); 543 | } 544 | } 545 | } 546 | } 547 | 548 | return true; 549 | } 550 | 551 | int PolarisTask::parse_instances_response(const std::string &body, std::string &revision) { 552 | json j = json::parse(body, nullptr, false); 553 | if (j.is_discarded()) { 554 | return POLARIS_ERR_SERVER_PARSE; 555 | } 556 | int code = j.at("code").get(); 557 | if (code != 200000 && code != 200001) { 558 | return code; 559 | } 560 | revision = j.at("service").at("revision").get(); 561 | this->discover_res = body; 562 | return 0; 563 | } 564 | 565 | int PolarisTask::parse_route_response(const std::string &body, std::string &revision) { 566 | json j = json::parse(body, nullptr, false); 567 | if (j.is_discarded()) { 568 | return POLARIS_ERR_SERVER_PARSE; 569 | } 570 | int code = j.at("code").get(); 571 | if (code != 200000 && code != 200001) { 572 | return code; 573 | } 574 | this->route_res = body; 575 | if (j.find("routing") != j.end() && j.at("routing").find("revision") != j.at("routing").end()) { 576 | revision = j.at("routing").at("revision").get(); 577 | } 578 | return 0; 579 | } 580 | 581 | int PolarisTask::parse_register_response(const std::string &body) { 582 | json j = json::parse(body, nullptr, false); 583 | if (j.is_discarded()) { 584 | return POLARIS_ERR_SERVER_PARSE; 585 | } 586 | int code = j.at("code").get(); 587 | if (code != 200000 && code != 200001) { 588 | if (code == 400201) 589 | return 0; // todo: existed resource err, should update if existed later 590 | if (code == 400141) // HeartbeatOnDisabledIns, should set as heartbeat disable code 591 | return POLARIS_ERR_HEARTBEAT_DISABLE; 592 | return code; 593 | } 594 | return 0; 595 | } 596 | 597 | int PolarisTask::parse_ratelimit_response(const std::string &body, std::string &revision) { 598 | json j = json::parse(body, nullptr, false); 599 | if (j.is_discarded()) { 600 | return POLARIS_ERR_SERVER_PARSE; 601 | } 602 | int code = j.at("code").get(); 603 | if (code != 200000 && code != 200001) { 604 | return code; 605 | } 606 | this->ratelimit_res = body; 607 | revision = j.at("rateLimit").at("revision").get(); 608 | return 0; 609 | } 610 | 611 | int PolarisTask::parse_circuitbreaker_response(const std::string &body, std::string &revision) { 612 | json j = json::parse(body, nullptr, false); 613 | if (j.is_discarded()) { 614 | return POLARIS_ERR_SERVER_PARSE; 615 | } 616 | int code = j.at("code").get(); 617 | if (code != 200000 && code != 200001) { 618 | return code; 619 | } 620 | this->circuitbreaker_res = body; 621 | revision = j.at("circuitBreaker").at("revision").get(); 622 | return 0; 623 | } 624 | 625 | bool PolarisTask::get_discover_result(struct discover_result *result) const { 626 | if (this->discover_res.empty()) return false; 627 | json j = json::parse(this->discover_res, nullptr, false); 628 | if (j.is_discarded()) { 629 | return false; 630 | } 631 | *result = j; 632 | return true; 633 | } 634 | 635 | bool PolarisTask::get_route_result(struct route_result *result) const { 636 | if (this->route_res.empty()) return false; 637 | json j = json::parse(this->route_res, nullptr, false); 638 | if (j.is_discarded()) { 639 | return false; 640 | } 641 | *result = j; 642 | return true; 643 | } 644 | 645 | bool PolarisTask::get_ratelimit_result(struct ratelimit_result *result) const { 646 | if (this->ratelimit_res.empty()) return false; 647 | json j = json::parse(this->ratelimit_res, nullptr, false); 648 | if (j.is_discarded()) { 649 | return false; 650 | } 651 | *result = j; 652 | return true; 653 | } 654 | 655 | bool PolarisTask::get_circuitbreaker_result(struct circuitbreaker_result *result) const { 656 | if (this->circuitbreaker_res.empty()) return false; 657 | json j = json::parse(this->circuitbreaker_res, nullptr, false); 658 | if (j.is_discarded()) { 659 | return false; 660 | } 661 | *result = j; 662 | return true; 663 | } 664 | 665 | void PolarisTask::check_failed() { 666 | if (this->state != WFT_STATE_SUCCESS) { 667 | this->cluster.get_mutex()->lock(); 668 | if (this->apitype == API_HEARTBEAT) { 669 | if (this->cluster.healthcheck_failed() >= CLUSTER_FAILED_MAX) { 670 | this->cluster.clear_healthcheck_failed(); 671 | *this->cluster.get_status() &= ~CLUSTER_STATE_HEALTHCHECK; 672 | } 673 | } else { 674 | if (this->cluster.discover_failed() >= CLUSTER_FAILED_MAX) { 675 | this->cluster.clear_discover_failed(); 676 | *this->cluster.get_status() &= ~CLUSTER_STATE_DISCOVER; 677 | } 678 | } 679 | this->cluster.get_mutex()->unlock(); 680 | } 681 | } 682 | 683 | }; // namespace polaris 684 | --------------------------------------------------------------------------------