先看下服务端处理的InstanceController概览图:
InstanceController概览图
当客户端调用reqApi向服务器发送Http PUT心跳请求,URI是/nacos/v1/ns/instance/beat,对应的controller就是InstanceController的beat方法。beat方法首先检查Instance是否存在,Instance不存在的情况:比如客户端超过30秒没有向服务器发送心跳,这时服务端会删除这个实例,30秒后再发送心跳时就会出现Instance不存在的情况。
说回来,如果实例不存在,并且客户端也没有发送BeatInfo包,这时会返回给客户端一个实例不存在的错误;如果Instance实例不存在,但客户端发送了BeatInfo包,这时能拿到BeatInfo里的信息,beat方法就会创建Instance对象,并调用ServiceManager类的registerInstance方法注册服务实例。
这个BeatInfo包就是客户端发送心跳时判断的参数:Light_Beat_Enabled,如果这个参数是false就发送,默认值就是false,这个逻辑在讲客户端时讲过一遍了。
我们具体看下InstanceController类beat方法代码:
InstanceController类beat方法-1
InstanceController类beat方法-2
InstanceController类beat方法-3
ServiceManager类getInstance方法涉及到Service、Cluster、Instance的内存数据结构,服务注册就是把数据写到这几个类的关键变量中,服务发现也是从这几个类的变量中查找,所以这几个类的数据结构非常重要。
首先,Service服务是存储在ServiceManager类的serviceMap变量里,类型是:
Map> serviceMap = new ConcurrentHashMap<>();
Map里key含义如下:Map(namespace, Map(group::serviceName, Service))
serviceMap是个双层Map结构,先用namespace拿到该名字空间的所有Service,再用serviceName拿到Service对象。
Cluster集群存在Service类的clusterMap变量里,类型是:Map clusterMap = new HashMap<>();Map里key含义:Map;clusterMap中存储该service所有的集群,使用clustername可以直接拿到Cluster对象实例。
Instance分为临时服务和永久服务,是分开存的,在Cluster类中的2个变量里:
Set persistentInstances = new HashSet<>() //永久服务实例
Set ephemeralInstances = new HashSet<>() //临时服务实例
persistentInstances中是该集群中所有永久服务的Instance的集合;ephemeralInstances 是该集合中所有临时服务的Instance的集合,Instance对象是对应Ip、port、healthy等属性。
ServiceManager类getInstance就是从上面几个变量结构中查找到具体的Instance实例,如果数据结构中没有就创建一个Instance对象,然后调用ServiceManager类的registerInstance方法注册实例,registerInstance注册实例就是向那几个数据结构Map中塞数据,然后把注册数据再同步到nacos服务器集群其他节点。这个registerInstance注册服务逻辑后面会介绍。
接着看InstanceController类beat方法最后一段代码:
InstanceController类beat方法-4
首先组装心跳基本属性到clientBeat对象,传给Service的processClientBeat方法,processClientBeat拿到ip和port就可以找到Instance,然后做该Instance的心跳续期。下面是给客户端设置心跳间隔时间和是否发送心跳BeatInfo对象,这2个属性设置后会覆盖默认属性(心跳间隔默认是5秒,默认发送BeatInfo),设置后返回给客户端后,客户端就按这个最新的属性设置发送心跳。之前在客户端上报心跳逻辑时,也详细讲过这2个属性。上面代码可以看出设置这2个属性的值是从nacos控制台拿到的,用户在控制台上可以设置这2个属性的metadata元数据,这个之后可以看下怎么在控制台设置这些元数据。
好了,InstanceController类beat方法我们说完了。
我们接着看下Service类的处理心跳方法processClientBeat,这个是服务端心跳的核心逻辑,见下图,只剩下最后这一个了:
InstanceController类beat流程
看下processClientBeat代码:
processClientBeat方法
这段代码很简单,创建一个处理心跳的处理器任务ClientBeatProcessor,然后扔到线程池中立即执行,见scheduleNow方法。我们看下ClientBeatProcessor这个线程怎么处理心跳的?
先看第一段,取出该服务的必要属性,主要是取出该服务的所有Instance,后面做遍历找要上报心跳的Instance。
ClientBeatProcessor线程-1
再看最后一段:
ClientBeatProcessor线程-2
这段是心跳服务续约的核心逻辑,先在所有Instance里找到匹配上报心跳的Instance,然后设置该实例的最后上报时间,nacos就是根据这个属性判断15秒心跳超时、30秒删除实例。然后刷新下实例的健康属性,比如上次心跳15超时,这个health属性会被设置成false,这次上报后会重置成健康true。最后调用PushService组件,该组件是通过udp协议向客户端推送服务变更信息,这个后面有详细说明。