RPC(5)- 健康检查

最后更新:2020-05-19

1. 健康检查

在分布式系统中,为了保证所有服务可用,当服务发生问题时能及时摘除有问题的服务,避免在发请求的时候选择出有问题的节点而影响业务,需要定期检测服务可用性,即健康检查。

一般而言,我们可以通过TCP心跳来检查服务端的运行情况,当服务方下线,调用方肯定会收到连接断开的通知事件。但是TCP心跳是有弊端的,因为长连接的心跳维持都只是表明链路上的正常,服务未必是可用的,比如数据库挂掉。

所以业内一般会用调用方主动探测的方式(也可以理解为一种心跳):调用方通过HTTP或TCP调用服务方发布的某个接口来完成健康检查,返回结果成功表明服务状态确实正常。

服务端定义一个专门用来响应健康检查请求的 api ,针对调用方发送(通过HTTP或TCP)的健康检查请求发回约定的响应格式数据。

服务方的状态一般会有两种状态:

  • 健康状态:健康检查一直成功;
  • 亚健康状态:健康检查连续几次失败;

节点的状态并不是固定不变的,它会根据心跳或者重连的结果来动态变化,具体状态间转换图如下:

  1. 一开始初始化的时候,如果建立连接成功,那就是健康状态,否则就是不健康状态
  2. 如果健康状态的节点连续出现几次不能响应请求的情况,那就会被标记为不健康状态
  3. 不健康后,如果连续几次都能正常响应心跳请求,那就可以转回健康状态

2. 反应服务真正的健康情况

服务层面的健康检查一个很重要的一点是要反应服务真正的健康状况,所以服务端在实现健康检查接口时要考虑自身情况, 检查各种强依赖项,然后反馈给客户端当前服务端是否足够健康可以接受新的请求:

  • 检查强依赖的外部服务的连通性、响应时间等信息(db/redis/queue/…)
  • 检查机器负载情况,当前机器负载是否在健康范围内
  • 检查 CPU 利用率、内存使用率
  • 检查程序后台任务的排队情况
  • 检查磁盘剩余空间、IO 情况
  • 检查机器带宽是否快跑满了
  • …等等

需要根据服务本身的实际情况选择合适的检查项(不是每个都需要检查,不同的服务的强依赖项不一样),以及有些检查项可能不适合在请求内完成(比如会导致响应时间太长客户端超时了),这种可能需要安装外部程序来协助检查,比如带宽/IO 信息可以从外部获取。

总之就是要真正反应服务真正的健康状况,一般不会啥都不做就回一个标志正常的响应。 如果这样的话跟普通的端口探活似乎没有太大的区别。

当然,如果场景就是只需要检查协议以及端口探活的话,直接响应一个表示成功的响应也没啥问题

3. overload 状态

一般健康检查的结果都是两种状态:健康和不健康。根据需要可以考虑引入第三种状态:overload 。 这个状态的目的是应对一种 场景:

服务可以正常处理健康检查,但是本身的负载已经非常高了,再接入多一点请求可能就要宕机了,然后节点宕机对整体服务的影响比较大(比如一些有状态服务宕机导致的客户端重连会对剩下的健康节点造成比较大的影响)。

一般这种情况都会触发系统负载相关告警(比如 CPU 或内存使用率告警),然后人工手动介入把节点从负载均衡器或者哪里给摘除防止持续处理请求导致宕机。

对于这种场景可以引入上面说的 overload状态。对于处于 overload 状态的节点不再接受新的请求,等到节点恢复为健康状态后再接受新的请求。至于为啥不直接把 overload 的解决标记为不健康呢,因为不健康状态一般都是节点宕机了不能自动恢复的情况,以及部分系统对于不健康的节点会自动触发实例重启的操作,这个自动重启的操作可能不是我们预期的也可能会触发更大的问题(比如引发客户端大量重连),所以不健康状态跟 overload 还是有一点区别,可以根据实际情况选择是引入 overload 状态还是直接用不健康状态代替。

这第三种状态也不一样要是跟负载有关,只要是一种介于健康和不健康两者之间的状态都可以考虑引入第三种状态(比如同时支持读写操作的节点在这第三种状态的时候只支持 操作,不接受 操作)。

4. 判断条件

在健康检查时,我们不能简单的把总失败次数当做判断条件,因为每个接口的调用频次不一样,有的接口可能 1 秒内调用上百次,有的接口可能半个小时才会调用一次。

同时服务的接口响应时间也是不一样的,有的接口可能 1ms,有的接口可能是 10s,所以我们也不能把 TPS 至来当作判断条件。

我们可以使用可用率连判断服务端的健康状态,可用率的计算方式是某一个时间窗口内接口调用成功次数的百分比(成功次数 / 总调用次数)。当可用率低于某个比例就认为这个节点存在问题,把它挪到亚健康列表,这样既考虑了高低频的调用接口,也兼顾了接口响应时间不同的问题。

有一个特例那就是:对于 Connection refused 错误一般建议直接标记为亚健康而不是等几次失败再标记为亚健康,而从亚健康(死亡)到健康需要通过几次检查才算恢复健康。

健康检查太频繁可能会影响被检查的服务,间隔太长可能会失去健康检查的意义无法及时探查到不健康的状态。

5. 参考资料

《RPC实战与核心原理》

https://mozillazg.com/2019/01/notes-about-design-health-checking.html

Edgar

Edgar
一个略懂Java的小菜比