我是靠谱客的博主 欢呼大叔,最近开发中收集的这篇文章主要介绍RPC 开发系列四: 健康检测,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

因为有了集群,所以每次发请求前,RPC 框架会根据路由和负载均衡算法选择一个具体的 IP 地址。为了保证请求成功,我们就需要确保每次选择出来的 IP 对应的连接是健康的。

调用方跟服务集群节点之间的网络状况是瞬息万变的,两者之间可能会出现闪断或者网络设备损坏等情况。

解决方案是让调用方实时感知到节点的状态变化,这样他们才能做出正确的选择。这个道理像我们开车一样,车有各种各样的零件,我们不可能在开车之前先去挨个检查下他们的健康情况,转而是应该有一套反馈机制,比如今天我的大灯坏了,那中控台就可以给我提示;明天我的胎压不够了,中控台也能够收到提示。汽车中大部分关键零件的状态变化,我作为调用方,都能够第一时间了解。

一、问题分析

有一天,我们公司某个业务研发团队的负责人急匆匆跑过来,让我帮他解决个问题。仔细听完他的描述后,我才明白,原来是他们发现线上业务的某个接口可用性并不高,基本上十次调用里总会有几次失败。

查看了具体的监控数据之后,我们发现只有请求具体打到某台机器的时候才会有这个问题,也就是说,集群中有某台机器出了问题。于是快刀斩乱麻,我建议他们先把这台“问题机器”下线,以快速解决目前的问题。

但对于我来说,问题并没有结束,我开始进一步琢磨:“接口调用某台机器的时候已经出现不能及时响应了,那为什么 RPC 框架还会继续把请求发到这台有问题的机器上呢?RPC 框架还会把请求发到这台机器上,也就是说从调用方的角度看,它没有觉得这台服务器有问题。”

就像警察破案一样,为了进一步了解事情的真相,我查看了问题时间点的监控和日志,在案发现场发现了这样几个线索:

  1. 通过日志发现请求确实会一直打到这台有问题的机器上,因为我看到日志里有很多超时的异常信息。
  2. 从监控上看,这台机器还是有一些成功的请求,这说明当时调用方跟服务之间的网络连接没有断开。因为如果连接断开之后,RPC 框架会把这个节点标识为“不健康”,不会被选出来用于发业务请求。
  3. 深入进去看异常日志,我发现调用方到目标机器的定时心跳会有间歇性失败。
  4. 从目标机器的监控上可以看到该机器的网络指标有异常,出问题时间点 TCP 重传数比正常高 10 倍以上。

有了对这四个线索的分析,我基本上可以得出这样的结论:那台问题服务器在某些时间段出现了网络故障,但也还能处理部分请求。换句话说,它处于半死不活的状态。但是(是转折,也是关键点),它还没彻底“死”,还有心跳,这样,调用方就觉得它还正常,所以就没有把它及时挪出健康状态列表。

我们为了快速解决问题,手动把那台问题机器下线了。刨根问底之后,其实更大的问题服务检测机制有问题,有的服务本来都已经病危了,但还以为是个感冒。

二、健康检测的逻辑

常用的检测方法就是用心跳机制。心跳机制说起来也不复杂,其实就是服务调用方每隔一段时间就问一下服务提供方,“兄弟,你还好吧?”,然后服务提供方很诚实地告诉调用方它目前的状态。

服务方的状态一般会有三种情况:

  1. 健康状态:建立连接成功,并且心跳探活也一直成功;
  2. 亚健康状态:建立连接成功,但是心跳请求连续失败;
  3. 死亡状态:建立连接失败。

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

 

首先,一开始初始化的时候,如果建立连接成功,那就是健康状态,否则就是死亡状态。这里没有亚健康这样的中间态。紧接着,如果健康状态的节点连续出现几次不能响应心跳请求的情况,那就会被标记为亚健康状态,也就是说,服务调用方会觉得它生病了。

亚健康状态,如果连续几次都能正常响应心跳请求,那就可以转回健康状态,证明病好了。如果病一直好不了,那就会被断定为是死亡节点,死亡之后还需要善后,比如关闭连接。

当然,死亡并不是真正死亡,它还有复活的机会。如果某个时间点里,死亡的节点能够重连成功,那它就可以重新被标记为健康状态。

当服务调用方通过心跳机制了解了节点的状态之后,每次发请求的时候,就可以优先从健康列表里面选择一个节点。当然,如果健康列表为空,为了提高可用性,也可以尝试从亚健康列表里面选择一个。

三、解决方案

一个节点从健康状态过渡到亚健康状态的前提是“连续”心跳失败次数必须到达某一个阈值,比如 3 次。而我们的场景里,节点的心跳日志只是间歇性失败,也就是时好时坏,这样,失败次数根本没到阈值,调用方会觉得它只是“生病”了,并且很快就好了。那怎么解决呢?

第一,调用方跟服务节点之间网络状况瞬息万变,出现网络波动的时候会导致误判。

第二,在负载高情况,服务端来不及处理心跳请求,由于心跳时间很短,会导致调用方很快触发连续心跳失败而造成断开连接。

核心是服务节点网络有问题,心跳间歇性失败。现在判断节点状态只有一个维度,那就是心跳检测,那是不是可以再加上业务请求的维度呢?

出现新的问题:

  1. 调用方每个接口的调用频次不一样,有的接口可能 1 秒内调用上百次,有的接口可能半个小时才会调用一次,所以我们不能把简单的把总失败的次数当作判断条件。
  2. 服务的接口响应时间也是不一样的,有的接口可能 1ms,有的接口可能是 10s,所以我们也不能把 TPS 至来当作判断条件。

最终方案:

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

四、总结

在 RPC 框架里面我们会有采用定时“健康检测”,其实在其它分布式系统设计的时候也会用到“心跳探活”机制。

为了处理应用僵死的情况?让每个应用实例提供一个“健康检测”的 URL,检测程序定时通过构造 HTTP 请求访问该 URL,然后根据响应结果来进行存活判断,这样就可以防止僵死状态的误判。

为了减少误判的几率,把检测程序部署在多个机器里面,分布在不同的机架,甚至不同的机房。因为网络同时故障的概率非常低,所以只要任意一个检测程序实例访问目标机器正常,就可以说明该目标机器正常。

最后

以上就是欢呼大叔为你收集整理的RPC 开发系列四: 健康检测的全部内容,希望文章能够帮你解决RPC 开发系列四: 健康检测所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(49)

评论列表共有 0 条评论

立即
投稿
返回
顶部