服务发现的作用:实时感知集群IP的变化,实现接口跟服务集群节点IP的映射。超大规模集群更要考虑最终一致性。“推拉结合,以拉为准”。
1 健康检测
因为有了集群,所以每次发请求前,RPC框架会根据路由和负载均衡算法选一个具体的IP地址。为保证请求成功,就要确保每次选出来的IP对应连接是健康的。
但调用方跟服务集群节点之间的网络状况瞬息万变,两者之间可能会闪断或网络设备损坏等,怎么保证选出来的连接一定可用?
终极的解决方案是让调用方实时感知到节点的状态变化,这样他们才能做正确选择。像开车一样,车有各种各样的零件,我们不可能在开车之前先去挨个检查下他们的健康情况,转而是应该有一套反馈机制,比如今天我的大灯坏了,那中控台就可以给我提示;明天我的胎压不够了,中控台也能够收到提示。汽车中大部分关键零件的状态变化,我作为调用方,都能够第一时间了解。
回到RPC框架里,专业一点的词来就是服务的健康检测。
2 生产事故
线上业务的某个接口可用性并不高,十次调用总有几次失败。
查看监控数据后,发现只有请求具体打到某台机器才有这问题,即集群中有某台机器异常。建议他们先“问题机器”下线。
接口调用某台机器时已出现不能及时响应了,为何RPC框架还继续把请求发到这台有问题机器?即在调用方角度,它没有觉得这台服务器异常。
于是查看问题时间点的监控和日志:
- 通过日志发现,请求确实会一直打到这异常机器,因为日志有很多超时的异常信息
- 监控上看,这台机器还是有一些成功请求,这说明当时调用方跟服务之间的网络连接没有断开。因为若连接断开后,RPC框架会把该节点标识为“不健康”,不会被选出来用于发业务请求
- 深入看异常日志,发现调用方到目标机器的定时心跳会有间歇性失败
- 从目标机器的监控,可看到该机器的网络指标有异常,出问题时间点TCP重传数比正常高10倍以上
基本可得:那台问题服务器在某些时间段出现网络故障,但也还能处理部分请求。即它处于半死不活状态。但还没彻底“死”,还有心跳,这样,调用方就觉得它还正常,所以就没有把它挪出健康状态列表。
所以,更大问题是服务检测机制有问题,有的服务本来都已新冠晚期,但还以为人家只是普通感冒。
3 健康检测的逻辑
当服务方下线,正常情况下我们肯定会收到连接断开的通知事件,在这事件里直接加处理逻辑不就行?
是的,汽车案例里检测都是这样做的。但这不行,应用健康状况不仅包括TCP连接状况,还包括应用本身是否存活,很多情况下TCP连接没断开,但应用可能已“僵死”。
所以,常用检测方法即心跳,服务调用方定时问服务提供方,“兄弟,还好吧?”,然后服务提供方如实地告诉调用方它目前状态。
服务方的状态
① 健康状态
我很好。建立连接成功,并且心跳探活也一直成功
② 亚健康状态
我生病了。建立连接成功,但是心跳请求连续失败
③ 死亡状态
没回复。建立连接失败。
节点的状态会根据心跳或重连结果而动态变化:
初始化时,若建立连接成功,那就是健康状态,否则死亡状态,没有亚健康中间态。
若健康状态的节点连续出现不能响应心跳请求情况,就标记为亚健康,即服务调用方会觉得它生病了。
生病之后(亚健康状态):
- 若连续几次都能正常响应心跳请求,就可转回健康状态,证明病好了
- 若病一直好不了,断定为是死亡节点,死亡之后还需要善后,如关闭连接
死亡还有复活的机会。如果某个时间点里,死亡节点能重连成功,就重新标记为健康状态。
当服务调用方通过心跳了解节点状态后,每次发请求时,优先从健康列表里选择一个节点。若健康列表为空,为提高可用性,也可尝试从亚健康列表里面选个。
3 具体解决方案
一个节点从健康状态过渡到亚健康状态的前提:“连续”心跳失败次数必须到达某一个阈值,如3次。
而场景里,节点心跳日志间歇性失败,时好时坏,失败次数根本没到阈值,调用方觉得它只是“生病”,并且很快就好了。怎么解决?改下配置,调低阈值?是的,这是最快解决方法,但治标不治本:
- 调用方跟服务节点之间网络状况瞬息万变,出现网络波动的时候会导致误判
- 负载高情况,服务端来不及处理心跳请求,由于心跳时间很短,会导致调用方很快触发连续心跳失败而造成断开连接
问题核心是服务节点网络有问题,心跳间歇性失败。现在判断节点状态只有一个维度,那就是心跳检测,那是不是可以再加上业务请求维度?
但又发现新麻烦:
- 调用方每个接口的调用频次不一样,有的接口可能1秒内调用上百次,有的接口可能半个小时才会调用一次,不能把简单的把总失败的次数当作判断条件
- 服务的接口响应时间也是不一样的,有的接口可能1ms,有的接口可能是10s,也不能把TPS至来当作判断条件
找到可用率突破口,应该相对完美。可用率的计算方式:
某一个时间窗口内,接口调用成功次数的百分比(成功次数/总调用次数)
当可用率低于某个比例就认为该节点异常,挪到亚健康列表:
- 既考虑了高低频的调用接口
- 兼顾接口响应时间不同的问题
5 加完心跳机制,就万事大吉了吗
不是,因为检测程序所在机器和目标机器之间的网络可能还会故障,就会误判。你以为人家已经生病或者挂了,其实是心跳仪器坏了。
有个办法可减少误判率,把检测程序部署在多个机器里面,分布不同机架,甚至不同机房。因为网络同时故障概率低,所以只要任意一个检测程序实例访问目标机器正常,就可以说明该目标机器正常。
6 总结
健康检测帮助我们从连接列表里面过滤掉一些存在问题的节点,避免在发请求的时候选择出有问题的节点而影响业务。但是在设计健康检测方案的时候,我们不能简单地从TCP连接是否健康、心跳是否正常等简单维度考虑,因为健康检测的目的就是要保证“业务无损”,所以在设计方案的时候,我们可以加入业务请求可用率因素,这样能最大化地提升RPC接口可用率。
正常情况下,我们大概30S会发一次心跳请求,这个间隔一般不会太短,如果太短会给服务节点造成很大的压力。但是如果太长的话,又不能及时摘除有问题的节点。
除了在RPC框架里面我们会有采用定时“健康检测”,其实在其它分布式系统设计的时候也会用到“心跳探活”机制。
比如在应用监控系统设计的时候,需要对不健康的应用实例进行报警,好让运维人员及时处理。和咱们RPC的例子一样,在这个场景里,你也不能简单地依赖端口的连通性来判断应用是否存活,因为在端口连通正常的情况下,应用也可能僵死了。
那有啥其他办法能处理应用僵死的情况吗?我们可以让每个应用实例提供一个“健康检测”的URL,检测程序定时通过构造HTTP请求访问该URL,然后根据响应结果来进行存活判断,这样就可以防止僵死状态的误判。你想想,这不就是咱们前面讲到的心跳机制吗?
FAQ
有服务需通过银行安装在我们后台软件向银行分发交易。这个软件我们在使用过程发现会无故挂掉,为了能及时检测这种情况。 通过 openresty 写个小插件、通过 http 接口访问银行软件,查看银行软件是否挂了。然后接入钉钉 webhook,触发机器人报警。 由于当时是将 openresty 跟银行软件部署在同一台物理机器上去。某一天,整台机器挂了,报警机制当然也失效了。通过这件事,我现在每次部署服务时,会注意将服务部署在不同物理机器,防止意外。
成功次数/调用总次数,建议加上总次数阀值。如果2次,一次成功,一次失败,就可能误判。例如调用总数>10次以上,成次数/调用次数<50%,才比较准确
还是需要分场景对待的,没有最好的,只有最合适的。
心跳检测需要分两个纬度,一个机器本身的,一个是应用,单纬度肯定会出问题
之前使用过返回状态保存在MQ中,有专门的消费者去消费消息,其中要是失败率大于阈值,直接调用注册中心,下线该服务,同时使用agent机制,自动重启有问题的服务,之后要是还会出现失败,则报警发出,人工介入。
失败率统计是难点,需要考虑是否有网络设备坏或者不同idc问题
健康检测:调用方向服务方发送心跳检测,如果超过3次(阈值可以设置)未响应则认为服务节点挂掉。 会存在的遇到的问题 \1. 服务方会出现心跳正常响应,但是服务间歇性响应超时(亚健康状态),会导致调用方误判;可以用可用率的思路来解决。 \2. 调用方心跳机制出现问题,导致误判服务方挂掉;可以用调用方集群部署,其中一台调用显示正常则认为正常的办法来减少误判。 Dubbo通过IdleStateHandler设置定时任务,服务空闲发送心跳,实现健康检测 http://dubbo.apache.org/zh-cn/blog/dubbo-heartbeat-design.html
老师我认为心跳检测不应该接口的调用方来检测,这样的话调用接口的客户端量很大时,只是心跳检测就会把服务提供方的资源打满,而且当接口服务提供方很多时,客户端每个ip去健康检测也是不可能的
不一定要接口纬度,一般情况下多个接口直接会共享tcp连接的,可以用tcp连接纬度
1.上面说的基于失败率统计的方案,不就是熔断吗? 2.心跳检测,这个从调用方发心跳到服务方,会不会太重,基于熔断是不是就可以了 3.后面又说心跳检测可以放在多台机器去综合判断,刚刚不是说由调用方发起心跳吗?又变成第三方心跳检测了? 我理解这里有注册中心做心跳,再加熔断,失败重试等就可以了,不知道对不对
调用方到提供方之间心跳正常才能保证链路没有问题
心跳检测,单一纬度的标准始终差点意思。还是要结合业务场景,多维度判断,来保证结果准确性。例如 连续失败是最直接的纬度,可以综合考虑变更为 单位时间内失败次数,或单位次数的成功率
RPC 框架的心跳检测怎么做的呢?只听说过心跳检测这个概念,但在代码层面如何做,没有概念。看到老师在最后提到检测应用是否可用,可以在应用实例中开一个 url 供检测程序发 http 请求检测。但非应用级别的心跳检测也是这样做的吗?
定时发心跳消息是最简单的方法,通过判断是否正常响应
可用率这个指标具体怎么实现呢?因为一般使用RPC框架都是三方框架,我们是需要自己对三方接口进行重新实现吗?
看看有没有插件支持
1:心跳探活核心要点在于 第一及时 第二准确 第三全面,网络环境瞬息万变,需要考虑探活机器本身是否OK?探活的机制是否OK? 探活机制的设置应视情况而定,可以是节点级?也可以是服务级?为了防止因网络通信问题误判,可以设置多个探活节点。为了使探活更准确,可以假如可用率,调用次数等维度。
你以为别人挂了其实是自己挂了?
检测的目的防止别人挂
如果在多个机房部房部署探活程序,如果部分检测有问题 部分检测没有问题,这个状态需要怎么判断,又该怎样同步给所有探活程序?
多机房主要担心跨机房网络问题,只要有存活的就认为存活。
健康检查这套逻辑需要业务和运维的配合实现,业务要提供heath check的endpoint,运维要调用这个endpoint来查看服务的情况,所以在进一步,一些通用的框架会自动集成health check的功能并可以通过配置打开,当新服务上线的时候,监控检查功能会自动提供。
心跳检测文中说是调用方定时去看看提供方是否存活。但是平常好像不是这么做的,会有一台专门做健康检查的机器定时去调用健康检查接口,是说这两种方式都是可以的么 觉得第一种会不会做起来成本比较高?
用其他机器去检查反映出来的结果不一定准,可能中间经过的网络设备不一样。
标签:调用,服务,框架,检测,接口,RPC,心跳,节点 From: https://blog.51cto.com/u_11440114/6040293