写点什么

Nacos 如何实现服务自动注册

用户头像
编号94530
关注
发布于: 2020 年 09 月 20 日
Nacos如何实现服务自动注册

1. 背景


接着上次的说,Nacos 问题的分析。这次就来分享一下自己的分析方法。以及如何利用 Spring 事件完成服务注册。


2. 分析一下


2.1 问题分析过程


问题是这样的,我以为是 Nacos 服务注册不上吗? 那就肯定是注册有问题了, 那就去找是服务是如何注册的。


关键是从哪找呢?具体步骤:


  1. 知道服务启动方式。如 Nacos,就是通过 properties,那就找哪有用到启动的属性文件

  2. 项目用 SpringBoot 集成,那核心类的加载就在 Spring.factories 中。(这个文件会被 springSPI 加载到)

  3. 找到关键类


很凑巧,我们在 spring.factories 中找到了名字很像启动注册类的类。如下图:



在找类的时候不要怕错,大胆猜,只要验证了自己的想法即可,不对就在接着找。


com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration类我们找到了,接下来就是去看这个类中的代码去验证自己的想法了。


2.2 Nacos 是如何利用 Spring 事件来实现服务注册的?


com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration代码如下:


public class NacosServiceRegistryAutoConfiguration {	// 注册类实现,验证我们的想法,需要用到NacosDiscoverProperties	@Bean	public NacosServiceRegistry nacosServiceRegistry(			NacosDiscoveryProperties nacosDiscoveryProperties) {		return new NacosServiceRegistry(nacosDiscoveryProperties);	}	// 构建注册事例,依然要用到NacosDiscoverProperties	@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);	}}
复制代码


通过查看代码,发现和我们猜想没有出入,是通过NacosDiscoverProperties来进行初始化,和官方给的 Demo 也相似(只是没继承 SpringBoot,名字不叫这个),前面两个类我们先不用关注(好奇的可以先点进去看看),我们直接进入第三个方法NacosAutoServiceRegistration中,通过名字我们也可以看出,这个是自动注册的,参数中包含了 Registry 和 Registration。我们进入类中接着查看。


public class NacosAutoServiceRegistration		extends AbstractAutoServiceRegistration<Registration> {
private NacosRegistration registration; // 构造参数,关键 public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) { super(serviceRegistry, autoServiceRegistrationProperties); this.registration = registration; } // 注册方法 @Override protected void register() { if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) { log.debug("Registration disabled."); return; } if (this.registration.getPort() < 0) { this.registration.setPort(getPort().get()); } // 调用了父类的注册方法 super.register(); }}
复制代码


进入类中,我们发现了 register()方法,更进一步验证我们想法,是通过这个类来进行服务注册,但是在 register()方法中,调用了父类的 register()方法, 这点就要引起我们的好奇,父类都没都啥东西,调用父类的方法有啥用呢? 这个时候就想到了肯定有构造参数,或者方法初始化父类的东西啦!


我们观察构造参数,发现把 serviceRegistry 传入到了父类构造参数中,我们直接查看父类代码。


// 类public abstract class AbstractAutoServiceRegistration<R extends Registration>		implements AutoServiceRegistration, ApplicationContextAware,		ApplicationListener<WebServerInitializedEvent> {      }
复制代码


AbstractAutoServiceRegistration是继承了ApplicationListener<WebServerInitializedEvent>,看到这,不就是我们熟悉的领域了吗?继承了ApplicationListener那就不多 BB,直接看onApplicationEvent()方法就好啦。(这个事件是 WebServerInitializedEvent,这点还是要知道的哈)


	@Override	@SuppressWarnings("deprecation")	public void onApplicationEvent(WebServerInitializedEvent event) {    // 调用了bind方法		bind(event);	}		@Deprecated	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());    // 调用了start()方法		this.start();	}
复制代码


我们直接进入 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();      // 服务注册事件			this.context.publishEvent(					new InstanceRegisteredEvent<>(this, getConfiguration()));			this.running.compareAndSet(false, true);		}
}
复制代码


start()方法里面还发布了两个事件,可见 Spring 中,服务的注册前后都有一些监听器来处理服务信息。先跳过这些,我们直接关注我们的核心——注册,直接进入 register()方法中。


	protected void register() {    // this.serviceRegistry 是我们传递进来的,    // getRegistration() 是在NacosAutoServiceRegistration中实现的		this.serviceRegistry.register(getRegistration());	}
复制代码


到这我们基本知道是怎么回事了,this.serviceRegistry是传递进来的,那调用的register()方法则是 Nacos 自己实现的注册,getRegistration()NacosServiceRegistryAutoConfiguration中通过nacosDiscoveryProperties生成的。那我们就看一下register()方法就好啦。


	@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实例 Instance instance = getNacosInstanceFromRegistration(registration);
try { // 向服务端发送注册请求 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); // rethrow a RuntimeException if the registration is failed. // issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132 rethrowRuntimeException(e); } }
复制代码


到这我们就看懂了,也知道大概流程了。流程图如下



3. 吸星大法:吸收代码经验


Spring 在实现的时候,通过设计模式(装饰),实现流程,又把关键部分丢给开发自己实现,提高了拓展性,然后巧妙的结合了观察者模式(变种),在合适的时间注册服务,妙啊~~


Nacos 在实现的时候,深知 Spring 各种属性,各种用法,一定是对 Spring 有个深入了解的人(或者正好看过? 这个解释有点牵强),结合 AutoRegistry 和事件(不知道,肯定不知道怎么调用的),妙啊~~


看到这实现的代码,简直舒服,妙啊~~


xua~~(), 爽啊


4. 总结


总的看下来,这个启动,只能有 Web 容器的时候才能注册, 我猜主要是和 namingServer 注册有关,所以也不能算是一个 bug 了。


发布于: 2020 年 09 月 20 日阅读数: 1502
用户头像

编号94530

关注

你的每一个点赞我都当成了喜欢 2020.04.29 加入

公众号: 星球x 欢迎大家关注,谢谢!

评论

发布
暂无评论
Nacos如何实现服务自动注册