Go 应用开发
- 1: 使用 Go SDK
- 1.1: 注册发现
- 1.2: 动态路由
- 1.3: 负载均衡
- 1.4: 熔断降级
- 1.5: 访问限流
- 1.6: 缓存与高可用
- 1.7: 网络连接
- 1.8: 配置管理
- 1.9: 可观测性
- 1.10: 二次寻址
- 2: 使用 dubbogo
- 3: 使用 gRPC-Go
1 - 使用 Go SDK
1.1 - 注册发现
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
服务注册
SDK实例构建
当初始化好 polaris.yaml 文件之后,你可以直接使用在 package github.com/polarismesh/polaris-go 下的 NewProviderAPI 方法进行构造一个 ProviderAPI SDK 实例
import (
...
"github.com/polarismesh/polaris-go"
)
func main() {
provider, err := polaris.NewProviderAPI()
}
注册请求体
// InstanceRegisterRequest 注册服务请求
type InstanceRegisterRequest struct {
// 必选,服务名
Service string
// 必选,命名空间
Namespace string
// 必选,服务监听host,支持IPv6地址
Host string
// 必选,服务实例监听port
Port int
// 可选,资源访问Token,即用户/用户组访问凭据,仅当服务端开启客户端鉴权时才需配置
ServiceToken string
// 以下字段可选,默认nil表示客户端不配置,使用服务端配置
// 服务协议
Protocol *string
// 服务权重,默认100,范围0-10000
Weight *int
// 实例提供服务版本号
Version *string
// 用户自定义metadata信息
Metadata map[string]string
// 该服务实例是否健康,默认健康
Healthy *bool
// 该服务实例是否隔离,默认不隔离
Isolate *bool
// 设置心跳健康检查ttl,单位为s,不填默认为5s,TTL的取值范围为 (0s, 60s]
// 开启了心跳健康检查,客户端必须以TTL间隔上报心跳
// 健康检查服务器3个TTL未受到心跳则将实例置为不健康
TTL *int
// Location 当前注册实例的地理位置信息,主要用于就近路由
Location *Location
// 可选,单次查询超时时间,默认直接获取全局的超时配置
// 用户总最大超时时间为(1+RetryCount) * Timeout
Timeout *time.Duration
// 可选,重试次数,默认直接获取全局的超时配置
RetryCount *int
}
发起注册请求
你在初始化完 InstanceRegisterRequest 结构体后,只需要调用 ProviderAPI.RegisterInstance 方法即可完成实例注册,并且 RegisterInstance 方法内部会自动维护实例的心跳上报。
resp, err := provider.RegisterInstance(registerRequest)
服务发现
SDK实例构建
consumer, err := polaris.NewConsumerAPI()
发现服务实例
GetAllInstances
直接返回目标服务下的所有实例,包括不健康、隔离、权重为0、被熔断的实例,也会在返回的实例列表中。
// GetAllInstancesRequest 获取所有实例的请求
type GetAllInstancesRequest struct {
// 必选,服务名
Service string
// 必选,命名空间
Namespace string
// 可选,单次查询超时时间,默认直接获取全局的超时配置
// 用户总最大超时时间为(1+RetryCount) * Timeout
Timeout *time.Duration
// 可选,重试次数,默认直接获取全局的超时配置
RetryCount *int
}
// 调用该方法执行请求
consumer.GetAllInstances()
GetInstances
每次获取一批可用服务提供者实例,该方法会执行路由流程。
该方法默认会过滤掉不健康、隔离、权重为0、被熔断的实例。
执行路由流程的条件
- 配置了 GetInstancesRequest.SourceService.Metadata 属性,会触发自定义路由流程
- 设置了 GetInstancesRequest.Metadata 属性,会触发元数据路由流程
// GetInstancesRequest 批量服务实例查询请求
type GetInstancesRequest struct {
// 必选,服务名
Service string
// 必选,命名空间
Namespace string
// 可选,元数据信息,仅用于dstMetadata路由插件的过滤
Metadata map[string]string
// 主调方服务信息,只用于路由规则匹配
SourceService *ServiceInfo
// 可选,是否跳过服务路由筛选,默认false
SkipRouteFilter bool
// 可选,单次查询超时时间,默认直接获取全局的超时配置
// 用户总最大超时时间为(1+RetryCount) * Timeout
Timeout *time.Duration
// 可选,重试次数,默认直接获取全局的超时配置
RetryCount *int
}
// 调用该方法执行请求
consumer.GetInstances()
GetOneInstances
每次仅获取一个可用服务提供者实例,该方法会依次执行路由、负载均衡流程。
该方法默认会过滤掉不健康、隔离、权重为0、被熔断的实例。
执行路由流程的条件
- 配置了 GetOneInstanceRequest.SourceService.Metadata 属性,会触发自定义路由流程
- 设置了 GetOneInstanceRequest.Metadata 属性,会触发元数据路由流程
// GetOneInstanceRequest 单个服务实例查询请求
type GetOneInstanceRequest struct {
// 必选,服务名
Service string
// 必选,命名空间
Namespace string
// 可选,元数据信息,仅用于dstMetadata路由插件的过滤
Metadata map[string]string
// 是否开启元数据匹配不到时启用自定义匹配规则,仅用于dstMetadata路由插件
EnableFailOverDefaultMeta bool
// 自定义匹配规则,仅当EnableFailOverDefaultMeta为true时生效
FailOverDefaultMeta FailOverDefaultMetaConfig
// 用户计算hash值的key
HashKey []byte
// 主调方服务信息
SourceService *ServiceInfo
// 可选,单次查询超时时间,默认直接获取全局的超时配置
// 用户总最大超时时间为(1+RetryCount) * Timeout
Timeout *time.Duration
// 可选,重试次数,默认直接获取全局的超时配置
RetryCount *int
// 可选,备份节点数
// 对于一致性hash等有状态的负载均衡方式
ReplicateCount int
// 可选,负载均衡算法
LbPolicy string
}
// 调用该方法执行请求
consumer.GetOneInstance()
如何基于 polaris-go 客户端完成一个服务发现的程序
1.2 - 动态路由
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
SDK实例构建
router, err := polaris.NewRouterAPI()
路由请求
// ProcessRoutersRequest 执行路由请求结构体
type ProcessRoutersRequest struct {
// 可选参数,设置本次路由请求期望执行的路由插件
// 当前支持的路由插件如下
// - 自定义路由:ruleBasedRouter
// - 就近路由:nearbyBasedRouter
// - 元数据路由:dstMetaRouter
Routers []string
// 可选参数,主调服务信息,你可以通过 ServiceInfo.Metadata 设置本次请求的流量标签信息
SourceService ServiceInfo
// 必选参数,待执行服务路由的实例列表
// 1. InstancesResponse, returned from ConsumerAPI.GetAllInstances.
// 2. DefaultServiceInstances, for user to construct manually.
DstInstances ServiceInstances
// 可选参数,对应路由规则中的方法($method)标签
Method string
// 可选,单次查询超时时间,默认直接获取全局的超时配置
// 用户总最大超时时间为(1+RetryCount) * Timeout
Timeout *time.Duration
// 可选,重试次数,默认直接获取全局的超时配置
RetryCount *int
}
如果当前 ProcessRoutersRequest 还不支持 AddArgument 方法,同时服务端版本 >= 1.12.0,SourceService.Metadata 对应的 key 名称如下:
- 路径: $path
- 方法: $method
- 请求头: $header.{标签键}
- 请求参数: $query.{标签键}
- 请求COOKIE: $cookie.{标签键}
- 主调IP: $caller_ip
- 自定义: {标签键}
执行服务路由
你在根据本次调用的上下文信息,初始化完成 ProcessRoutersRequest 结构体之后,只需要调用 RouterAPI.ProcessRouters 执行服务路由, 并从响应 model.InstancesResponse 获取符合本次路由条件的实例列表。
resp, err := router.ProcessRouters(routerRequest)
instances := resp.GetInstances()
如何基于 polaris-go 客户端完成一个动态路由的程序
1.3 - 负载均衡
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
SDK实例构建
router, err := polaris.NewRouterAPI()
负载均衡请求
// ProcessRoutersRequest 执行负载均衡请求结构体
type ProcessLoadBalanceRequest struct {
// 必选参数,待执行负载均衡的实例列表
// 1. InstancesResponse, returned from ConsumerAPI.GetAllInstances.
// 2. DefaultServiceInstances, for user to construct manually.
DstInstances ServiceInstances
// 可选参数,负载均衡策略
// 当前支持的负载均衡策略如下
// - 权重随机: weightedRandom
// - 一致性hash环: ringHash
// - maglev hash: maglev
// - 普通hash: hash
LbPolicy string
// 可选参数,对于有状态的负载均衡方式,用户可以设置用于 hash 计算的 Key
HashKey []byte
}
执行负载均衡
你在你在使用 ConsumerAPI.getAllInstances 或者 ConsumerAPI.getInstances 获取到服务实例列表后,完成 ProcessLoadBalanceRequest 初始化,只需要调用 RouterAPI.ProcessLoadBalance 执行服务路由即可
resp, err := router.ProcessLoadBalance(loadbalanceRequest)
instance := resp.GetInstance()
如何基于 polaris-go 客户端完成一个负载均衡的程序
1.4 - 熔断降级
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
SDK实例构建
当初始化好 polaris.yaml 文件之后,你可以直接使用在 package github.com/polarismesh/polaris-go 下的 NewCircuitBreakerAPI 方法进行构造一个 CircuitBreakerAPI SDK 实例
import (
...
"github.com/polarismesh/polaris-go"
)
func main() {
circuitbreakerAPI, err := polaris.NewCircuitBreakerAPI()
}
熔断整个服务
配置熔断规则
配置服务熔断规则,针对 default 命名空间下所有的服务,对于时延大于 500 毫秒,或者返回码为 500 的请求,标识为错误请求,一旦一分钟内错误率30%及以上或连续错误数在5个以上,则对服务进行熔断。
使用SDK进行熔断判断
方法说明
北极星 Go SDK 提供以下熔断相关的方法,所有的方法都在CircuitBreakAPI
接口中提供。
- Check: 检查资源是否可被调用,并对资源获取调用申请。对于半开的资源,如果半开的调用配额申请成功,返回true,否则返回false。
- Report: 该方法供用户在资源调用完成后,上报调用的结果,包括返回码、时延等信息,供熔断逻辑判断。
- MakeFunctionDecorator: 创建一个函数调用装饰器
model.DecoratorFunction
,装饰器可以对 Go 的函数接口进行装饰。具体的定义如下
// @param ctx: 当前调用上下文信息
// @param args: 方法入参
// @return interface{}: 用户方法实际执行的返回结果
// @return *CallAborted: 如果方法调用、服务调用被熔断, 则会返回 CallAborted 结构题指针
// @return error: 返回用户方法调用的 error 或者内部熔断执行逻辑的内部错误
type DecoratorFunction func(ctx context.Context, args interface{}) (interface{}, *CallAborted, error)
使用示例
// 创建CircuitBreakAPI实例
circuitbreakerAPI, err := polaris.NewCircuitBreakerAPI()
dealF := circuitbreakerAPI.MakeFunctionDecorator(func(ctx context.Context, args interface{}) (interface{}, error) {
// 用户业务逻辑函数
}, &api.RequestContext{
RequestContext: model.RequestContext{
Callee: &model.ServiceKey{
Namespace: "被调服务所在命名空间",
Service: "被调服务名称",
},
Caller: &model.ServiceKey{
Namespace: "主调服务所在命名空间",
Service: "主调服务名称",
},
},
})
ret, abort, err := dealF(context.Background(), endpoint)
样例地址
熔断单个接口
配置熔断规则
配置接口熔断规则,针对 default 命名空间所有服务的 /echo 接口,对于时延大于500毫秒,或者返回码为 500 的请求,标识为错误请求,一旦一分钟内错误率30%及以上或连续错误数在5个以上,则对接口进行熔断。
使用SDK进行熔断判断
熔断所使用的SDK接口及方法与服务级熔断相同,这里不再重复介绍。
使用示例
circuitbreakerAPI, err := polaris.NewCircuitBreakerAPI()
dealF := circuitbreakerAPI.MakeFunctionDecorator(func(ctx context.Context, args interface{}) (interface{}, error) {
resp, err := http.Get(fmt.Sprintf("http://%+v/echo", args))
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
data, _ := ioutil.ReadAll(resp.Body)
return string(data), nil
}, &api.RequestContext{
RequestContext: model.RequestContext{
Callee: &model.ServiceKey{
Namespace: "被调服务所在命名空间",
Service: "被调服务名称",
},
Caller: &model.ServiceKey{
Namespace: "主调服务所在命名空间",
Service: "主调服务名称",
},
Method: "接口名称",
},
})
ret, abort, err := dealF(context.Background(), endpoint)
样例地址
熔断单个实例
配置熔断规则
配置实例熔断规则,针对default命名空间下所有的服务实例,对于时延大于500毫秒,或者返回码为500的请求,标识为错误请求,每个实例的错误率是单独统计的,一旦一分钟内错误率30%及以上或连续错误数在5个以上,则对被调实例(IP:PORT)进行熔断。
使用SDK进行熔断判断
当实例被熔断时,该实例会暂时不接收请求,原本路由到该实例的请求会路由到其他实例。这个过程在服务路由过程中自动完成,用户无需进行额外的熔断状态判断等操作。
执行服务路由
// model.ResourceStat 中 RetStatus 字段的取值
// RetSuccess 调用成功
RetSuccess RetStatus = "success"
// RetFail 调用失败
RetFail RetStatus = "fail"
// RetTimeout 调用超时
RetTimeout RetStatus = "timeout"
// RetFlowControl 限流
RetFlowControl RetStatus = "flow_control"
// RetReject 被熔断
RetReject RetStatus = "reject"
// RetUnknown
RetUnknown RetStatus = "unknown"
circuitbreakerAPI, err := polaris.NewCircuitBreakerAPI()
// 构造 model.InstanceResource 对象
insRes, _ := model.NewInstanceResource(&model.ServiceKey{
Namespace: "被调服务所在命名空间",
Service: "被调服务名称",
}, &model.ServiceKey{
Namespace: "主调服务所在命名空间",
Service: "主调服务名称",
}, "协议信息, 比如 http/grpc/dubbo/tcp 等等", "被调实例的 IP", {被调实例端口信息})
// 上报每次的调用结果
circuitbreakerAPI.Report(&model.ResourceStat{
Delay: time.Since(start),
RetStatus: model.RetFail, //
RetCode: "响应码, string 类型",
Resource: insRes,
})
// 获取一个服务实例进行调用
getOneRequest := &polaris.GetOneInstanceRequest{}
getOneRequest.Namespace = namespace
getOneRequest.Service = service
getOneRequest.IncludeCircuitBreakInstances = true
oneInstResp, err := svr.consumer.GetOneInstance(getOneRequest)
样例地址
1.5 - 访问限流
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
SDK实例构建
当初始化好 polaris.yaml 文件之后,你可以直接使用在 package github.com/polarismesh/polaris-go 下的 NewLimitAPI 方法进行构造一个 LimitAPI SDK 实例
import (
...
"github.com/polarismesh/polaris-go"
)
func main() {
limiter, err := polaris.NewLimitAPI()
}
请求配额
type QuotaRequest interface {
// SetNamespace 设置命名空间
SetNamespace(string)
// SetService 设置服务名
SetService(string)
// SetLabels 设置业务标签信息
// Deprecated: please use AddArgument instead
SetLabels(map[string]string)
// SetMethod set method
SetMethod(method string)
// AddArgument add the match argument
AddArgument(argument model.Argument)
// SetToken set token to acquire
SetToken(uint32)
// SetTimeout 设置单次请求超时时间
SetTimeout(timeout time.Duration)
// SetRetryCount 设置最大重试次数
SetRetryCount(retryCount int)
}
如果当前 QuotaRequest 还不支持 AddArgument 方法,同时服务端版本 >= 1.11.0,SetLabels 对应的 key 名称如下:
- 路径: $path
- 方法: $method
- 请求头: $header.{标签键}
- 请求参数: $query.{标签键}
- 主调服务: $caller_service
- 主调IP: $caller_ip
- 自定义: {标签键}
发起请求配额申请
你在接收到请求之后对 QuotaRequest 结构体完成初始化后,只需要调用 LimitAPI.GetQuota 方法即可完成本次请求配额的申请。
ret, err := limiter.GetQuota(QuotaRequest)
对于请求配额结果的结构体如下。
// QuotaFuture 实时/延时分配future
type QuotaFuture interface {
// Done 标识分配是否结束
Done() <-chan struct{}
// Get 等待一段时间后,获取分配结果,用于匀速排队
Get() *model.QuotaResponse
// GetImmediately 立刻获取分配结果,不等待
GetImmediately() *model.QuotaResponse
// Release 释放资源,仅用于并发数限流的场景
Release()
}
分布式限流使用
如果要使用分布式限流,请先确保已经部署了北极星分布式限流 server
部署完后确认北极星控制台存在服务 命名空间: Polaris, 服务名: polaris.limiter。
确认完毕后,调整 polaris.yaml 配置文件,在控制台配置分布式限流规则,SDK 仍然使用 ret, err := limiter.GetQuota(QuotaRequest) 即可。
provider:
rateLimit:
enable: true
limiterNamespace: Polaris
limiterService: polaris.limiter
如何基于 polaris-go 客户端完成一个节点熔断的程序
1.6 - 缓存与高可用
概述
我们研发的项目在使用服务注册发现后,可以在请求过程中自动匹配到最合适的节点,并能通过服务注册中心灵活的调整访问路由规则、限流策略、熔断策略,实现灰度发布、失败熔断等复杂的业务运维场景。北极星SDK大幅减轻了业务研发的负担,使业务代码不需要直接参与服务注册中心的交互,只要调用SDK提供的基础API就可以获得最终结果,SDK能够实现上述抽象统合能力,缓存的设计和实现是基础。 使用北极星的业务研发理解SDK缓存实现机制,有助于编写更安全的代码逻辑,设计合理的高可用方案和相关配置
本文试图回答的场景问题:
- 北极星SDK产生了哪些缓存,各自有什么用
- 怎样调节和观测这些缓存的内容
- 缓存对业务高可用机制产生哪些影响
- 我们需要关注哪些缓存相关的配置项,应该在哪些场景进行调整
缓存构成
内存缓存
为什么需要内存缓存
性能
在使用服务注册发现的业务场景中,为了保障访问的目标后端地址符合预期,访问端(Consumer)每次连接被访问端(Provider)时,都需要经过基于服务注册发现的一系列查询和过滤逻辑,包括:查询当前有哪些健康的被访问端实例、经过路由策略筛选掉哪些、经过熔断策略筛选掉哪些、最终经过负载均衡策略筛选出唯一的被访问端地址
在这一系列的服务发现过程中,都需要从服务注册中心(Server)获取各类实时数据,包括:被访问端的全部实例列表、路由策略配置、熔断策略配置等。如果每次业务请求都需要经过一系列的服务信息查询,势必会大幅降低业务性能,同时对服务注册中心造成巨大压力 SDK需要提供上述信息的动态缓存能力,让业务可以直接通过内存中获取到需要的数据信息
可靠性
同样的,缓存手段也保障了在服务注册中心不可用情况下的业务连续性,详细请见 典型场景 高可用 章节
缓存哪些内容
缓存数据格式
每个SDKContex维护一个全局的sync.Map内存缓存表,各类缓存数据均保存在这个缓存表中。其中Key标识数据类型和服务名,Value为数据报文。以服务实例的缓存表内容为例:
SDK使用 sync.Map的Load()、Store()、Delete()等原子动作对缓存表进行维护
缓存类型
实例信息
- 类型:Instances
- 说明:单个服务的所有实例信息,包含所有健康或异常的实例
- 使用场景:调用GetInstances场景使用,查询所有实例
路由信息
- 类型:Routing
- 说明:单个服务的所有路由规则数据
- 使用场景:调用ProcessRouter场景使用,查询服务关联的路由规则
熔断信息
- 类型:CircuitBreaker
- 说明:单个服务的所有熔断规则信息
- 使用场景:调用GetInstances场景使用,用于判断返回的实例是否被熔断
限流信息
- 类型:RateLimiting
- 说明:单个服务的所有限流规则数据
服务信息
- 类型:Services
- 说明:根据输入的标签批量查询服务
- 使用场景:调用WatchServices/GetServices场景使用,查询所有服务信息
怎样产生和更新
SDK的Cache模块对上层模块提供Get/Load/Report等原语方法
以获取服务实例信息为例:
- 获取缓存:上层模块优先调用Get原语,Get尝试从内存缓存表中获取对应的服务数据,如果Get数据为空或以失效,则发起远程调用的Load流程
- 远程调用:上层模块调用Load会触发生成查询任务,并由任务调度队列轮询发往服务注册中心,由SDK维护的固定长连接发送和接收数据报文,详见 网络连接 。查询任务产生后,SDK就开启了针对这条服务信息的Watch流程
- Watch机制:SDK的连接模块持续监听服务注册中心的回包,并根据回包类型产生缓存数据,更新到缓存表中,Watch流程包含两个关键点:
- 差异化更新:为了避免不必要的缓存表更新,对回包revision和缓存数据revision进行对比,有差异再更新
- Watch频率:为了控制每个客户端SDK与服务注册中心的通信频率,每条任务轮询的最小间隔时长由配置
.consumer.localCache.serviceRefreshInterval
确定,默认为2秒
- 更新缓存状态:上层模块通过调用Report原语更新缓存表中服务实例的熔断状态,用于下一次查询获取服务实例的熔断信息
缓存多久
请求计数:SDK的Cache模块对上层模块提供Get原语获取缓存数据,每次调用Get获取缓存表中的一条数据后,就会对应更新这条数据的最后访问时间(lastVisitTime)
缓存GC:为了避免缓存表占用空间越来越大,查询任务队列越来越多,SDK设计支持缓存的GC机制。通过配置 .consumer.localCache.serviceExpireTime
(默认24小时)定义过期时长,当一条缓存数据大于过期时长没有被访问后,将会被从缓存表中删除,并会连带删除这条缓存数据对应的同步任务和持久化文件缓存。缓存数据被GC后,意味着对应服务的Watch流程也终结,将由下一次Get查询获取结果为空时重新发起Watch和缓存
判断缓存是否过期的公式可简化为:
time.Now() - lastVisitime > serviceExpiretime
持久化缓存
为什么需要持久化缓存
可靠性
服务注册中心故障后,业务依靠内存缓存表可以继续对其他服务寻址,但是如果业务碰巧也重启了,内存缓存表就会丢失,这时就需要文件缓存来顶上
可维护性
通常业务研发对SDK产生的数据会经过再加工使用,文件缓存增加了服务发现数据的可视性,我们可以通过观测缓存文件来分析SDK的内存数据内容,进而在遇到服务发现数据不准时,方便判断问题的归属区域
存在哪里
由配置项 .consumer.localCache.persistDir
确定,默认为 ./polaris/backup
什么格式
以服务default/demo的实例缓存和路由缓存数据为例:
- 文件名称:svc#$命名空间$服务名$数据类型.json
- 文件内容:数据类型返回的请求报文
读写机制
读取
服务启动时由配置项 .consumer.localCache.startUseFileCache
(默认false)决定是否由持久化缓存产生内存缓存表,如果持久化缓存被读取到内存缓存表中,由配置项.consumer.localCache.persistAvailableInterval
(默认5分钟)决定这份数据是否有效,文件缓存在内存缓存表中的有效范围可简化表示为:
startUseFileCache && (time.Now() - file.ModTime() < persistAvailableInterval)
更新 当内存缓存Watch流程判定缓存发生新增、更新或删除时,均会发起持久化缓存更新调度任务,调度任务每100毫秒轮询一次,对待执行的任务进行持久化操作,既写入到文件
删除 当内存缓存表被GC时,对应的持久化缓存文件同时也会被删除
典型场景
高可用
故障场景
1.注册中心故障
服务注册中心发生故障时,SDK缓存使业务能够继续保持通信和路由选择,直到缓存失效
2.访问端与注册中心网络断开
对于访问端服务A,与服务注册中心发生故障效果相同,SDK缓存使业务能够继续保持通信和路由选择,直到缓存失效
3.被访问端与注册中心网络断开
被访问端服务B与注册中心连接断开时,因为B无法向注册中心更新心跳信息,访问端服务A获取到服务B的所有实例均为下线状态。SDK会执行默认的兜底路由逻辑,认为所有的服务实例均为健康状态,不影响服务A向服务B访问
4.上述故障场景+服务A重启
服务A配置开启文件缓存并且允许初始化读取时,重启后会读取持久化缓存到内存缓存表中,这样业务能够继续保持与服务B的通信和路由选择
边界条件
上述故障场景生效期间,当以下条件同时触发时,缓存机制无法保障业务的通信或路由选择符合预期
1.被访端服务实例下线
被访问端B的服务实例下线后,因为服务A内的缓存无法通过Watch注册中心动态更新,A有可能继续访问到异常的服务实例
2.访问端缓存过期
访问端缓存过期后(具体触发条件参考 缓存构成 章节),因为前述故障条件,服务A不再能够重新生成缓存,A访问B必现失败
可用性矩阵
注册中心故障 | 访问端与注册中心断连 | 被访端与注册中心断连 | 访问端重启 | 被访端实例下线 | 被访端缓存失效 | 访问端业务是否可用 |
---|---|---|---|---|---|---|
N | N | Y | N | N | N | 可用 |
N | Y | N | N | N | N | 可用 |
Y | Y | Y | N | N | N | 可用 |
Y | Y | Y | Y | N | N | 可用 |
Y | Y | Y | Y | Y | N | 部分可用 |
Y | Y | Y | Y | Y | Y | 不可用 |
多Context用法
因为部分历史原因,业务研发有在同一个进程内开启多个SDKContext的用法,每个SDKContext实例会产生独立的长连接与服务注册中心交互,并各自维护独立的内存缓存表,但默认共享使用同一份持久化缓存
这种用法可能导致缓存同步异常和问题排查的困难,强烈建议改为单个业务进程使用全局共享一个SDKContext。如果由于特殊原因无法改造,需要对使用方式做如下约束:
// 初始化配置X
cfgX := config.NewDefaultConfigurationWithDomain()
// 开启文件缓存、设置独立的文件缓存路径
cfgX.GetConsumer().GetLocalCache().SetPersistDir("./polaris/ctx-x/backup")
cfgX.GetConsumer().GetLocalCache().SetPersistEnable(true)
// 使用指定的配置初始化SDKContextX
sdkCtxFoo, _ := polaris.NewSDKContextByConfig(cfgX)
// 初始化配置Y
cfgY := config.NewDefaultConfigurationWithDomain()
// 开启文件缓存、设置独立的文件缓存路径
cfgY.GetConsumer().GetLocalCache().SetPersistDir("./polaris/ctx-y/backup")
cfgY.GetConsumer().GetLocalCache().SetPersistEnable(true)
// 使用指定的配置初始化SDKContextY
sdkCtxY, _ := polaris.NewSDKContextByConfig(cfgY)
配置汇总
- 配置项:
consumer.localCache.serviceExpireTime
- 说明:内存缓存过期时间
- 默认值:24小时
- 配置项:
consumer.localCache.serviceRefreshInterval
- 说明:内存缓存任务最小发送间隔
- 默认值:2秒
- 配置项:
consumer.localCache.persitEnable
- 说明:是否开启持久化缓存
- 默认值:true
- 配置项:
consumer.localCache.persistDir
- 说明:持久化缓存存放路径
- 默认值:./polaris/config
- 配置项:
consumer.localCache.startUseFileCache
- 说明:启动读取持久化缓存初始化内存缓存表
- 默认值:false
- 配置项:
consumer.localCache.persistAvailableInterval
- 说明:启动读取持久化缓存数据有效时间
- 默认值:60秒
1.7 - 网络连接
概述
北极星go sdk做为业务层代码调用北极星服务能力的中间层,为业务层抽象了服务、配置、限流相关的接口能力,使业务层不需要感知和管理与北极星服务端的连接。本文介绍北极星go sdk网络连接管理的内部实现机制和相关配置项,为业务研发和运维优化提供参考。
本文试图回答的场景问题:
- 北极星SDK与北极星服务端有哪些连接
- 这些连接断开或超时异常时,会触发什么问题和现象
- 我们需要关注哪些连接相关的配置项,应该在哪些场景进行调整
连接分类
服务连接(Register)
服务注册反注册、路由规则、负载均衡、熔断、健康检查等业务能力使用服务连接
连接地址
配置项 global.serverConnector.address
连接分类
长连接
- 客户端调用服务发现(Disover)接口发起长连接,并周期性发起针对某项数据的监听任务;当超过配置项
consumer.localCache.serviceExpireTime
(默认24小时)后没有访问该项数据,则停止该周期性监听任务;当长连接中没有任何监听任务,超过global.serverConnector.connectionIdleTimeout
(默认3s)时长没有访问流量后,长连接会断开,由下一次查询重新发起;
短链接
- 非周期性:服务注册(Register)/反注册(DeRegister)发起,调用结束后关闭
- 周期性:上报客户端(ReportClient)接口定时向服务端上报客户端信息,上报动作发起的定时周期由
global.api.reportInterval
(默认2分钟) 配置项规定,;每个SDKContext运行一个周期性上报客户端协程,通常使用场景下每个业务客户端运行一个全局SDKContext - 周期性:服务注册后,需要周期性向Server端发起心跳(HeartBeat),更新Server端记录的服务实例的心跳时间;心跳动作会发起短链接,更新完成后关闭;心跳动作发起的周期为服务注册参数中的TTL时间,默认为5秒
配置连接(Configuration)
客户端对配置文件、配置分组相关的操作使用配置连接
连接地址
配置项 config.configConnector.address
连接分类
短连接
- 非周期性:客户端对配置的增删改成接口均会发起短链接,在接口操作完成后关闭
限流连接(RateLimiter)
客户端对限流接口的调用使用限流连接
连接地址
配置项 provider.rateLimit.limiterNamespace
(默认为Polaris) 和 provider.rateLimit.limiterService
(默认为polaris.limiter) 注册的后端服务
连接分类
长连接
- 客户端调用限流接口(GetQuota)查询发起长连接,超过配置项
provider.rateLimit.purgeInterval
(默认1分钟)时长没有调用访问Quota数据时,会发起闲置检查;闲置连接检查超过global.serverConnector.connectionIdleTimeout
(默认3秒)时长没有访问流量后,长连接会断开,由下一次查询重新发起
监控连接(StatReporter)
客户端上报服务调用结果相关的监控数据
连接地址
配置项 global.statReporter.plugin.prometheus.address
连接分类
短连接
- 周期性:客户端启动后,定时间隔上报服务调用、路由、限流结果等监控数据。
连接信息汇总
相关配置汇总
配置项:global.serverConnector.connectTimeout
说明:服务客户端连接服务端同步请求超时时间
默认值:500毫秒
配置项:global.serverConnector.messageTimeout
说明:服务客户端接受服务端异步消息超时时间
默认值:1500毫秒
配置项:global.serverConnector.connectionIdelTimeout
说明:服务连接闲置连接超时时间
默认值:3秒
配置项:global.serverConnector.reconnectInterval
说明:服务客户端重连时间间隔
默认值:500毫秒
配置项:global.api.reportInterval
说明:客户端定时上报周期
默认值:2分钟
配置项:consumer.localCache.serviceExpireTime
说明:客户端缓存失效时间
默认值:24小时
配置项:config.configConnector.connectTimeout
说明:配置客户端连接服务端同步请求超时时间
默认值:500毫秒
配置项:config.configConnector.messageTimeout
说明:配置客户端接受服务端异步消息超时时间
默认值:1500毫秒
配置项:config.configConnector.connectionIdelTimeout
说明:配置连接闲置连接超时时间
默认值:3秒
配置项:config.configConnector.reconnectInterval
说明:配置客户端重连时间间隔
默认值:500毫秒
配置项:provider.ratelimit.purgeInterval
说明:限流查询任务超时检查周期
默认值:1分钟
配置项:global.statReporter.plugin.prometheus.interval
说明:监控数据定时上报周期
默认值:1分钟
业务场景Q&A
Q:北极星SDK与北极星服务端有哪些连接
A:正常业务运行情况下,1个服务发现长连接 + 1个限流长连接 + 若干实时打开关闭的短连接,例如:注册一个服务,TTL设置为2秒,操作系统默认TCP MSL 30秒(net.ipv4.tcp_fin_timeout=60) ,则通过 netstat -ano |grep 8091
预期看到 2 个 ESTABLISHED 状态连接 和 30个左右 TIMEOUT 状态连接
Q:这些连接断开或超时异常时,会触发什么问题和现象
A:重点关注:服务发现长连接异常断开后,调用SDK负载均衡、路由等接口不受影响,临时使用缓存数据,直到serviceExpireTime触发缓存数据失效或连接恢复;限流长连接断开后,调用SDK限流接口不受影响,临时退化到单机限流能力直到连接恢复
Q:我们需要关注哪些连接相关的配置项,应该在哪些场景进行调整
A:当客户端与服务端网络条件不稳定时,应适度调整增加以下参数提高容错性:
consumer.localCache.serviceExpireTime
(服务发现连接断开后缓存可用时间)
global.serverConnector.connectTimeout
(服务发现同步gRPC请求超时时间)
global.serverConnector.messageTimeout
(服务发现异步gRPC请求回包超时时间)
config.configConnector.connectTimeout
(配置同步gRPC请求超时时间)
config.configConnector.messageTimeout
(配置异步gRPC请求回包超时时间)
1.8 - 配置管理
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
SDK实例构建
当初始化好 polaris.yaml 文件之后,你可以直接使用在 package github.com/polarismesh/polaris-go 下的 NewConfigAPI 方法进行构造一个 ConfigAPI SDK 实例
import (
...
"github.com/polarismesh/polaris-go"
)
func main() {
configAPI, err := polaris.NewConfigAPI()
}
获取配置文件
// namespace: 命名空间
// fileGroup: 配置分组名称
// fileName: 配置文件名称
GetConfigFile(namespace, fileGroup, fileName string) (model.ConfigFile, error)
对配置文件发起监听
func changeListener(event model.ConfigFileChangeEvent) {
}
func main() {
configFile, err := configAPI.GetConfigFile(namespace, fileGroup, fileName)
configFile.AddChangeListener(changeListener)
}
查询加密配置
需要更新 polaris-go 的版本至 v1.5.0 及以上
// namespace: 命名空间
// fileGroup: 配置分组名称
// fileName: 配置文件名称
GetConfigFile(namespace, fileGroup, fileName string) (model.ConfigFile, error)
调整 polaris.yaml 配置文件
# 配置中心默认配置
config:
# 配置过滤器
configFilter:
enable: true
chain:
# 启用配置解密插件
- crypto
plugin:
crypto:
# 配置解密插件的算法插件类型
entries:
- name: AES
监听配置分组下的已发布文件列表变化
需要更新 polaris-go 的版本至 v1.5.6 及以上版本
// namespace: 命名空间
// group: 配置分组名称
GetConfigGroup(namesapce, group string) (model.ConfigFileGroup, error)
获取到目标配置分组后, 调用配置分组的 AddChangeListener 方法监听改配置分组下已发布配置文件列表的变化
group.AddChangeListener(func(event *model.ConfigGroupChangeEvent) {
before, _ := json.Marshal(event.Before)
after, _ := json.Marshal(event.After)
log.Printf("receive config_group change event\nbefore: %s\nafter: %s", string(before), string(after))
})
model.ConfigGroupChangeEvent 结构体的具体信息
type SimpleConfigFile struct {
// 配置文件命名空间
Namespace string
// 配置文件所在分组名称
FileGroup string
// 配置文件名称
FileName string
// 配置文件的发布版本号,由服务端
Version uint64
Md5 string
ReleaseTime time.Time
}
// ConfigGroupChangeEvent 配置文件变更事件
type ConfigGroupChangeEvent struct {
// Before 该配置分组之前所有已发布的配置文件列表信息
Before []*SimpleConfigFile
// After 该配置分组当前已发布的所有配置文件列表信息
After []*SimpleConfigFile
}
相关示例工程代码
1.9 - 可观测性
引入依赖
go get github.com/polarismesh/polaris-go@latest
通过配置文件 polaris.yaml 开启监控上报
- 你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
通过 prometheus pull 模式上报监控数据
#描述:全局配置项
global:
#统计上报设置
statReporter:
#描述:是否将统计信息上报至monitor
#类型:bool
enable: true
#描述:启用的统计上报插件类型
#类型:list
#范围:已经注册的统计上报插件的名字
chain:
- prometheus
plugin:
prometheus:
type: pull
#描述: 设置 prometheus http-server 的监听IP
#类型:string
#默认使用SDK的绑定IP
metricHost:
#描述: 设置 prometheus http-server 的监听端口
#类型:int
#默认值: 28080
#如果设置为负数,则不会开启默认的http-server
#如果设置为0,则随机选择一个可用端口进行启动 http-server
metricPort: 28080
通过 pushgateway push 模式上报监控数据
#描述:全局配置项
global:
#统计上报设置
statReporter:
#描述:是否将统计信息上报至monitor
#类型:bool
enable: true
#描述:启用的统计上报插件类型
#类型:list
#范围:已经注册的统计上报插件的名字
chain:
- prometheus
plugin:
prometheus:
type: push
#描述: 设置 pushgateway 的地址, 仅 type == push 时生效
#类型:string
#默认 ${global.serverConnector.addresses[0]}:9091
address: 127.0.0.1:9091
#描述:设置metric数据推送到pushgateway的执行周期, 仅 type == push 时生效
#类型:string
#格式:^\d+(ms|s|m|h)$
#范围:[1m:...]
#默认值:10m
interval: 10s
通过代码开启监控上报
通过 prometheus pull 模式上报监控数据
cfg := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
cfg.GetGlobal().GetStatReporter().SetEnable(true)
cfg.GetGlobal().GetStatReporter().SetChain([]string{"prometheus"})
cfg.GetGlobal().GetStatReporter().SetPluginConfig("prometheus", &prometheus.Config{
Type: "pull",
PortStr: "28080",
IP: "",
})
通过 pushgateway push 模式上报监控数据
cfg := config.NewDefaultConfiguration([]string{"127.0.0.1:8091"})
cfg.GetGlobal().GetStatReporter().SetEnable(true)
cfg.GetGlobal().GetStatReporter().SetChain([]string{"prometheus"})
cfg.GetGlobal().GetStatReporter().SetPluginConfig("prometheus", &prometheus.Config{
Type: "push",
Interval: 10 * time.Second,
Address: "",
})
SDK实例构建
使用在 package github.com/polarismesh/polaris-go 下的 NewConsumerAPI 方法进行构造一个 ConsumerAPI SDK 实例
import (
...
"github.com/polarismesh/polaris-go"
)
func main() {
consumer, err := polaris.NewConsumerAPI()
}
上报调用情况
type ServiceCallResult struct {
// 上报的服务实例
CalledInstance Instance
// 调用接口方法
Method string
// 必选,本地服务调用的状态,正常or异常
RetStatus RetStatus
// 必选,本地服务调用的返回码
RetCode *int32
// 必选,被调服务实例获取接口的最大时延
Delay *time.Duration
// 可选,主调服务实例的服务信息
SourceService *ServiceInfo
}
上报请求调用结果
你在根据请求调用情况对 ServiceCallResult 结构体完成初始化后,只需要调用 ConsumerAPI.UpdateServiceCallResult 方法即可完成请求调用结果上报。SDK 内部会根据上报的调用结果信息,将其转换为相应的流量调用指标数据,上报至 prometheus。
consumer.UpdateServiceCallResult(ServiceCallResult)
1.10 - 二次寻址
引入依赖
go get github.com/polarismesh/polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
修改 polaris.yaml 开启二次寻址
#描述:全局配置项
global:
#描述系统相关配置
system:
#服务发现集群
discoverCluster:
namespace: Polaris
service: polaris.discover
#可选:服务刷新间隔
refreshInterval: 10m
#健康检查集群
healthCheckCluster:
namespace: Polaris
service: polaris.healthcheck
#可选:服务刷新间隔
refreshInterval: 10m
2 - 使用 dubbogo
2.1 - 服务注册
在 dubbogo 中快速体验北极星的服务注册以及服务发现能力
环境准备
参考 dubbogo 官网文档
dubbogo.yaml 配置文件
dubbo:
registries:
polaris-1:
protocol: polaris
address: ${北极星服务端IP}:8091
namespace: ${北极星命名空间信息}
token: ${北极星资源鉴权 token} # 如果北极星服务端开启了针对客户端的鉴权,则需要配置该参数
示例代码(dubbogo 原生使用方式)
当前 PolarisMesh 已实现了 dubbogo 的注册发现扩展点,因此你只需要调整你的 dubbogo.yaml 文件中的 registries 配置项,新增 protocol 为 polaris 的注册中心配置即可,可以参考下面的样例。
func init() {
config.SetProviderService(&UserProvider{})
hessian.RegisterPOJO(&User{})
}
type UserProvider struct {}
func (u *UserProvider) GetUser(ctx context.Context, req *User) (*User, error) {
rsp := User{"A001", "Alex Stocks", 18, time.Now()}
return &rsp, nil
}
func main() {
if err := config.Load(); err != nil {
panic(err)
}
initSignal()
}
验证
可根据 dubbogo example 开展
2.2 - 服务发现
在 dubbogo 中快速体验北极星的服务发现能力
环境准备
参考 dubbogo 官网文档
dubbogo.yaml 配置文件
dubbo:
registries:
polaris-1:
protocol: polaris
address: ${北极星服务端IP}:8091
namespace: ${北极星命名空间信息}
token: ${北极星资源鉴权 token} # 如果北极星服务端开启了针对客户端的鉴权,则需要配置该参数
示例代码(dubbogo 原生使用方式)
dubbogo 在进行服务调用时,会先通过 Polaris Registry 的 Extension 获取到服务的实例列表,然后转换为 dubbogo invoker,最终完成 dubbogo 服务调用。
当前 Polaris 已实现了 dubbogo 原生的服务发现扩展点,因此原本的 dubbogo 服务调用无需调整业务代码,仅需要在 dubbogo.yaml 中新增 protocol 为 polaris 的注册中心配置即可。
func main() {
var userProvider = &UserProvider{}
config.SetConsumerService(userProvider)
hessian.RegisterPOJO(&User{})
if err := config.Load(); err != nil {
panic(err)
}
user, err := userProvider.GetUser(context.TODO(), &User{Name: "Alex001"})
if err != nil {
panic(err)
}
logger.Infof("response result: %v\n", user)
}
验证
可根据 dubbogo example 开展
2.3 - 动态路由
当前支持针对 dubbogo 消息的以下内容进行动态路由:
消息类型 | dubbo消息内容 | 路由规则请求类型 |
---|---|---|
消息头 | attachment | 请求头(HEADER) |
RPC方法 | method | 路径(PATH) |
环境准备
参考 dubbogo 官网文档
dubbogo.yaml 配置文件
dubbo:
registries:
polaris-1:
protocol: polaris
address: ${北极星服务端IP}:8091
namespace: ${北极星命名空间信息}
token: ${北极星资源鉴权 token} # 如果北极星服务端开启了针对客户端的鉴权,则需要配置该参数
- 在使用动态路由能力时,需要先启用 Polaris 在 dubbogo 中的注册发现功能。
如何配置服务路由参数
dubbogo 中的 PolarisMesh PriorityRouter 扩展点实现,能够根据用户配置的服务路由规则,自动的从当前 RPC 调用上下文以及请求信息中识别出需要参与服务路由的请求标签信息。
假定一个场景:
- 希望 uid 为 user-1 的请求,路由到 environment 标签为 pre 的实例上
- 希望 uid 为 user-2 的请求,路由到 environment 标签为 dev 的实例上
- 其他则路由到 environment 标签为 prod 的实例上,那可以为 dubbogo 服务设置三条路由规则。
- 请求匹配规则为 请求参数(QUERY)
- 请求匹配规则为 请求头(HEADER):
- 标签来源: RPC 调用的额外标签信息,即 Invoaction.Attachments()
示例代码(dubbogo 原生使用方式)
func (s *Service) GetUser(uid string) {
atta := make(map[string]interface{})
atta["uid"] = uid
// 通过这种方式往 attachement 传入路由条件
reqContext := context.WithValue(context.Background(), constant.DubboCtxKey("attachment"), atta)
for i := 0; i < 5; i++ {
time.Sleep(200 * time.Millisecond)
user, err := userProvider.GetUser(reqContext, &User{Name: "Alex001"})
if err != nil {
logger.Errorf("error: %v\n", err)
}
logger.Infof("response: %v\n", user)
}
}
验证
可根据 dubbogo example 开展
2.4 - 访问限流
当前支持针对 dubbogo 消息的以下内容进行访问限流:
消息类型 | dubbo消息内容 | 路由规则请求类型 |
---|---|---|
消息头 | attachment | 请求头(HEADER) |
RPC方法 | method | 路径(PATH) |
环境准备
参考 dubbogo 官网文档
dubbogo.yaml 配置文件
在 dubbogo 中启用 Polaris 的 TpsLimiter,具体开启配置参考如下
dubbo:
registries:
polaris-1:
protocol: polaris
address: ${北极星服务端IP}:8091
namespace: ${北极星命名空间信息}
token: ${北极星资源鉴权 token} # 如果北极星服务端开启了针对客户端的鉴权,则需要配置该参数
provider:
services:
UserProvider:
interface: org.apache.dubbo.UserProvider.Test
tps.limiter: polaris-limit # 配置 tps.limiter 为 polaris-limiter 即可
- Polaris 的访问限流能力是工作在 Provider 侧的。
- 在使用访问限流能力时,需要先启用 Polaris 在 dubbogo 中的注册发现功能。
如何配置服务限流参数
dubbogo 中的 PolarisMesh TpsLimiter 扩展点实现,能够根据用户配置的限流规则,自动的从当前 RPC 调用上下文以及请求信息中识别出需要参与限流的请求标签信息。
比如对 dubbogo 中的 GetUser 方法,对请求参数 Name 为 Alex 的请求进行限流,速率为10/s。
- 请求匹配规则为 请求参数(QUERY)
- 请求匹配规则为 请求头(HEADER):
- 标签来源: RPC 调用的额外标签信息,即 Invoaction.Attachments()
验证
可根据 dubbogo example 开展
3 - 使用 gRPC-Go
3.1 - 服务注册
引入依赖
go get github.com/polarismesh/grpc-go-polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于 grpc-go-polaris 初始化 polaris sdk。polaris.yaml配置详细
服务注册
// 完成 grpc Server 的创建以及注册 grpc service
srv := grpc.NewServer()
listen, err := net.Listen("tcp", "0.0.0.0:8080")
if err != nil {
log.Fatal(err)
}
pb.RegisterEchoServerServer(srv, &EchoQuickStartService{
actualPort: listen.Addr().(*net.TCPAddr).Port,
})
// 将 使用 polaris.Serve 方法启动 grpc server
if err := polaris.Serve(srv, listen,
polaris.WithServiceName("QuickStartEchoServerGRPC"),
); nil != err {
log.Printf("listen err: %v", err)
}
服务端 Options
WithGRPCServerOptions(opts …grpc.ServerOption)
设置 gRPC-Server 的相关 Option。
WithServerNamespace(namespace string)
设置 grpc 服务注册到北极星的命名空间,默认为 default。
WithServiceName(svcName string)
设置 grpc 服务的名称,可选,不过不设置,则会通过 grpc.Server 的 GetServiceInfo() 获取所有 grpc service 信息,进行服务注册。
WithServerMetadata(metadata map[string]string)
设置服务实例的标签信息。
WithServerHost(host string)
设置服务实例注册的 host 信息,可选,默认将通过和北极星服务端建立一次 TCP 连接获取本机对外 IP
WithServerVersion(version string)
设置服务实例的版本信息。
WithTTL(ttl int) ServerOption
设置服务实例心跳上报的周期,默认 5s
WithToken(token string)
当北极星服务端开启客户端鉴权时,需要设置用户/用户组访问凭据,鉴权文档可参考 权限控制。
WithDelayRegisterEnable(strategy DelayStrategy)
设置延迟注册策略
WithGracefulStopEnable(duration time.Duration)
启用服务实例优雅下线能力,默认开启
WithGracefulStopDisable()
禁用服务实例优雅下线能力
如何基于 grpc-go-polaris 完成一个服务注册的程序
3.2 - 服务发现
引入依赖
go get github.com/polarismesh/grpc-go-polaris-go@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于 grpc-go-polaris 初始化 polaris sdk。polaris.yaml配置详细
服务发现
// 使用 grpc-go-polaris 提供的 DialContext 即可
conn, err := polaris.DialContext(ctx, "polaris://QuickStartEchoServerGRPC",
polaris.WithGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())),
polaris.WithDisableRouter(),
)
客户端 Options
WithGRPCDialOptions(opts …grpc.DialOption)
设置 grpc.DialContext 需要的 grpc.DialOption 参数
WithClientNamespace(namespace string)
设置主调服务所在的命名空间,可选
WithSrcService(srcService string)
设置主调服务的服务名称,可选
WithPolarisConfig(polarisCfg config.Configuration)
通过代码设置 polaris-go 的配置信息
WithDisableRouter()
禁用 polaris 的动态路由能力
WithEnableRouter()
启用 polaris 的动态路由能力
如何基于 grpc-go-polaris 完成一个服务发现的程序
3.3 - 动态路由
当前支持针对 gRPC 消息的以下内容进行动态路由:
消息类型 | gRPC 消息内容 | 路由规则请求类型 |
---|---|---|
消息头 | metadata | 请求头(HEADER) |
gRPC方法 | method | 路径(PATH) |
引入依赖
go get github.com/polarismesh/grpc-go-polaris@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于 grpc-go-polaris 初始化 polaris sdk。polaris.yaml配置详细
gRPC Client 构建
// 使用 grpc-go-polaris 提供的 DialContext 即可
conn, err := polaris.DialContext(ctx, "polaris://QuickStartEchoServerGRPC",
polaris.WithGRPCDialOptions(grpc.WithTransportCredentials(insecure.NewCredentials())),
polaris.WithEnableRouter(),
)
如何配置动态路由参数
gRPC-Go 中的 PolarisMesh Balancer 扩展点实现,能够根据用户配置的服务路由规则,自动的从当前 RPC 调用上下文以及请求信息中识别出需要参与服务路由的请求标签信息。
假定一个场景:
- 希望 uid 为 user-1 的请求,路由到 env 标签为 dev 的实例上
- 希望 uid 为 user-2 的请求,路由到 env 标签为 pre 的实例上
- 其他则路由到 env 标签为 prod 的实例上,那可以为 gRPC-Go 服务设置三条路由规则。
- 请求匹配规则为 请求头(HEADER):
- 标签来源: RPC 调用的额外标签信息,即 metadata.FromOutgoingContext(balancer.PickInfo.Ctx)
示例代码(gRPC-Go原生使用方式)
func (s *Service) GetUser(uid, value string) {
md := metadata.Pairs("uid", uid)
ctx := metadata.NewOutgoingContext(context.Background(), md)
for i := 0; i < 5; i++ {
time.Sleep(200 * time.Millisecond)
resp, err := s.echoClient.Echo(ctx, &pb.EchoRequest{Value: value})
if err != nil {
logger.Errorf("error: %v\n", err)
}
logger.Infof("response: %v\n", user)
}
}
验证
可根据 grpc-polaris-go example 开展
3.4 - 负载均衡
引入依赖
go get github.com/polarismesh/grpc-go-polaris@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于初始化 polaris-go SDK。polaris.yaml配置详细
设置默认负载均衡策略
修改 polaris.yaml 文件
#描述:主调端配置
consumer:
...
#描述:负载均衡相关配置
loadbalancer:
#描述:负载均衡类型
#范围:已注册的负载均衡插件名
#默认值:权重随机负载均衡
#支持参数:ringHash(一致性hash环)/maglev(maglev算法一致性hash)/hash(普通一致性hash)
type: weightedRandom
plugin:
#描述:虚拟节点的数量
#类型:int
#默认值:500
#ringHash:
# vnodeCount: 500
#maglev:
# # 初始化表向量区间
# tableSize: 65537
设置请求级别负载均衡策略
ctx := metadata.NewIncomingContext(context.Background(), metadata.MD{})
// 请求时设置本次请求的负载均衡算法
ctx = polaris.RequestScopeLbPolicy(ctx, api.LBPolicyRingHash)
ctx = polaris.RequestScopeLbHashKey(ctx, r.Header.Get("uid"))
resp, err := echoClient.Echo(ctx, &pb.EchoRequest{Value: value})
3.5 - 访问限流
当前支持针对 gRPC 消息的以下内容进行访问限流:
消息类型 | gRPC 消息内容 | 路由规则请求类型 |
---|---|---|
消息头 | metadata | 请求头(HEADER) |
gRPC方法 | method | 路径(PATH) |
引入依赖
go get github.com/polarismesh/grpc-go-polaris@latest
初始化 polaris.yaml
你需要在项目的根路径下创建一个 polaris.yaml 文件用于 grpc-go-polaris 初始化 polaris sdk。polaris.yaml配置详细
gRPC Server 构建
listen, err := net.Listen("tcp", "0.0.0.0:0")
if err != nil {
log.Fatal(err)
}
listenAddr := listen.Addr().String()
interceptor := polaris.NewRateLimitInterceptor().WithServiceName("RateLimitEchoServerGRPC")
// 注册 polaris 服务限流拦截器
srv := grpc.NewServer(grpc.UnaryInterceptor(interceptor.UnaryInterceptor))
pb.RegisterEchoServerServer(srv, &EchoRateLimitService{})
// 启动服务
if err := polaris.Serve(srv, listen,
polaris.WithServiceName("RateLimitEchoServerGRPC"),
); nil != err {
log.Printf("listen err: %v", err)
}
如何配置访问限流参数
gRPC-Go 中的 PolarisMesh RateLimiter 扩展点实现,能够根据用户配置的限流规则,自动的从当前 RPC 调用上下文以及请求信息中识别出需要参与限流的请求标签信息。
比如对 gRPC-Go 中的 Echo(context.Context, &EchoRequest) 方法,对 Metadata 中 uid 为 user-1 的进行限流,速率为10/s。
- 请求匹配规则为 请求头(HEADER):
- 标签来源: RPC 调用的额外标签信息,即 metadata.FromIncomingContext(context.Context)
验证
可根据 grpc-polaris-go example 开展