
微服务:剖析一下源码,Nacos的健康检查竟如此简单
前言
前面我们多次提到Nacos的健康检查,比如《微服务之:服务挂的太干脆,Nacos还没反应过来,怎么办?》一文中还对健康检查进行了自定义调优。那么,Nacos的健康检查和心跳机制到底是如何实现的呢?在项目实践中是否又可以参考Nacos的健康检查机制,运用于其他地方呢?
这篇文章,就带大家来揭开Nacos健康检查机制的面纱。
Nacos的健康检查
Nacos中临时实例基于心跳上报方式维持活性,基本的健康检查流程基本如下:Nacos客户端会维护一个定时任务,每隔5秒发送一次心跳请求,以确保自己处于活跃状态。Nacos服务端在15秒内如果没收到客户端的心跳请求,会将该实例设置为不健康,在30秒内没收到心跳,会将这个临时实例摘除。
原理很简单,关于代码层的实现,下面来就逐步来进行解析。
客户端的心跳
实例基于心跳上报的形式来维持活性,当然就离不开心跳功能的实现了。这里以客户端心跳实现为基准来进行分析。
Spring Cloud提供了一个标准接口ServiceRegistry,Nacos对应的实现类为NacosServiceRegistry。Spring Cloud项目启动时会实例化NacosServiceRegistry,并调用它的register方法来进行实例的注册。
在该方法中有两处需要注意,第一处是构建Instance的getNacosInstanceFromRegistration方法,该方法内会设置Instance的元数据(metadata),通过源元数据可以配置服务器端健康检查的参数。比如,在Spring Cloud中配置的如下参数,都可以通过元数据项在服务注册时传递给Nacos的服务端。
其中的heart-beat-interval、heart-beat-timeout、ip-delete-timeout这些健康检查的参数,都是基于元数据上报上去的。
register方法的第二处就是调用NamingService#registerInstance来进行实例的注册。NamingService是由Nacos的客户端提供,也就是说Nacos客户端的心跳本身是由Nacos生态提供的。
在registerInstance方法中最终会调用到下面的方法:
其中BeatInfo#addBeatInfo便是进行心跳处理的入口。当然,前提条件是当前的实例需要是临时(瞬时)实例。
对应的方法实现如下:
在倒数第二行可以看到,客户端是通过定时任务来处理心跳的,具体的心跳请求由BeatTask完成。定时任务的执行频次,封装在BeatInfo,回退往上看,会发现BeatInfo的Period来源于Instance#getInstanceHeartBeatInterval()。该方法具体实现如下:
可以看出定时任务的执行间隔就是配置的metadata中的数据preserved.heart.beat.interval,与上面提到配置heart-beat-interval本质是一回事,默认是5秒。
BeatTask类具体实现如下:
在run方法中通过NamingProxy#sendBeat完成了心跳请求的发送,而在run方法的最后,再次开启了一个定时任务,这样周期性的进行心跳请求。
NamingProxy#sendBeat方法实现如下:
实际上,就是调用了Nacos服务端提供的"/nacos/v1/ns/instance/beat"服务。
在客户端的常量类Constants中定义了心跳相关的默认参数:
这样就呼应了最开始说的Nacos健康检查机制的几个时间维度。
服务端接收心跳
分析客户端的过程中已经可以看出请求的是/nacos/v1/ns/instance/beat这个服务。Nacos服务端是在Naming项目中的InstanceController中实现的。
服务端在接收到请求时,主要做了两件事:第一,如果发送心跳的实例不存在,则将其进行注册;第二,调用其Service的processClientBeat方法进行心跳处理。
processClientBeat方法实现如下:
ClientBeatProcessor同样是一个实现了Runnable的Task,通过HealthCheckReactor定义的scheduleNow方法进行立即执行。
scheduleNow方法实现:
再来看看ClientBeatProcessor中对具体任务的实现:
在run方法中先检查了发送心跳的实例和IP是否一致,如果一致则更新最后一次心跳时间。同时,如果该实例之前未被标记且处于不健康状态,则将其改为健康状态,并将变动通过PushService提供事件机制进行发布。事件是由Spring的ApplicationContext进行发布,事件为ServiceChangeEvent。
通过上述心跳操作,Nacos服务端的实例的健康状态和最后心跳时间已经被刷新。那么,如果没有收到心跳时,服务器端又是如何判断呢?
服务端心跳检查
客户端发起心跳,服务器端来检查客户端的心跳是否正常,或者说对应的实例中的心跳更新时间是否正常。
服务器端心跳的触发是在服务实例注册时触发的,同样在InstanceController中,register注册实现如下:
心跳相关实现在第一次创建空的Service中实现,最终会调到如下方法:
在putServiceAndInit方法中对Service进行初始化:
service.init()方法实现:
延迟5秒执行,每5秒检查一次。
在init方法的第一行便可以看到执行健康检查的Task,具体Task是由ClientBeatCheckTask来实现,对应的run方法核心代码如下:
在第一个for循环中,先判断当前时间与上次心跳时间的间隔是否大于超时时间。如果实例已经超时,且为被标记,且健康状态为健康,则将健康状态设置为不健康,同时发布状态变化的事件。
在第二个for循环中,如果实例已经被标记则跳出循环。如果未标记,同时当前时间与上次心跳时间的间隔大于删除IP时间,则将对应的实例删除。
小结
通过本文的源码分析,我们从Spring Cloud开始,追踪到Nacos Client中的心跳时间,再追踪到Nacos服务端接收心跳的实现和检查实例是否健康的实现。想必通过整个源码的梳理,你已经对整个Nacos心跳的实现有所了解。关注我,持续更新Nacos的最新干货。
文章转载自公众号:程序员新视界
