使用polaris-go

环境准备

准备polaris后台环境

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

准备编译环境

Polaris-go是以源码的方式提供集成,需要配置go mod环境进行依赖获取,并且需要确保是在以下版本环境中进行编译使用:

  1. 64 bit操作系统,支持Linux/Unix/Windows,推荐选用 Linux/Unix
  2. golang SDK 64bit 1.12+,下载链接:https://golang.org/dl/

快速接入

引入依赖

可以在polaris-go的release note上获取到Polaris的所有版本以及相关介绍。推荐使用最新的稳定版本。 在应用go.mod文件中,引入polaris.go依赖。

  1. github.com/polarismesh/polaris-go $version

配置服务端地址

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

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

服务注册与心跳上报

  1. 创建ProviderAPI ProviderAPI的所有方法都是线程安全的,所以一个进程创建一个ProviderAPI来使用就足够了,最后进程退出前要调用一下Destroy()方法

    1. provider, err := api.NewProviderAPI()
    2. if nil != err {
    3. log.Fatal(err)
    4. }
    5. //before process exits
    6. provider.Destroy()
  2. 执行服务注册

    1. request := &api.InstanceRegisterRequest{}
    2. request.Namespace = "Test"
    3. request.Service = "dummy"
    4. request.Host = "127.0.0.1"
    5. request.Port = 12380
    6. request.SetTTL(2) //set the instance ttl, server will set instance unhealthy when not receiving heartbeat after 2*ttl
    7. resp, err := provider.Register(request)
    8. if nil != err {
    9. log.Fatal(err)
    10. }
  3. 执行心跳上报

    1. hbRequest := &api.InstanceHeartbeatRequest{}
    2. hbRequest.Namespace = "Test"
    3. hbRequest.Service = "dummy"
    4. hbRequest.Host = "127.0.0.1"
    5. hbRequest.Port = 12380
    6. err = provider.Heartbeat(hbRequest)
  4. 执行服务反注册

    1. request := &api.InstanceDeRegisterRequest{}
    2. request.Namespace = "Test"
    3. request.Service = "dummy"
    4. request.Host = "127.0.0.1"
    5. request.Port = 12380
    6. err = provider.Deregister(request)

服务发现

  1. 创建ConsumerAPI ConsumerAPI的所有方法都是线程安全的,所以一个进程创建一个ConsumerAPI来使用就足够了,最后进程退出前要调用一下Destroy()方法

    1. consumer, err := api.NewConsumerAPI()
    2. if nil != err {
    3. log.Fatal(err)
    4. }
    5. //before process exits
    6. consumer.Destroy()
  2. 拉取所有的服务实例

    1. request := &api.GetAllInstancesRequest{}
    2. request.Namespace = "Test"
    3. request.Service = "dummy"
    4. Resp, err := consumer.GetAllInstances(request)
    5. for i, instance := range getAllInstResp.Instances {
    6. log.Printf("instance %d is %s:%d\n", i, instance.GetHost(), instance.GetPort())
    7. }

服务路由与负载均衡

  1. 使用场景

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

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

    注册version 1.0的服务实例

    1. for i := 0; i < 3; i++ {
    2. request := &api.InstanceRegisterRequest{}
    3. request.Namespace = "Test"
    4. request.Service = "dummyGrey"
    5. request.Host = "127.0.0.1"
    6. request.Port = 12390 + i
    7. request.SetVersion("1.0")
    8. resp, err := provider.Register(request)
    9. if nil != err {
    10. log.Fatal(err)
    11. }
    12. }

    注册version 2.0的服务实例

    1. for i := 0; i < 2; i++ {
    2. request := &api.InstanceRegisterRequest{}
    3. request.Namespace = "Test"
    4. request.Service = "dummyGrey"
    5. request.Host = "127.0.0.1"
    6. request.Port = 12370 + i
    7. request.SetVersion("2.0")
    8. resp, err := provider.Register(request)
    9. if nil != err {
    10. log.Fatal(err)
    11. }
    12. }
  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. //set the grey metadata
    2. svcInfo := &model.ServiceInfo{}
    3. svcInfo.Metadata = map[string]string{"grey": "true"}
    4. request := &api.GetOneInstanceRequest{}
    5. request.Namespace = "Test"
    6. request.Service = "dummyGrey"
    7. request.SourceService = svcInfo
    8. resp, err := consumer.GetOneInstance(request)
    9. if nil != err {
    10. log.Fatal(err)
    11. }
    12. log.Printf("instance after loadbalance is %s:%d\n", resp.Instances[0].GetHost(), resp.Instances[0].GetPort())

