使用polaris-cpp

环境准备

准备polaris后台环境

您需要先下载 Polaris并启动,详细可参考服务端安装指南

准备编译/运行环境

Polaris-cpp目前支持在linux下编译,g++ >= 4.4.6

在Ubuntu上可用如下命名安装相关依赖:

  1. sudo apt-get install autoconf automake libtool make g++

编译

依赖安装完成以后,执行如下命令编译

  1. make
  2. make package

执行make package后会在当前目录下生成一个polaris_cpp_sdk.tar.gz压缩文件。该文件的内容如下:

  1. |-- include/polaris # 头文件
  2. | |-- consumer.h provider.h limit.h ...
  3. |-- dlib # 动态库
  4. | |-- libpolaris_api.so
  5. `-- slib # 静态库
  6. |-- libpolaris_api.a libprotobuf.a

其中include/polaris/为头文件目录。业务程序使用#include "polaris/xxx.h"这种方式包含头文件。

dlib/为动态库目录。libpolaris_api.so为polaris的动态库。注:该动态库已经链接了libprotobuf.a 使用动态库,在发布应用程序时需要将该动态库一起发布,并需要确保能搜到这些动态库。

slib/为静态库目录。用户使用静态编译时需要链接该目录下libpolaris_api.a和libprotobuf.a两个静态库。

除支持make方式编译外,还支持bazel方式编译:

  1. sh bazel_build.sh # 编译polaris_api
  2. sh bazel_clean.sh # 编译清理

快速接入

引入依赖

静态库方式使用

  1. g++ -I./polaris_cpp_sdk/include main.cpp -L./polaris_cpp_sdk/slib -lpolaris_api -lprotobuf -pthread -lz -lrt -o main

动态库方式使用

  1. g++ -I./polaris_cpp_sdk/include main.cpp -L./polaris_cpp_sdk/dlib -lpolaris_api -pthread -lz -lrt -o main

配置服务端地址

在应用当前运行目录下,添加polaris.yaml文件,配置服务端地址信息

  1. global:
  2. serverConnector:
  3. addresses:
  4. - 127.0.0.1:8091

服务注册与心跳上报

  1. 创建ProviderAPI

    ProviderApi对象上的方法都是线程安全的,一个进程只需要创建一个即可 通过默认配置文件创建ProviderApi对象:

    1. // 这个方法默认加载当前目录下的`polaris.yaml`配置文件初始化Context来创建ProviderApi。
    2. // 如果该配置文件不存在,则使用默认配置;否则,加载该文件的配置项覆盖相关默认配置。
    3. polaris::ProviderApi* provider_api = polaris::ProviderApi::CreateWithDefaultFile();
    4. if (provider_api == NULL) { // 创建错误,创建失败原因可查看日志~/polaris/log/polaris.log
    5. abort();
    6. }
    7. // 使用provider_api
    8. // 不再使用后,释放provider_api
    9. delete provider_api;
  2. 执行服务注册

    服务注册接口用于向服务中注册服务实例。服务注册必须带上服务token,可以到控制台查看服务的token。 此外可以配置是否开启健康检查,对于开启了健康检查的服务实例,注册完成后必须定期心跳上报接口维持自身的健康状态

    1. std::string service_namespace = "Test";
    2. std::string service_name = "dummy";
    3. std::string service_token; // 默认无token鉴权
    4. std::string host = "127.0.0.1";
    5. int port = 9092;
    6. polaris::InstanceRegisterRequest register_req(service_namespace, service_name, service_token, host, port);
    7. // 设置开启健康检查,不设置默认为不开启
    8. register_req.SetHealthCheckFlag(true);
    9. register_req.SetHealthCheckType(polaris::kHeartbeatHealthCheck);
    10. register_req.SetTtl(5); // 心跳服务器超过3*ttl时间未收到客户端上报心跳就将实例设置为不健康
    11. // 调用服务注册接口
    12. std::string instance_id; // 调用成功会返回instance_id
    13. polaris::ReturnCode ret = provider->Register(register_req, instance_id);
    14. if (ret != polaris::kReturnOk) {
    15. abort();
    16. }
  3. 执行心跳上报

    如果在服务注册的时候开启了上报心跳,则业务需要定时调用心跳上报接口维持服务健康状态

    1. // instance_id为通过服务注册接口返回的服务实例ID
    2. polaris::InstanceHeartbeatRequest heartbeat_req(service_token, instance_id);
    3. while (true) {
    4. if (provider->Heartbeat(heartbeat_req) == kReturnOk) {
    5. sleep(5);
    6. }
    7. }
  4. 执行服务反注册

    服务退出时,可调用服务反注册接口将服务实例从服务的实例列表中删除

    1. polaris::InstanceDeregisterRequest deregister_req(service_token, instance_id);
    2. ret = provider->Deregister(deregister_req);

服务发现

  1. 创建ConsumerAPI

    业务程序在调用相关接口前必须先创建ConsumerApi对象。ConsumerApi对象是线程安全的,一个进程只需要创建一个即可。

    1. // 这个方法默认加载当前目录下的`polaris.yaml`配置文件初始化Context来创建ConsumerApi。
    2. // 如果该配置文件不存在,则使用默认配置;否则,加载该文件的配置项覆盖相关默认配置。
    3. polaris::ConsumerApi* consumer_api = polaris::ConsumerApi::CreateWithDefaultFile();
    4. if (consumer_api == NULL) { // 创建错误,创建失败原因可查看日志~/polaris/log/polaris.log
    5. abort();
    6. }
    7. // 使用consumer_api
    8. // 不再使用后,释放consumer_api
    9. delete consumer_api;
  2. 拉取所有的服务实例

    1. polaris::ServiceKey service_key = {"Test", "dummy"};
    2. polaris::GetInstancesRequest request(service_key);
    3. polaris::InstancesResponse* response = NULL;
    4. // 调用接口
    5. polaris::ReturnCode ret = consumer->GetAllInstances(request, response);
    6. if (ret == polaris::kReturnOk) {
    7. std::vector<polaris::Instance>& instances = response->GetInstances();
    8. for (std::size_t i = 0; i < instances.size(); ++i) {
    9. std::cout << instances[i].GetHost() << ":" << instances[i].GetPort() << std::endl;
    10. }
    11. delete response;
    12. } else {
    13. std::cout << "get all instances for service with error:" << polaris::ReturnCodeToMsg(ret).c_str() << std::endl;
    14. }

服务路由与负载均衡

  1. 使用场景

    dummyGrey服务下,有5个实例,3个实例部署了version 1.0的应用,2个实例部署了version 2.0的应用,需要保证只有灰度用户才能请求到version 2.0的应用。

  2. 添加不同分组的多个实例

    注册version 1.0的服务实例

    1. std::string service_namespace = "Test";
    2. std::string service_name = "dummyGrey";
    3. std::string host = "127.0.0.1";
    4. std::string service_token; // 默认无token鉴权
    5. std::string instance_id; // 调用成功会返回instance_id
    6. for (int i = 0; i < 3; i++) {
    7. int port = 12390 + i;
    8. polaris::InstanceRegisterRequest register_req(service_namespace, service_name, service_token, host, port);
    9. register_req.SetVersion("1.0");
    10. // 调用服务注册接口
    11. polaris::ReturnCode ret = provider->Register(register_req, instance_id);
    12. if (ret != polaris::kReturnOk) {
    13. abort();
    14. }
    15. }

    注册version 2.0的服务实例

    1. for (int i = 0; i < 2; i++) {
    2. request := &api.InstanceRegisterRequest{}
    3. polaris::InstanceRegisterRequest register_req(service_namespace, service_name, service_token, host, port);
    4. register_req.SetVersion("2.0");
    5. // 调用服务注册接口
    6. polaris::ReturnCode ret = provider->Register(register_req, instance_id);
    7. if (ret != polaris::kReturnOk) {
    8. abort();
    9. }
    10. }
  3. 添加路由规则

    路由规则中声明,带有灰度标签(grey=true)的请求,路由到version 2.0的实例分组,否则路由到version 1.0的实例分组,规则文本如下:

    1. [
    2. {
    3. "service":"dummyGrey",
    4. "namespace":"Test",
    5. "inbounds":[
    6. {
    7. "sources": [
    8. {
    9. "service": "*",
    10. "namespace": "*",
    11. "metadata": {
    12. "grey": {
    13. "value": "true"
    14. }
    15. }
    16. }
    17. ],
    18. "destinations": [
    19. {
    20. "service": "dummyGrey",
    21. "namespace": "Test",
    22. "metadata": {
    23. "version": {
    24. "value": "2.0"
    25. }
    26. },
    27. "priority": 0,
    28. "weight": 100,
    29. "isolate": false
    30. }
    31. ]
    32. },
    33. {
    34. "sources": [
    35. {
    36. "service": "*",
    37. "namespace": "*"
    38. }
    39. ],
    40. "destinations": [
    41. {
    42. "service": "dummyGrey",
    43. "namespace": "Test",
    44. "metadata": {
    45. "version": {
    46. "value": "1.0"
    47. }
    48. },
    49. "priority": 0,
    50. "weight": 100,
    51. "isolate": false
    52. }
    53. ]
    54. }
    55. ],
    56. "outbounds":[]
    57. }
    58. ]

    将规则文本保存为data.json文件,通过接口写入到Polaris服务端

    1. curl -XPOST -H'Content-Type:application/json' -d @data.json 'http://127.0.0.1:8090/naming/v1/routings'
  4. 拉取经过路由及负载均衡后的单个实例

    1. polaris::ServiceKey service_key = {"Test", "dummy"};
    2. polaris::GetOneInstanceRequest request(service_key);
    3. polaris::ServiceInfo service_info;
    4. service_info.metadata_["grey"] = true;
    5. request.SetSourceService(service_info);
    6. polaris::Instance instance;
    7. polaris::ReturnCode ret = consumer->GetOneInstance(request, instance);
    8. if (ret != polaris::kReturnOk) {
    9. std::cout << "get one instance with error: " << polaris::ReturnCodeToMsg(ret) << std::endl;
    10. abort();
    11. }
    12. std::cout << instance.GetHost() << ":" << instance.GetPort() << std::endl;

故障节点熔断

Polaris支持在主调方侧感知到被调实例出现异常,并且及时将其熔断剔除的能力,以及当实例异常解除后,通过主动探测或者超时放量等机制将其及时恢复。

  1. 添加2个服务实例

    1. //add 2 instances, one is 127.0.0.1:10010, second is 127.0.0.1:10011
    2. std::string service_namespace = "Test";
    3. std::string service_name = "dummy";
    4. std::string host = "127.0.0.1";
    5. std::string service_token; // 默认无token鉴权
    6. std::string instance_id; // 调用成功会返回instance_id
    7. for (int i = 0; i < 3; i++) {
    8. int port = 10010 + i;
    9. polaris::InstanceRegisterRequest register_req(service_namespace, service_name, service_token, host, port);
    10. // 调用服务注册接口
    11. polaris::ReturnCode ret = provider->Register(register_req, instance_id);
    12. if (ret != polaris::kReturnOk) {
    13. abort();
    14. }
    15. }
  2. 针对其中一个实例连续上报10次失败(模拟业务调用10次失败)

    1. //report 10 continuous failure
    2. for (int i = 0; i < 10; i++) {
    3. polaris::ServiceCallResult result;
    4. result.SetServiceNamespace(service_namespace);
    5. result.SetServiceName(service_name);
    6. result.SetInstanceHostAndPort("127.0.0.1", 10010)
    7. result.SetDelay(1000); // 调用延迟
    8. result.SetRetStatus(polaris::kCallRetError); // 上报错误
    9. consumer->UpdateServiceCallResult(result);
    10. }
  3. 实例被熔断,通过GetOneInstance无法再获取该实例(已经被剔除)

    1. polaris::ServiceKey service_key = {"Test", "dummy"};
    2. polaris::GetOneInstanceRequest request(service_key);
    3. polaris::Instance instance;
    4. for (int i = 0; i < 10; i++) {
    5. polaris::ReturnCode ret = consumer->GetOneInstance(request, instance);
    6. if (ret != polaris::kReturnOk) {
    7. std::cout << "get one instance with error: " << polaris::ReturnCodeToMsg(ret) << std::endl;
    8. abort();
    9. }
    10. //instance port won't be 10010, it's been kick off
    11. std::cout << instance.GetHost() << ":" << instance.GetPort() << std::endl;
    12. }

服务限流

  1. 使用场景

    dummyLimit服务有2个接口,接口/path1最大QPS为100,接口/path2最大QPS为300,规则文本如下:

    1. [
    2. {
    3. "service":"dummyLimit",
    4. "namespace":"Test",
    5. "resource":"QPS",
    6. "type":"LOCAL",
    7. "labels":{
    8. "method":{
    9. "type": "EXACT",
    10. "value":"/path1"
    11. }
    12. },
    13. "amounts":[
    14. {
    15. "maxAmount": 100,
    16. "validDuration": "1s"
    17. }
    18. ]
    19. },
    20. {
    21. "service":"dummyLimit",
    22. "namespace":"Test",
    23. "resource":"QPS",
    24. "type":"LOCAL",
    25. "labels":{
    26. "method":{
    27. "type": "EXACT",
    28. "value":"/path2"
    29. }
    30. },
    31. "amounts":[
    32. {
    33. "maxAmount": 300,
    34. "validDuration": "1s"
    35. }
    36. ]
    37. }
    38. ]

    将规则文本保存为data.json文件,通过接口写入到Polaris服务端

    1. curl -XPOST -H'Content-Type:application/json' -d @data.json 'http://127.0.0.1:8090/naming/v1/ratelimits'
  2. 创建LimitAPI

    LimitAPI的所有方法都是线程安全的,所以一个进程创建一个LimitAPI来使用就足够了

    1. // 使用运行目录下的polaris.yaml配置创建Limit API对象
    2. polaris::LimitApi* limit_api = polaris::LimitApi::CreateWithDefaultFile();
    3. if (limit_api == NULL) {
    4. std::cout << "create limit api failed" << std::endl;
    5. abort();
    6. }
    7. // 使用limit_api
    8. // 不再使用后,释放limit_api
    9. delete limit_api;

    在运行目录下创建polaris.yaml文件,写入如下内容:

    1. rateLimiter:
    2. mode: local
  3. 针对/path1获取配额

    每次收到访问/path1的请求时,需要先获取配额,以判断本次是否限流

    1. polaris::QuotaRequest quota_request; // 限流请求
    2. quota_request.SetServiceNamespace("Test"); // 设置限流规则对应服务的命名空间
    3. quota_request.SetServiceName("dummyLimit"); // 设置限流规则对应的服务名
    4. std::map<std::string, std::string> labels;
    5. labels["method"] = "/path1";
    6. quota_request.SetLabels(labels); // 设置label用于匹配限流规则
    7. polaris::ReturnCode ret;
    8. polaris::QuotaResultCode result;
    9. if ((ret = limit_api->GetQuota(quota_request, result)) != polaris::kReturnOk) {
    10. std::cout << "get quota for service with error:" << polaris::ReturnCodeToMsg(ret).c_str() << std::endl;
    11. abort();
    12. }
    13. if (result == polaris::kQuotaResultOk) {
    14. //quota acquired, now can continue the procedure process
    15. std::count << "quota result ok" << std::endl;
    16. } else {
    17. //quota limited, we should block the user request
    18. std::count << "quota result fail" << std::endl;
    19. }
  4. 针对/path2获取配额

    1. polaris::QuotaRequest quota_request; // 限流请求
    2. quota_request.SetServiceNamespace("Test"); // 设置限流规则对应服务的命名空间
    3. quota_request.SetServiceName("dummyLimit"); // 设置限流规则对应的服务名
    4. std::map<std::string, std::string> labels;
    5. labels["method"] = "/path2";
    6. quota_request.SetLabels(labels); // 设置label用于匹配限流规则
    7. polaris::ReturnCode ret;
    8. polaris::QuotaResultCode result;
    9. if ((ret = limit_api->GetQuota(quota_request, result)) != polaris::kReturnOk) {
    10. std::cout << "get quota for service with error:" << polaris::ReturnCodeToMsg(ret).c_str() << std::endl;
    11. abort();
    12. }
    13. if (result == polaris::kQuotaResultOk) {
    14. //quota acquired, now can continue the procedure process
    15. std::count << "quota result ok" << std::endl;
    16. } else {
    17. //quota limited, we should block the user request
    18. std::count << "quota result fail" << std::endl;
    19. }

相关链接

Polaris使用polaris-cpp - 图1 (opens new window)

Polaris CPP使用polaris-cpp - 图2 (opens new window)