1. 准备工作
我们在使用Nacos作为SpringCloud中的注册中心组件时,最常用到的是它的三个功能:服务注册、服务发现和配置中心。
现在我们单机启动多个user-client,当我们成功运行UserClientApplication
后可以在IDEA的service一栏中找到SpringBoot项目:
可以看到user-client的启动端口号是8082,和配置文件保持一致。
这时我们再去nacos上看看,可以看到user-client成功注册到Nacos上:
接着在Services一栏右键UserClientApplication → Copy Configuration:
自定义实例名称 → 点击 modify options → Program arguments
配置端口号,不能与已启动服务的端口号重复:
启动新配置好的服务:
此时Nacos上服务如下,可以看到有两个user-client的服务:
2. Nacos服务注册入口程序分析
与Spring相比,SpringCloud源码的入口程序可能比较”隐蔽“,并不是很直观。这时我们可以从两个方面入手:注解和自动配置类(也就是spring.factories文件)。
我们去查看这个依赖对应的spring.factories文件:
可以从类名推断,与服务注册相关的自动配置类是NacosServiceRegistryAutoConfiguration
,所以我们直接去这个类里面看看:
可以看到,NacosServiceRegistryAutoConfiguration
内部会注册三个Bean:NacosServiceRegistry
、NacosRegistration
和NacosAutoServiceRegistration
。
其中NacosAutoServiceRegistration
依赖于NacosServiceRegistry
和NacosRegistration
,服务注册的主要逻辑在NacosAutoServiceRegistration
中。
3. NacosAutoServiceRegistration源码分析
3.1 Nacos服务注册时机
我们先来看看服务注册是在SpringCloud启动过程中哪一步完成的。
我们来看看NacosAutoServiceRegistration
的类图:
可以看到NacosAutoServiceRegistration
的直接父类是AbstractAutoServiceRegistration
,而AbstractAutoServiceRegistration
实现了监听器接口ApplicationListener
,会重写onApplicationEvent方法:
所以我们可以推测,上下文容器在启动某一时刻会发布WebServerInitializedEvent
类型的事件,该事件会被器NacosAutoServiceRegistration
监听(其实是其父类器AbstractAutoServiceRegistration
),然后调用onApplicationEvent方法,该方法会调用start方法,start方法里面会调用register方法完成服务注册。
那么,SpringCloud会在什么时候发布WebServerInitializedEvent
类型的事件呢?
之前讲解Spring源码时有提到过,启动的核心方法refresh里面会在实例化所有Bean后调用finishRefresh方法,该方法里面会发布一些事件。
因此我们回过头再来看看AbstractApplicationContext
的finishRefresh方法:
进入到onRefresh方法会涉及到bean的生命周期的处理,最后会调用WebServerStartStopLifecycle
的start方法:
可以看到这里会发布ServletWebServerInitializedEvent
类型的事件,ServletWebServerInitializedEvent
的父类是WebServerInitializedEvent
,最终会被NacosAutoServiceRegistration
监听,启动Nacos服务注册:
总结一下,Nacos服务注册发生的时机是SpringCloud启动时的finishRefresh阶段,该阶段已经完成了beanFactory中bean的实例化。
3.2 服务注册源码实现
我们前面有说过AbstractAutoServiceRegistration
的register方法是服务注册的入口:
我们看看NacosRegistry
的register方法:
首先是关于NamingService
,NacosServiceRegistry
有一个NacosServiceManager
类型的属性,它来负责生产管理NamingService
:
我们继续看看NamingService
的registerInstance方法:
getExecuteClientProxy方法主要通过当前实例是否是临时的来选择不同的请求模式:
我们先看NamingGrpcClientProxy
:
再看看NamingHttpClientProxy
: