Nacos服务注册源码分析
- Nacos服务注册原理
- Nacos服务注册怎么做的
- Nacos服务注册源码解读
带着以上三个问题,进入到今天的源码解读
问题1:Nacos服务注册原理
Nacos首先从bootstrap.yml配置文件中读取我们配置好的nacos配置,这里面一般包括spring.application.name、spring.cloud.nacos.discovery.server-addr、spring.cloud.nacos.username、spring.cloud.nacos.password以及其他的一些例如cluster、namespace等信息,依托ConfigurationProperties注解 让spring将这些配置封装成一个对象,在容器刷新完成之后执行一个register方法,把当前应用注册到指定的Nacos服务端
问题2:Nacos服务注册怎么做的
我们知道 像这种第三方集成springboot的应用,一般都会使用starter封装起来,而starter的核心就是自动装配,那么我们只需要搜索类似nacosAutoconfig之类的 就应该可以找到
不出所料 我们成功搜索到了几个类,大致可以分为三类 config配置中心、discovery注册中心以及ribbon负载均衡 ,目前我们关注的是注册中心 那么直接选择NacosDiscoveryAutoConfiguration即可。
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(
value = {"spring.cloud.service-registry.auto-registration.enabled"},
matchIfMissing = true
)
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class})
public class NacosDiscoveryAutoConfiguration {
public NacosDiscoveryAutoConfiguration() {
}
@Bean
public NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties, ApplicationContext context) {
return new NacosRegistration(nacosDiscoveryProperties, context);
}
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
}
}
通过观察这个类 可以看出来它是一个典型的springboot配置类,它里面干了三件事,也可以说是一件事,那就是注册Bean,第一、第二个bean是第三个bean的入参 ,说明第三个bean是重点,点进去之后发现这个类里面有一个register方法,盲猜 这个应该就是用来注册的方法,registration对象就是我们配置文件中的配置信息
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 = 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());
try {
this.namingService.registerInstance(serviceId, instance);
...
} catch (Exception var5) {
...
}
}
}
那么是谁来调用这个register方法呢?这个我们后面再说。回到NacosDiscoveryAutoConfiguration类,这里面有几个注解需要关注,这几个注解定义了这个类执行的前提条件
@Configuration #声明这是一个springboot配置类
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled#条件注解:只有NacosDiscoveryEnabled为true时 这个类才生效
@ConditionalOnProperty(value = {"spring.cloud.service-registry.auto-registration.enabled"},
matchIfMissing = true
)#和上面一样 只有配置文件中enabled为true时 这个类才生效 默认为true 如何使用其他框架例如dubbo进行服务注册 需要把这个设为false 要不然就会注册两个相同的服务
@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class})#声明需要在这两个bean创建之后进行加载
@ConditionalOnBean({AutoServiceRegistrationProperties.class})#容器中存在这个bean才会生效
问题3:Nacos服务注册源码解读
通过第二步的探究,我们锁定了register这个方法,那么是由谁来调用它的呢?
通过debug的方式 我们从控制台看到了这样的堆栈信息
通过堆栈信息可以清楚的看出来 它是从spring核心方法refresh方法中的finishRefresh方法进入的,它的大致链路就是refresh–>finishRefresh–>this.getLifecycleProcessor().onRefresh()–>startBeans(这个方法有一个while循环 当beanName是webServerStartStop时 走到下面链路)
–>((DefaultLifecycleProcessor.LifecycleGroup)phases.get(key)).start()
–>DefaultLifecycleProcessor.this.doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);
–>this.doStart(lifecycleBeans, dependency, autoStartupOnly);
–>bean.start()
–>this.applicationContext.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
–>this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
–>this.invokeListener(listener, event);
–this.doInvokeListener(listener, event);
–>listener.onApplicationEvent(event);
–>AbstractAutoServiceRegistration.onApplicationEvent.this.bind(event)
–this.start()
–this.register();
–this.serviceRegistry.register(this.getRegistration());
Nacos巧妙的运用springboot提供的事件订阅和发布机制,使得springboot应用在容器启动完成后第一时间调用register方法 将当前应用信息作为服务注册到nacos服务端
总结
首先springboot应用启动时会自动加载一个beanNamewebServerStartStop
,这个bean对应的类是WebServerStartStopLifecycle
,这个类又实现了SmartLifecycle
这个接口,关于lifecycle
接口的解释,借用网上的一段话
Lifecycle是Spring中最基础的生命周期接口,该接口定义了容器启动和停止的方法。方便开发者扩展自己的特定逻辑,比如启动和停止某些后台进程。Lifecycle常用来管理一个组件的启动和停止,可能会有这样的疑惑:开始和停止的逻辑写在一个bean的初始化方法和销毁方法中不可以了吗,为什么要实现个Lifecycle接口呢?这里说明一下,bean的初始化方法和销毁方法是Bean生命周期级别的;而Lifecycle是容器生命周期级别的。
它的意思就是 只有当容器启动和关闭的时候 ,springboot应用才会调用实现了这些接口的类
当我们context刷新成功之后会调用finishRefresh方法
protected void finishRefresh() {
this.clearResourceCaches();
this.initLifecycleProcessor();#在这里把所有实现了lifecycle的bean进行加载
this.getLifecycleProcessor().onRefresh();#在这里把上面加载好的bean一一执行
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
LiveBeansView.registerApplicationContext(this);
}
里面就有一个WebServerStartStopLifecycle
这个bean,按照设定 会去调用这个bean的start方法
public void start() {
this.webServer.start();
this.running = true;
this.applicationContext.publishEvent(
new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}
这里关键的是最后的publishEvent方法,它会去发布一个事件,并将满足条件的监听器进行依次执行,从代码中可以看出 只要是ServletWebServerInitializedEvent
相关的且实现了ApplicationListener
的类 都会被执行,而ServletWebServerInitializedEvent
又是WebServerInitializedEvent
的子类,换句话讲 只要是WebServerInitializedEvent
相关的类 都会被执行。
巧合的是AbstractAutoServiceRegistration
这个类实现了ApplicationListener
,并且入参泛型就是WebServerInitializedEvent
,那么一定会执行到这个类中的方法。那么这个类是哪来的呢?回到最上面自动装配的那块,nacos自动装配的3个bean,其中最后一个beanNacosAutoServiceRegistration
的父类就是AbstractAutoServiceRegistration
在执行满足条件的监听器方法时,会调用onApplicationEvent
方法,然后到bind
方法、start
方法,最终在start
方法中调用this.register()
方法,调用的就是ServiceRegistry
的register
方法,而当前应用有且只有一个ServiceRegistry
的实现类,就是NacosServiceRegistry
,最终成功执行register方法,至此 nacos成功注册。
public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
private static final Log logger = LogFactory.getLog(AbstractAutoServiceRegistration.class);
private final ServiceRegistry<R> serviceRegistry;
private boolean autoStartup = true;
private AtomicBoolean running = new AtomicBoolean(false);
private int order = 0;
private ApplicationContext context;
private Environment environment;
private AtomicInteger port = new AtomicInteger(0);
private AutoServiceRegistrationProperties properties;
protected void register() {
this.serviceRegistry.register(this.getRegistration());
}
public void onApplicationEvent(WebServerInitializedEvent event) {
this.bind(event);
}
/** @deprecated */
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
this.port.compareAndSet(0, event.getWebServer().getPort());
this.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);
}
}
........
}
}