问题引出
单独的nacos项目中分为client端和server端,client端会调用register去向server端注册,但是我们在做微服务的时候引入的并不是单独的nacos客户端的jar包,而是springcloud alibaba nacos这个jar包,当我们引入这个jar包之后,就可以在项目启动的时候自动地往nacos服务端去注册这个服务了,那么springcloud alibaba nacos这个包能做到自动注册的原理是什么呢?
Nacos,SpringCloud Alibaba,SpringCloud三者之间的关系
1.naocs
nacos项目我们可以单独去download下来,里面主要包含了client和server等模块,如下图中的nacos-all就是整个nacos项目
我们如果想单独使用这个nacos项目去运行的话就需要去手动地去调用client模块的register方法去向server端模块去注册服务,但是很明显我们并不会这样去用,因为现在每出一个流行框架基本都需要去与spring进行集成,毕竟现在spring全家桶时代,不然你一个产品出来之后并不会有多少人去用
2.spring cloud
springcloud是spring对于实现微服务的一整套方案,里面集成了很多并不是spring官方开发的组件,这些组件会被集成到springcloud中去方便开发者的使用,也正是因为市场上有很多第三方组件,所以springcloud必须要有一个标准去集成这些组件,而其中的springcloud-commons包就是对于这些第三方包的一个抽象的标准,就是说如果有其他的第三方框架想集成到springcloud中来的话,就需要去根据这个标准去实现
3.spring cloud alibaba
springcloud alibaba这个项目里面包含了很多alibaba官方开发的用于微服务的框架组件,比如nacos,sentinel,seata等等,但是我们上面也说过现在你单独的一个原生的组件基本都要与spring去进行集成的,所以spring cloud alibaba就相当于这些原生的组件与springcloud 之间的粘合剂一样,里面开发了很多的starter包把这些组件去集成到springcloud中去了。就拿nacos来说,我们通常使用都是去依赖下面这个包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>
而这个包就是springcloud alibaba项目里面的一个模块
自动注册原理?
我们这里先说出答案,其实在sc alibaba中nacos实现自动注册原理很简单,它其实就是利用了spring的事件机制去完成的。我们上面说过springcloud-commons这个包主要是实现了集成到sc的标准,那么为什么需要这个标准呢?因为可能第三方的组件有很多,它们都想集成到sc中去,那么sc就需要提供一套抽象的标准去让这些第三方组件去实现。而nacos利用spring的事件机制去实现就是用的sc里面对于注册中心的一个标准。那么这个标准具体是怎样的呢?我们下面来看springcloud-commons这个包,来到AbstractAutoServiceRegistration这个类,这个类它主要是完成自动注册的,它完成了对自动注册的一个抽象流程,具体的注册逻辑交给具体的注册中心去完成。首先它是一个抽象类,这个类实现了ApplicationListener接口,并且接口泛型为WebServerInitializedEvent,作用就是这个类具有了监听器的功能,监听的事件为WebServerInitializedEvent,当监听到这个事件的时候就会调用onApplicationEvent方法,所以下面来到它的onApplicationEvent方法
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
}
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
if ("management".equals(((ConfigurableWebServerApplicationContext) context)
.getServerNamespace())) {
return;
}
}
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
}
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
if (!this.running.get()) {
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
register();
if (shouldRegisterManagement()) {
registerManagement();
}
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
this.running.compareAndSet(false, true);
}
}
可以看到最终调到了start方法,而这个start方法里面又调了一个register方法,这个方法就和注册有关
protected void register() {
this.serviceRegistry.register(getRegistration());
}
里面调用了serviceRegistry的register方法,这个serviceRegistry它是一个接口,里面定义了服务注册,服务销毁,服务更新等方法,证明了肯定有对应的实现类去实现了它
public interface ServiceRegistry<R extends Registration> {
/**
* Registers the registration. A registration typically has information about an
* instance, such as its hostname and port.
* @param registration registration meta data
*/
void register(R registration);
/**
* Deregisters the registration.
* @param registration registration meta data
*/
void deregister(R registration);
/**
* Closes the ServiceRegistry. This is a lifecycle method.
*/
void close();
/**
* Sets the status of the registration. The status values are determined by the
* individual implementations.
* @param registration The registration to update.
* @param status The status to set.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
void setStatus(R registration, String status);
/**
* Gets the status of a particular registration.
* @param registration The registration to query.
* @param <T> The type of the status.
* @return The status of the registration.
* @see org.springframework.cloud.client.serviceregistry.endpoint.ServiceRegistryEndpoint
*/
<T> T getStatus(R registration);
}
小总结:这个类的作用就是抽象了自动注册的模板过程,而实现它能实现自动注册的原理在于它实现了ApplicationListener接口,当收到了WebServerInitializedEvent事件就会去调用注册方法,对于具体的注册逻辑就交给具体的实现类去实现(比如说nacos,这里就调用nacos client去完成注册)
既然AbstractAutoServiceRegistration和ServiceRegistry都是抽象类或者接口,那么肯定会有对应的实现类去实现它们,而我们上面也说了这是sc所指定的标准,所以具体的实现应该是由具体的一个框架去提供的,所以sc alibaba nacos里面就肯定会有对这个标准的实现
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class })
public class 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);
}
}
在springcloud-alibaba-nacos-discovery包中我们可以找到NacosDiscoveryAutoConfiguration这个配置类,从这个配置类里面可以看到它往容器中注入了3个bean,而这个3个bean都是继承sc的标准去实现的,首先看下NacosServiceRegistry
@Override
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
//根据registration对象转换成Instance对象
//registration对象是sc定义的实例对象,而Instance对象是nacos的实例对象
Instance instance = getNacosInstanceFromRegistration(registration);
try {
//调用nacos client端的注册api往server端注册服务
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
}
}
这个类就是sc-alibaba-nacos对ServiceRegistry接口的实现,其中上面的register方法里面调用的是nacos原生的注册api,但是这个类要生效得被spring所管理吧?所以还需要去扩展springboot,在spring.factories文件中加入这个类的全类名
总结
当我们在pom文件中加入springcloud-alibaba-nacos-discovery包的时候,在springboot启动的时候就能把NacosDiscoveryAutoConfiguration这个加载到spring容器中,进而就能够把里面被@Bean注解标注的NacosServiceRegistry,NacosRegistration ,NacosAutoServiceRegistration也加入到spring容器中,而NacosAutoServiceRegistration这个类实现了ApplicationListener接口,所以它具有了监听器的功能,监听的事件是WebInitializedEvent,而这个事件产生的时机是当springboot容器启动的时候(此时容器中的bean已经初始化完成了),也就是说在springboot启动的时候,这个类会监听到WebInitializedEvent事件进而调用其onApplicationEvent方法,而在这个方法中就是调用注册方法了,具体的注册逻辑会交给具体的注册组件去实现,这里就是NacosServiceRegistry调用了nacos原生的注册api,到此为止,整个springcloud-alibaba-nacos-discovery包的自动注册已经完成了