前话
PolarisMesh(北极星)是腾讯开源的服务治理平台,致力于解决分布式和微服务架构中的服务管理、流量管理、配置管理、故障容错和可观测性问题,针对不同的技术栈和环境提供服务治理的标准方案和最佳实践。
PolarisMesh 官网:
https://polarismesh.cn/#/
PolarisMesh Github:
https://github.com/polarismesh/polaris
Polaris-server 作为 PolarisMesh 的控制面,该进程主要负责服务数据、配置数据、治理规则的管理以及下发至北极星 SDK 以及实现了 xDS 的客户端。
Polaris-server 是如何处理客户端的服务实例的心跳请求的呢?心跳数据是怎么存储的呢?带着这个疑问,我们来探究看下 Polaris-server 的健康检查模块,看看北极星是如何实现的。
前期准备
正题
从上一篇文章我们看到,客户端在注册实例之后,会维护实例的心跳上报任务。
public InstanceRegisterResponse registerInstance(InstanceRegisterRequest request, RegisterFunction registerFunction,
HeartbeatFunction heartbeatFunction) {
// 将注册请求发送至北极星服务端
InstanceRegisterResponse instanceRegisterResponse = registerFunction.doRegister(request,
createRegisterV2Header());
// 当前实例的注册管理状态进行本地保存
RegisterState registerState = RegisterStateManager.putRegisterState(sdkContext, request);
if (registerState != null) {
// 首次存入实例的注册状态时,为该实例注册创建定期心跳上报动作任务
registerState.setTaskFuture(asyncRegisterExecutor.scheduleWithFixedDelay(
() -> doRunHeartbeat(registerState, registerFunction, heartbeatFunction), request.getTtl(),
request.getTtl(), TimeUnit.SECONDS));
}
return instanceRegisterResponse;
}
客户端会按照用户实际设置的 TTL 间隔进行心跳定期上报,而客户端和服务端的心跳上报通信协议基于 gRPC,具体的定义信息如下:
service PolarisGRPC {
...
// 被调方上报心跳
rpc Heartbeat(Instance) returns (Response) {}
}
那么当服务端收到心跳后,是如何处理心跳数据,以及如何根据 TTL 进行实例的健康状态检查的呢?这里我们先通过一个简单的数据图来对北极星服务端对于服务实例心跳包的处理有一个大致的理解后,我们再逐步针对每个细节进行分析。
服务端处理实例心跳
从流程图可知,客户端发起服务实例心跳包到北极星服务端后,先后经过 Apiserver -> Resource Auth Flter -> Service -> Healthcheck -> Checker Plugin。这里来分析下每个部分它们的职能:
如果细心查看流程图的读者会发现,Healthcheck 内部还有一层缓存,这层缓存的数据来源于北极星的 Cache 模块,并且该缓存只会保存开启了健康检查的实例信息,为什么需要这一层缓存呢?由于北极星的服务实例健康检查开关可根据用户实际需要选择开启或关闭,只有开启了健康检查的引擎实例才能上报心跳数据,如果没有开启健康检查的实例,上报心跳数据是一个无效数据,如果对这类的数据不加以拦截,那么会导致 Checker Plugin 内部保存了无效的心跳包数据,同时也会在有限的服务端资源内,和正常的心跳数据请求争抢服务端资源。因此这里做了一个心跳请求的判断处理。
服务端检查实例心跳
当服务端将客户端的实例心跳数据请求处理之后,接下来就是如何检查服务实例的健康状态是否处于正常状态、心跳包是否在 3TTL 内正常上报,北极星集群间各节点如何协同工作,一起完成服务实例的健康状态检查。这些都在 Service 下的 Healthcheck 模块完成。
这里先来看看 Healthcheck 在 polaris-server.yaml 中的相关配置。
healthcheck:
# 是否开启该北极星节点的健康检查组件
open: true
# 北极星健康检查集群的服务名
service: polaris.checker
# 设置时间轮参数
slotNum: 30
# 设置最小的实例健康检查周期
minCheckInterval: 1s
# 设置最大的实例健康检查周期
maxCheckInterval: 30s
# 由于一个北极星 SDK 实例会将当前 SDK 实例的相关信息上报到北极星服务段,因此这里也需要做一个额外检查
clientReportInterval: 120s
batch:
# 心跳数据批量修改的控制器
heartbeat:
open: true
queueSize: 10240
waitTime: 32ms
maxBatchCount: 32
concurrency: 64
# 健康检查的类型插件
checkers:
# 单机场景,基于 map 的心跳检查插件
- name: heartbeatMemory
# 集群场景,基于 DB 选主的分布式心跳检查插件
- name: heartbeatLeader
这里来分析下 Healthcheck 中每个部分的职能。
接着再通过一个时序图来看看 Healthcheck 是如何完成上述相关任务的。
通过 Dispatcher 组件的一致性 Hash 环对所有开启了健康检查服务实例进行责任节点分发,使得北极星的健康检查可以进行很好的水平扩展,同时将具体的健康检查执行判断以及健康检查数据 CURD 操作独立成插件。