一、springboot启动日志报NacosServiceRegistry
springboot+nacos启动时,log会有一条nacos注册信息如下:
**很显然,启动注册服务的操作便在NacosServiceRegistry
当中。
NacosServiceRegistry
可以通过查找所有类直接寻找,也可以从以下springboot自动装配的角度去寻找
通过查看spring.factories
文件也可以看到,nacos自动装配的类如下,nacos按照了springboot的约定进行了自动装配:**
进入到NacosDiscoveryAutoConfiguration
发现,前两个Bean都是作为最后一个Bean的参数,猜想第三个bean是主要的注册方法。
****点进去发现他继承了AbstractAutoServiceRegistration.class
,并且AbstractAutoServiceRegistration.class
实现了ApplicationContextAware以及ApplicationListener,果然下方有一个onApplicationEvent
的方法
监听器中有一个bind方法,bind方法中又有一个start方法:
public void start() {
if (!this.isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
} else {
if (!this.running.get()) {
this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
this.register();
if (this.shouldRegisterManagement()) {
this.registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
this.running.compareAndSet(false, true);
}
}
}
其中的register便是注册方法。由此便找到了NacosServiceRegistry.class
二、register
剖析
register方法如下:
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
} else {
String serviceId = registration.getServiceId();
Instance instance = this.getNacosInstanceFromRegistration(registration);
try {
this.namingService.registerInstance(serviceId, instance);
log.info("nacos registry, {} {}:{} register finished", new Object[]{serviceId, instance.getIp(), instance.getPort()});
} catch (Exception var5) {
log.error("nacos registry, {} register failed...{},", new Object[]{serviceId, registration.toString(), var5});
}
}
}
2.1 getServiceId
查看源码最终发现读取了配置文件的spring项目的name
2.2 getNacosInstanceFromRegistration
方法获取了本地客户端的一些信息,然后生成实例
private Instance getNacosInstanceFromRegistration(Registration registration) {
Instance instance = new Instance();
instance.setIp(registration.getHost());
instance.setPort(registration.getPort());
instance.setWeight((double)this.nacosDiscoveryProperties.getWeight());
instance.setClusterName(this.nacosDiscoveryProperties.getClusterName());
instance.setMetadata(registration.getMetadata());
return instance;
}
那么注册的主要方法便是this.namingService.registerInstance(serviceId, instance);
2.3 registerInstance
方法设置了注册需要的一些基础信息,其中groupname默认为DEFAULT_GROUP
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
if (instance.isEphemeral()) {
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
long instanceInterval = instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(instanceInterval == 0L ? DEFAULT_HEART_BEAT_INTERVAL : instanceInterval);
this.beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), beatInfo);
}
this.serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName), groupName, instance);
}
其中最后一行代码执行了注册动作,注册之前会通过schedule
的方式添加定时心跳检测任务,默认五秒检测,而且会根据计算机可用资源核心数除以二分配检测资源(最小为1核心数),暂时不看
2.4 registerService
方法进行了post请求,将本地的一些基础信息作为参数放入到请求体当中,进行post
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
LogUtils.NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", new Object[]{this.namespaceId, serviceName, instance});
Map<String, String> params = new HashMap(9);
params.put("namespaceId", this.namespaceId);
params.put("serviceName", serviceName);
params.put("groupName", groupName);
params.put("clusterName", instance.getClusterName());
params.put("ip", instance.getIp());
params.put("port", String.valueOf(instance.getPort()));
params.put("weight", String.valueOf(instance.getWeight()));
params.put("enable", String.valueOf(instance.isEnabled()));
params.put("healthy", String.valueOf(instance.isHealthy()));
params.put("ephemeral", String.valueOf(instance.isEphemeral()));
params.put("metadata", JSON.toJSONString(instance.getMetadata()));
this.reqAPI(UtilAndComs.NACOS_URL_INSTANCE, params, (String)"POST");
}
最后通过callServer方法中的httpClient进行了http请求,请求地址为http://127.0.0.1:8848/nacos/v1/ns/instance
而通过debug执行这一行代码之后,在nacos就能看到服务已经注册了上去。
客户端已经知道注册的流程是怎么样的了,接下来看服务端接收到http请求会有什么样的操作。
三、服务端的处理
上面的http请求地址为http://127.0.0.1:8848/nacos/v1/ns/instance
,对应的controller方法为register
方法,位置如下:
3.1 点击去之后发现最终的实现方法为registerInstance
public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
createEmptyService(namespaceId, serviceName, instance.isEphemeral());
Service service = getService(namespaceId, serviceName);
if (service == null) {
throw new NacosException(NacosException.INVALID_PARAM,
"service not found, namespace: " + namespaceId + ", service: " + serviceName);
}
addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}
首先会去创建一个新的service,namespaceId为键,如果namespaceId不存在,则执行createServiceIfAbsent
,服务第一次都会进行这个方法的执行,执行之后会保存在一个叫serviceMap的Map存储结构当中,相当于缓存
public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
createServiceIfAbsent(namespaceId, serviceName, local, null);
}
3.2 接着是addInstance
方法,这个方法会根据ip地址
public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)
throws NacosException {
String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);
Service service = getService(namespaceId, serviceName);
synchronized (service) {
List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);
Instances instances = new Instances();
instances.setInstanceList(instanceList);
consistencyService.put(key, instances);
}
}
最后通过consistencyService.put(key, instances);
做持久化,并没有持久到数据库当中。
ConsistencyService
类会通过添加监听器的方式,当值进行变化时执行onchange方法来更改服务当前状态: