【Spring Cloud】 Nacos注册中心 服务端注册服务流程源码

1.处理客户端请求

找到 InstanceController

@RequestMapping(UtilsAndCommons.NACOS_NAMING_CONTEXT + "/instance")

image-20220403201854097

image-20220403225439877

这个地址就是我们客服端注册服务的地址 客服端服务的注册流程可以去看这篇博文【Spring Cloud 】 Nacos 服务注册过程 源码_秋日的晚霞的博客-CSDN博客

image-20220403202018706

看controller方法

image-20220403202126755

上来先拿到 namespaceId 和 serviceName

image-20220403211359081

默认命名空间 : public

分组 : DEFAULT_GROUP@@ + 客户端注册的服务名

解析 instance 对象 封装了服务提供者的参数

image-20220403211543798

接下来注册服务 调用 registerInstance 方法

serviceManager.registerInstance(namespaceId, serviceName, instance);

跟进去 来到

com.alibaba.nacos.naming.core.ServiceManager. registerInstance

2.注册实例

com.alibaba.nacos.naming.core.ServiceManager. registerInstance

image-20220403211736900

首先调用 createEmptyService 在注册表里面创建一个空的service结构

跟进去 来到 createServiceIfAbsent 方法

image-20220403211850798

第一次进来 Service service = getService(namespaceId, serviceName) 肯定是null

进入if逻辑

创建了一个新的 service 对象 设置 服务名,命名空间,分组名 记录修改时间

image-20220403212012868

来到 putServiceAndInit(service)

image-20220403212208988

跟进去

image-20220403212312468

`serviceMap 双层map

image-20220403212330991

看下执行完putService后serviceMap 的数据

image-20220403212509535

外层map的key为 namespace value也为一个map,内层map的key为 groupName 分组名 value为 service对象

image-20220403213220448

3.服务实例心跳检查

回到 putServiceAndInit 方法

接着执行 service.init() 方法

image-20220403213346275

来到 com.alibaba.nacos.naming.core.Service.init 方法

image-20220403213427424

执行一个定时任务 第一次5秒后执行,然后每隔5秒执行一次 看下线程任务时什么 跟进task

image-20220403213506026

线程任务

com.alibaba.nacos.naming.healthcheck.ClientBeatCheckTask.run()

image-20220403214111904

`instance.getInstanceHeartBeatTimeOut() 值是15秒

如果当前时间 减去 实例最后一次心跳时间 大于 15秒 进 if判断 将健康状态设置为false

也就是说 HealthCheckReactor.scheduleCheck(clientBeatCheckTask) 开启了一个心跳检测所有的实例健康状态,一旦距离上一次心跳检测时间超过15 s 将健康状态设置为false

4.发送消息给队列

回到 registerInstance 方法

继续看 addInstance(namespaceId, serviceName, instance.isEphemeral(), instance)

跟进去来到 com.alibaba.nacos.naming.core.ServiceManager.addInstance

image-20220403215331621

首先生成一个实例key 值 com.alibaba.nacos.naming.iplist.ephemeral.public##DEFAULT_GROUP@@nacos-test

String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);

生成实例集合

List<Instance> instanceList = addIpAddresses(service, ephemeral, ips)

new Instances 封装集合数据

Instances instances = new Instances();
            instances.setInstanceList(instanceList);

image-20220403215727082

来到consistencyService.put(key, instances) 方法

来到 com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.put

image-20220403215853870

`Record value 就是封装Instance 的 Instances instances Instances 继承 Record 多态的体现

image-20220403215936351

跟进 onPut 方法

来到 com.alibaba.nacos.naming.consistency.ephemeral.distro.DistroConsistencyServiceImpl.onPut 方法

image-20220403220259020

又new 了一个 Datum 对象 Datum value为 Instances key为前面生成的实例key

image-20220403220335697

然后又把Datum 丢到 DataStore的map 中

image-20220403220530440

最后调用 notifier.addTask方法 跟进addTask方法

image-20220403220713239

往services中put key为 datumKey value为空字符串

private ConcurrentHashMap<String, String> services = new ConcurrentHashMap<>(10 * 1024)

最后调用task.offer将这个key和执行的动作扔到内存队列中 然后返回null了 方法结束了

难度到这就结束了吗?

5.监听队列,取出新服务实例

我们看下这个tasks所属的类

image-20220403221239669

`Notifier 也是一个线程任务类 ,那么我们看下它的线程任务

image-20220403221332029

从队列中拿到datumKey 调用handle 方法

image-20220403221711873

打个断点,看下执行流程,重新注册服务

来到 listener.onChange

image-20220403221823581

onChange方法的两个参数 一个为 datumKey 一个为 Instances

可以看到 又把服务实例集合通过datumKey 从dataStore中拿出来了

跟进去 来到com.alibaba.nacos.naming.core.Service.onChange方法

遍历所有的instance实例 设置合理的权重

image-20220403221940620

然后调用 updateIPs 方法 跟进去

上来又new了一个 ipMap 好多map

image-20220403222121424

遍历所有的instance

image-20220403222745703

添加到 clusterIPs 中

再到下面一个for遍历

image-20220403222856196

调用 clusterMap.get(entry.getKey()).updateIps(entryIPs, ephemeral) 拿到cluster调用updateIps 方法

来到 com.alibaba.nacos.naming.core.Cluster.updateIps

6.写时复制,注册新服务实例到注册表中

image-20220403223050216

ephemeral 上个方法传进来的参数,为true,则将 ephemeralInstances 的赋值给 Set toUpdateInstances

Set<Instance> toUpdateInstances = ephemeral ? ephemeralInstances : persistentInstances;

那这个 ephemeralInstances 是什么呢

它就是 Cluster存放服务实例的set旧集合

private Set<Instance> ephemeralInstances = new HashSet<>();

将旧的实例集合赋值给了 toUpdateInstances 接下来就是操作新注册的服务实例,最后再将所有的最新的实例赋值给toUpdateInstances,

然后再将 toUpdateInstances 的值赋值给 ephemeralInstances 至此一个新的服务实例注册完毕

image-20220403223554758

这就是一种写时复制的思想,将注册好的服务实例,复制一份,操作这份复制的数据,操作完毕后,再将复制的数据替换原来的旧数据

目的是为了解决并发冲突,在写的过程中,其他线程读到的还是旧数据,等真正写完之后再将数据更新回去。不会读到脏数据.保证读到的服务实例都是可用的

猜你喜欢

转载自blog.csdn.net/JAVAlife2021/article/details/123946613
今日推荐