Oracle将监听服务注册到集群中,Nacos(2)——Nacos服务端服务注册处理

1.3 服务注册

1.3.1 服务注册流程

创建完service后, 会重新从缓存中获取service,之后便会向service中注册服务,ServiceManager#addInstance()方法:

1public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips) throws NacosException{

2    // 生成服务唯一的key:"com.alibaba.nacos.naming.iplist."+(是否为瞬时节点)"ephemeral"+"namespaceId"+"##"+"service:spring.application.name"

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

4    // 获取服务列表,需要将实例放入到指定的服务下

5    Service service = getService(namespaceId, serviceName);

6    // 进行同步操作

7    synchronized (service) {

8        // 添加到service所在的实例列表下,返回的是更新后新的实例列表

9        List instanceList = addIpAddresses(service, ephemeral, ips);

10        // 创建新的Instances持久化对象

11        Instances instances = new Instances();

12        instances.setInstanceList(instanceList);

13        // 使用一致性协议,存储更新后的实例数据

14        consistencyService.put(key, instances);

15    }

16}

生成服务唯一的key,命名格式为"com.alibaba.nacos.naming.iplist."+"ephemeral."+namespaceId+"##"+"服务的spring.application.name",举一个例子可以是:"com.alibaba.nacos.naming.iplist.ephemeral.public##sunshine-taurus"。

获取需要将服务注册到的service。

注册服务时,需要对service进行同步操作。

将注册的服务实例添加到service的实例列表中,并返回最新的service服务列表。

使用Distro协议,存储更新后的实例数据。

注意,构建的key不是为了注册的服务实例而用,而是为了service的服务列表而用。

因为每个服务spring.application.name是相同的。

1.3.2 注册服务到服务列表

通过调用ServiceManager#addIpAddress()方法,将注册服务添加到当前service的服务列表中:

1public List addIpAddresses(Service service, boolean ephemeral, Instance... ips) throws NacosException{

2    return updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, ips);

3}

使用UPDATE_INSTANCE_ACTION_ADD添加操作,调用ServiceManager#updateIpAddresses()方法更新服务实例列表。

接下来会对ServiceManager#updateIpAddresses()方法进行逐步分析。

1.3.2.1 获取已有服务实例

首先,Nacos服务端会从一致性协议,对于临时节点,也就是从内存中获取指定service临时实例节点数据:

1// 从一致性协议中读取当前service的实例数据,如果是临时节点使用Distro协议,如果是持久节点使用Raft协议

2Datum datum = consistencyService.get(KeyBuilder.buildInstanceListKey(service.getNamespaceId(), service.getName(), ephemeral));

3// 获取当前Nacos上所有的临时实例列表

4List currentIPs = service.allIPs(ephemeral);

5Map currentInstances = new HashMap<>(currentIPs.size());

6Set currentInstanceIds = Sets.newHashSet();

7// 遍历当前所有的服务列表,放入到"ip:port"=>instance字典表,和实例ID集合中

8for (Instance instance : currentIPs) {

9    currentInstances.put(instance.toIPAddr(), instance);

10    currentInstanceIds.add(instance.getInstanceId());

11}

首先,从缓存中获取指定serviceKey的实例信息。

接着,从service缓存中获取所有临时节点信息。

将service缓存中的实例信息,转换为"ip:port"=>instance集合,instanceId集合。

1.3.2.2 更新缓存信息

接着,Nacos会以一致性协议中取出的实例数据为主,缓存的中的数据为辅,对实例数据进行更新:

1Map instanceMap;

2if (datum != null) {

3    // 当前存在指定service的数据,则对集群的数据进行更新后,使用此缓存

4    instanceMap = setValid(((Instances) datum.value).getInstanceList(), currentInstances);

5} else {

6    // 否则会创建一个新缓存

7    instanceMap = new HashMap<>(ips.length);

8}

接着调用ServiceManager#setValid()方法对数据进行更新:

1private Map setValid(List oldInstances, Map map){

2

3    Map instanceMap = new HashMap<>(oldInstances.size());

4    // 迭代缓存中所有指定service一致性协议数据中所有的实例

5    for (Instance instance : oldInstances) {

6        // 从集群缓存中获取实例信息

7        Instance instance1 = map.get(instance.toIPAddr());

8        if (instance1 != null) {

9            // 使用集群中信息,更新数据中的实例信息

10            instance.setHealthy(instance1.isHealthy());

11            instance.setLastBeat(instance1.getLastBeat());

12        }

13        // 重新放入到集群实例缓存中

14        instanceMap.put(instance.getDatumKey(), instance);

15    }

16    return instanceMap;

17}

在ServiceManager#setValid()方法中,主要做了:

遍历一致性协议中存储的实例列表。

使用service缓存中存储的实例信息,更新一致性协议中存储的实例信息。

返回更新后的一致性协议中存储的实例列表。

此时,已有数据列表的信息已经是最新的了。

1.3.2.3 添加实例

接下来,需要遍历所有需要添加的实例,添加到已有的实例集合中:

1for (Instance instance : ips) {

2    // 如果需要更新的实例所在集群不存在,则需要创建一个新集群

3    if (!service.getClusterMap().containsKey(instance.getClusterName())) {

4        Cluster cluster = new Cluster(instance.getClusterName(), service);

5        // 初始化集群,开始进行集群的健康检查任务

6        cluster.init();

7        // 将新创建的集群添加到指定service中

8        service.getClusterMap().put(instance.getClusterName(), cluster);

9        Loggers.SRV_LOG.warn("cluster: {} not found, ip: {}, will create new cluster with default configuration.",

10                             instance.getClusterName(), instance.toJSON());

11    }

12    if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {

13        // 如果是移除操作,则移除当前的实例

14        instanceMap.remove(instance.getDatumKey());

15    } else {

16        // 其他操作,也就是添加或更新,则直接覆盖缓存的实例对象

17        instance.setInstanceId(instance.generateInstanceId(currentInstanceIds));

18        instanceMap.put(instance.getDatumKey(), instance);

19    }

20

21}

22// 如果在添加操作后,指定service中没有实例,证明出现了异常,需要排查问题

23if (instanceMap.size() <= 0 && UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD.equals(action)) {

24    throw new IllegalArgumentException("ip list can not be empty, service: " + service.getName() + ", ip list: "

25                                       + JSON.toJSONString(instanceMap.values()));

26}

如果需要注册服务实例所在的cluster不存在,则会创建一个新的cluster,并关联到service上。

如果此次更新操作类型是移除操作,则从已有实例集合中,移除指定实例。

除了移除操作,其他情况下只能是添加操作,会生成实例的新instanceId,紧接着添加或更新实例集合中的实例数据。

在ServiceManager#updateIpAddresses()方法中开始部分创建的instanceId集合,用于在生成新实例的instanceId时,为SnowFlake算法提供不重复的序号。

注册完服务实例后,则返回更新后实例列表。

1.3.3 更新一致性协议数据

在注册服务实例过程中,实例列表是从一致性协议缓存中获取的。

但是在注册完成之后,并没有在ServiceManager#updateIpAddresses()方法中并没有对一致性协议中的数据进行更新,我个人认为是方法职责问题。

所以在注册完服务实例后,紧接着就是对新的service服务列表的数据进行一致性协议的更新。

注册成功后,即完成服务的service注册。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值