故障节点熔断

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

  1. 添加2个服务实例

    1. //add 2 instances, one is 127.0.0.1:10010, second is 127.0.0.1:10011
    2. for i := 0; i < 2; i++ {
    3. request := &api.InstanceRegisterRequest{}
    4. request.Namespace = "Test"
    5. request.Service = "dummy"
    6. request.Host = "127.0.0.1"
    7. request.Port = 10010 + i
    8. resp, err := provider.Register(request)
    9. if nil != err {
    10. log.Fatal(err)
    11. }
    12. }
  2. 针对其中一个实例连续上报10次失败(模拟业务调用10次失败)

    1. request := &api.GetAllInstancesRequest{}
    2. request.Namespace = "Test"
    3. request.Service = "dummy"
    4. Resp, err := consumer.GetAllInstances(request)
    5. var targetInstance model.Instance
    6. for _, instance := range getAllInstResp.Instances {
    7. if instance.GetPort() == 10010 {
    8. targetInstance = instance
    9. break
    10. }
    11. }
    12. //report 10 continuous failure
    13. for i := 0; i < 10; i++ {
    14. svcCallResult := &api.ServiceCallResult{}
    15. svcCallResult.SetCalledInstance(targetInstance)
    16. svcCallResult.SetRetStatus(api.RetFail)
    17. //return code for procedure call
    18. svcCallResult.SetRetCode(500)
    19. // delay interval for procedure call
    20. svcCallResult.SetDelay(30 * time.Millisecond)
    21. consumer.UpdateServiceCallResult(svcCallResult)
    22. }
  3. 实例被熔断,通过GetOneInstance无法再获取该实例(已经被剔除)

    1. for i := 0; i < 10; i++ {
    2. request := &api.GetOneInstanceRequest{}
    3. request.Namespace = "Test"
    4. request.Service = "dummy"
    5. resp, err := consumer.GetOneInstance(request)
    6. if nil != err {
    7. log.Fatal(err)
    8. }
    9. instance := resp.Instances[0]
    10. //instance port won't be 10010, it's been kick off
    11. log.Printf("instance port is %d\n", instance.GetPort())
    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. 创建RateLimitAPI

    RateLimitAPI的所有方法都是线程安全的,所以一个进程创建一个RateLimitAPI来使用就足够了,最后进程退出前要调用一下Destroy()方法

    1. limitAPI, err := api.NewLimitAPI()
    2. if nil != err {
    3. log.Fatal(err)
    4. }
    5. //before process exits
    6. limitAPI.Destroy()
  3. 针对/path1获取配额

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

    1. //创建访问限流请求
    2. quotaReq := api.NewQuotaRequest()
    3. //设置命名空间
    4. quotaReq.SetNamespace("Test")
    5. //设置服务名
    6. quotaReq.SetService("dummyLimit")
    7. //设置标签值
    8. quotaReq.SetLabels(map[string]string{"method", "/path1"})
    9. //调用配额获取接口
    10. future, err := limitAPI.GetQuota(quotaReq)
    11. if nil != err {
    12. log.Fatal(err)
    13. }
    14. resp := future.Get()
    15. if api.QuotaResultOk == resp.Code {
    16. //quota acquired, now can continue the procedure process
    17. log.Printf("quota result ok")
    18. } else {
    19. //quota limited, we should block the user request
    20. log.Printf("quota result fail, info is %s", resp.Info)
    21. }
  4. 针对/path2获取配额

    1. //创建访问限流请求
    2. quotaReq := api.NewQuotaRequest()
    3. //设置命名空间
    4. quotaReq.SetNamespace("Test")
    5. //设置服务名
    6. quotaReq.SetService("dummyLimit")
    7. //设置标签值
    8. quotaReq.SetLabels(map[string]string{"method", "/path2"})
    9. //调用配额获取接口
    10. future, err := limitAPI.GetQuota(quotaReq)
    11. if nil != err {
    12. log.Fatal(err)
    13. }
    14. resp := future.Get()
    15. if api.QuotaResultOk == resp.Code {
    16. //quota acquired, now can continue the procedure process
    17. log.Printf("quota result ok")
    18. } else {
    19. //quota limited, we should block the user request
    20. log.Printf("quota result fail, info is %s", resp.Info)
    21. }

相关链接

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

Polaris GO使用polaris-go - 图2 (opens new window)