InstanceController
进入InstanceController类,可以看到一个register方法,就是服务注册的方法了:
点击查看代码
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {
//获取namespaceId
final String namespaceId = WebUtils
.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
//尝试获取serviceName,其格式为 group_name@@service_name
final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
// 解析出实例信息,封装为Instance对象
final Instance instance = parseInstance(request);
// 注册实例
serviceManager.registerInstance(namespaceId, serviceName, instance);
return "ok";
}
而其中的registerInstance方法就是注册服务实例的方法:
我们可以继续跟进createEmptyService这个方法:
再继续跟进createServiceIfAbsent方法如果缺席就创建,也就是说不存在就创建,第一次来就创建。
进来createServiceIfAbsent以后,首先尝试从注册表中获取一次服务,跟进去
如果不为null则代表有,也就是组Map有,通过这个chooseServiceMap方法得到组map,跟进来:
其实就是从服务列表里面根据命名空间id获取Map。接着通过组的服务名获取Service:
如果为空,说明该服务是第一次注册,创建新的Service:
这个groupName其实是serviceName的一部分是两个@符号隔开的,我们再第一次创建服务的时候传进去的cluster是null,随意上图中的cluster肯定是null,里面的代码也即不会执行。
记下来就是putServiceAndInit,服务创建好就要把其put进来。继续跟进:
第一个方法putService,继续:
首先是判断一下serviceMap注册表是否有这个服务,这里其实是为了提高性能(单例模式就有这种判断),如果没有就设置一把锁,出于线程安全考虑,当我在创建的时候别人就不要创建了,否则出现并发写修改的安全问题,然后在这个同步代码块里面又判断一次有没有,其实就是为了保证线程安全,如果确实没有就往注册表里面put:
但这个ConcurrentSkipListMap并不是在同步代码块里填的,而是出来这个同步代码块,在这个:
serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);
这句代码填入的真正的服务,这样确保我们的服务一定能写进去。
其中的putIfAbsent表示:如果不存在才put,如果并发情况下有其他人put进来了,就不往里面put了。这就是保证put动作的有效性。
然后service已经有了,我们取一次,并进行初始化,其实是进行一个健康检测:
接下来进行一致性服务的监听:consistencyService
监听服务的变更:
到这里,putServiceAndInit这个服务注册和初始化就做完了:
但是要记得putServiceAndInit方法里面的两个监听器。后续分析会用到
继续往下执行createEmptyService后,我们需要添加实例到service中addInstance这个方法,跟进:
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);记住这些参数。
比较重要的是上图的同步代码块
点击查看代码
synchronized (service) {
// 拷贝注册表中旧的实例列表,然后结合新注册的实例,得到最终的实例列表
List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
//封装实例列表到instances
Instances instances = new Instances();
instances.setInstanceList(instanceList);
//更新注册表(更新本地注册表、同步给nacos集群中的其他节点)
consistencyService.put(key, instances);
}
因为ConsistencyService本身是个接口,它有很多的实现,Ctrl+h可以看到如下图: