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:




















