客户端服务发现原理源码
客户端服务发现
上一篇Nacos服务注册原理源码文章中,在Nacos提供的注册中心测试单元中代码,我们只讲到了服务的注册,注册完后,服务获取方法没有往下展开分析,今天我们主要就是讲解这个方法。
这个方法有很多重载方法,调用时加上一些默认请求参数比如:集群列表、是否订阅、分组 最终会调到下面这个方法中,通过代码能看到如果是订阅模式,会先从本地获取,如果本地不存在,会进行服务订阅,订阅时会返回对应的服务数据,如果是非订阅模式的话,调用服务端的查询接口进行查询。
服务订阅并获取
根据常识也能知道如果不从Nacos服务端获取服务信息,第一次本地缓存肯定不存在对应的服务信息,所以先分析订阅并获取,通过NacosNamingService#init
可以知道会走到NamingClientProxyDelegate#subscribe
该方法代码如下
通过group@@serviceName先从ServiceInfoHolder对象中服获取对应的服务信息,不存在的话就通过grpc向服务端发送请求,返回结果后,如果是订阅模式,会开启定时任务更新本地缓存,定时执行的逻辑如下:
其实订阅的每个服务都为他们开了个定时任务来同步注册中心的服务实例信息,具体逻辑为每隔一段时间(默认6s)从本地缓存中获取服务的信息,判断是否过期,过期重新请求服务端,进行缓存处理,修改最新的服务获取时间。操作缓存方法processServiceInfo
,这个方法比较重要,我们来看下它具体的代码
可以看到它将获取到的ServiceInfo存到了一个名为serviceInfoMap
的集合中,然后判断服务的是否发生变更,如果变更过发现服务变更的事件,并将新的ServiceInfo存到本地文件中。
服务端收到订阅服务请求后面会分析。
服务本地内存获取
其实理解了上面讲的订阅,其实这个就很简单了,定时任务将注册中心同步过来的服务存放到ServiceInfoHolder
下面这个属性中
private final ConcurrentMap<String, ServiceInfo> serviceInfoMap;
我们看下获取服务的方法
通过代码发现,如果没有开启故障转移开关,就会从集合汇总拿,故障转移后面会讲到,到这里客户端的服务发现原理基本讲完了。
服务端服务订阅处理
服务订阅的处理方法是SubscribeServiceRequestHandler#handle
,先获取服务的信息,如果是订阅模式获取,会发布一个服务订阅的事件,事件处理核心就是将该客户端添加到ClientServiceIndexesManager#subscriberIndexes
缓存中(后期如果订阅的服务发生变更,会通知到客户端)再通过传递的serviceName
拿到服务实例,拿着Service
对象去serviceStorage中取数据
看过上一篇务 注册原理源码的应该知道,我们在注册的时候先构建了单例的Service
,服务注册到了ClientServiceIndexesManager#publisherIndexes
map集合中,存储结构如下图,之前不是说map集合才是注册表嘛,但是这里怎么去serviceStorage中获取啦,其实该对象最终还是去注册表中取的我们接着往下看。
下面代码就是获取服务信息的方法,为了方便看我把这些方法都放在一张图中,如果ServiceStorage#serviceDataIndexes
缓存中存在该服务实例,则直接从缓存中取,不存在就从serviceIndexesManager(注册表)
中取,取出后设置到ServiceStorage#serviceDataIndexes
中,并返回
补一个服务端简单服务获取的流程图:
客户端故障转移
如果Nacos服务端宕机了,为了保证服务的正常调用,Nacos提供了故障转移机制, ServiceInfoHolder
对象在实例化的时候,初始化了缓存目录同时也创建了故障转移的对象FailoverReactor
我们看下故障转移对象初始化做了些啥
通过代码,发现故障转移对象还持有了本地缓存ServiceInfoHolder
对象,创建了一个线程池、还进行了对应的初始化操作。我们看下初始化方法的代码,初始化方法中就是创建了三个定时任务,第一个通过名称我们也能才到,该定时任务用来判断是否需要开启故障转移的,至于第二个、第三个
细心的同学会发现,第二个任务和第三个任务实际上是一个,只是触发的时机不同而已,一个30分钟执行一次,时间间隔为24小时,另一个是10s后执行一次,只会执行一次。
我们先看第二、三个定时任务,这两个任务执行的都是同一段代码,第一个定时任务读取的内容其实是第二三个任务写到故障文件中的。故障转移对象先从本地缓存中拿出服务的实例信息,然后写到了磁盘中。
再来看第一个定时任务,发现它5s中会去读取故障转移开关文件,1表示开启故障转移,0代表关闭故障转移。实际上他们都是往FailoverReactor#switchParams
该集合中添加了一个属性值failover-mode
,该属性就是之前在本地获取服务实例信息时用来判断是否开启了故障转移功能。
如果开启了故障转移,会去读取故障转移文件,中间读取的代码忽略,我们只需要关注读取到的实例信息放的位置,它会将实例信息放到FailoverReactor#serviceMap
中,当获取服务发现开启了故障转移时,就会从该集合中来读取服务信息。