本文转载自微信公众号《捉虫大师》,作者捉虫大师。转载本文请联系捕虫大师公众号。几个月前,我在这篇文章里给自己挖了一个坑?:文中提到的实际问题是服务探索,今天就来填这个坑。在微服务架构下,服务提供者(Provider)一般有多个节点,消费者(Consumer)根据负载均衡算法选择一个健康的节点进行调用。识别Provider节点是否健康是服务发现要讨论的内容。健康节点可以定义为可以正常响应Consumer请求的节点。不健康的节点自然无法响应Consumer的请求。不健康的原因可能是物理停电、网络断开、硬件故障、网络延迟、进程异常退出等。或者进程无法处理请求。一句话概括就是Provider节点无法处理请求,直到流量被移除。可分为三类:系统异常:如电源故障、网络断开、其他硬件故障或操作系统异常退出。-9进程无法处理请求:端口还在,但服务无法正常响应。比如FullGC时,Provider节点的状态只有healthy和unhealthy。从健康到不健康,叫做死亡检测,从不健康到健康,叫做检测,一般我们不把它分这么细,叫探火。至于谁来访问,可能是Consumer,也可能是注册中心,甚至是单独的检测组件。我们将从探测的始作俑者,罗列目前主流的探测方式。被动检测消费者最简单的方法是在消费者端进行探测。如果消费者调用提供者报错,消费者将移除提供者。为了防止偶尔出现的网络抖动或者其他干扰,可以设置一个时间窗口,窗口内的故障可以达到N次。第二次排除,过了这段时间,将Provider重置为正常。这种方式的典型代表就是Nginx。Nginx可以配置失败多长时间,失败多少次就认为Provider不可用。失败可以是连接失败或一些http状态代码(例如4xx、5xx)。该解决方案的缺点是显而易见的。它需要真实的流量来检测。如果配置失败,继续转发给下一个Provider,那么从时间窗口开始的一段时间内,耗时会增加。有影响。消费者主动检测和消费者被动健康检测的主要问题在于它使用的是真实的流量检测。其实可以通过使用旁路的方法检测来避免。阿里的Tengine开源了一个nginx_upstream_check_module模块来做主动健康检查。这个旁路总是可以检测到Provider。当检测到异常时,将其标记为不可用,并且不再向Provider发送请求。如果检测到Provider是健康的,它将被标记为健康。消费者端的探索很少在RPC框架中实现。不知道是出于怎样的考虑。事实上,一些公司在消费者端增加了检测机制。比如爱奇艺在Dubbo的消费端加入了检测机制。其实我公司内部的RPC框架在消费者端也有服务探索。参考《爱奇艺在 Dubbo 生态下的微服务架构实践》https://developer.aliyun.com/article/771495但官方并未集成Dubbo。至于为什么,我也在github上问过,也没人回复~Provider报心跳。当有注册中心时,explore这个任务可以交给注册中心。最简单的,我们想到了保持心跳的策略。Provider注册成功后,会不断向注册中心发送心跳包。注册中心定期检查Provider。如果长时间没有发送心跳包,将不可用并通知Consumer,恢复它并通知心跳是否恢复。有些组件也支持这种更新特性,比如etcd、redis等都可以搭建类似的系统。这种方式的代表是Nacos1.x版本中的临时实例。慢慢你会发现,这种方式去除故障节点的及时性与资源的使用是正相关的。如果想要更好的时效性,就必须缩短心跳间隔,这样会增加心跳请求量。每次心跳都更新节点的最后一次心跳时间占用大量资源。Provider和注册中心保持会话。为了解决心跳请求占用大量资源的问题,我们想到TCP连接不就是一种天然的健康检查机制吗?是否可以仅依赖TCP连接?这是上篇文章埋的坑,再总结一下本文中TCP连接断开的场景?:如果进程终止了,不管是正常还是异常,只要操作系统还在,TCP连接将正确断开。如果是电源故障、网络断开或其他因素导致操作系统挂掉,可能是网络没有正确断开。如果此时注册中心向Provider发送数据,可以及时检测到Provider异常并断开连接。如果注册中心不向Provider发送数据,则无法及时检测到连接断开。即使配置了TCPKeepAlive,也需要2小时左右,2小时肯定不能接受。为了防止这种情况的发生,光靠TCP是不够的,必须要在应用层实现心跳检测。为了节省资源,心跳包可以设计的小一些,发送频率不需要那么高。通常,它可以在1分钟内感应到。因为只有断电、断网或者硬件故障才会导致不知不觉断线,所以这个比例很小。您可以参考下图。图中的数据虽然是我杜撰的,但几乎脱不了干系。可以看出系统异常只占1%,而这1%中未发送的数据可能更少,可以认为这个概率很小。这种方式比较常见,比如Dubbo使用的Zookeeper,Nacos2.x版本(gRPC)的临时实例,SOFARegistry等都是采用这种方式。其中,SOFARegistry比较有意思。它提出了一种连接敏感的长期连接。乍一看,我还以为它用了什么黑科技。看完代码发现是TCP连接加应用层心跳检测。这些都封装在SOFABolt通信框架中。.参考《海量数据下的注册中心 - SOFARegistry 架构介绍》https://mp.weixin.qq.com/s/mZo7Dg6gfNqXoetaqgwMww但是这种方式也有一个明显的缺点。如果网络不好,TCP连接比较容易断开,会导致节点频繁掉线。注册中心主动检测除了上述方法外,还有一种方式是注册中心(即使是一个单独的组件)主动检测Provider,这与消费者主动检测类似,只是检测任务是移交给注册中心或单独的组件。主动检测最大的好处就是可以扩展出非常丰富的检测方式,比如最常见的检测端口是否存活,或者Provider的某个http接口的返回是否符合预期,甚至可以扩展到检测MySQL和Redis等协议。这也是一种可以解决服务假死的探测方法,也就是Nacos中探测常驻实例使用的方法。但实际使用该方法时,需要考虑主动检测组件的高可用性。高可用必须有一个副本,可以采用主备方式。如果单机存在性能瓶颈,需要分布式检测,主备可能不合适。必须有一个分布式协调器,这个要讲很久。但是在这里你知道上面有这样的东西。检测活动要考虑三个指标:能不能检测到?-何时检测到该功能?-时效性会不会错?-最稳定的功能是有语义的主动检测,时效性最强的是长连接会话持久化。如果稳定性不好,我们可以说谁强谁弱,但是一般一个集群都会设置一个检测和剔除比例,比如最多剔除50%的机器,防止检测错误导致所有节点离线。这也是一种底线策略。
