到底是Nacos先进行服务注册,还是Tomcat先启动

公众号:Hoeller,有精品面试题(不是很多,150道,但很经典)

答案还是比较简单,肯定是Tomcat先得启动,不然如果先进行服务注册,那么可能在Tomcat还没启动完成的情况下就有服务消费者拿到服务地址进行服务调用了,从而导致调用失败。结论得出来了,但是具体是怎么实现的呢,让我来带大家一探究竟。

Spring Cloud中想要使用Nacos进行服务注册,就需要引入以下starter:

而这个starter中会引入一个叫做NacosServiceRegistryAutoConfiguration的自动配置类,而这个自动配置类中则定义了一个NacosAutoServiceRegistration类型的Bean,这个Bean看名字(翻译过来就是Nacos自动服务注册)就能知道是用来进行服务注册的,NacosAutoServiceRegistration的父类是AbstractAutoServiceRegistration,该类不是Nacos实现的,而是Spring Cloud实现的,该类的定义为:

从定义可以看出,该类实现了Spring的ApplicationListener<WebServerInitializedEvent>接口,所以,NacosAutoServiceRegistration会监听WebServerInitializedEvent事件,一旦接收到该事件就会进行服务注册。

AbstractAutoServiceRegistration中的调用流程依次是:

NacosAutoServiceRegistration中重写了register()方法,所以最终服务注册的核心逻辑是由Nacos来具体实现的,具体细节本文就不展开了(关注我,后续文章会详细分析),从以上流程我们就能知道:当Nacos接收到WebServerInitializedEvent事件时就会进行服务注册

从以上过程可以看出,服务注册的时间点其实是由AbstractAutoServiceRegistration定义的,也就是由Spring Cloud定义的,Nacos只是负责实现具体的注册逻辑。

那么WebServerInitializedEvent事件又是什么时候发布的呢?

通过debug调用栈,我发现WebServerInitializedEvent事件是由一个叫WebServerStartStopLifecycle的对象中的start()方法发布出来的:

上述代码中会先调用webServer的start()方法,而WebServer在Spring Boot中是一个接口,本质上就是代表Tomcat或Jetty或Undertow,而在当前场景中webServer就是Tomcat,所以从上述代码就可以得出结论:Tomcat会先启动,启动完成后发布WebServerInitializedEvent事件,从而触发Nacos的服务注册。

那么Tomcat又是在Spring Boot启动过程的什么时间节点启动的呢?

通过debug,发现WebServerStartStopLifecycle的start()方法是通过AbstractApplicationContext中的finishRefresh()方法调过来的,我们知道Spring Boot启动过程中会创建并启动Spring 容器,而一个Spring容器启动完成后会发布ContextRefreshedEvent事件,而发布事件的代码也是在finishRefresh()中实现的,源码为:

上述代码中的getLifecycleProcessor().onRefresh()就会调用WebServerStartStopLifecycle中的start()方法,而getLifecycleProcessor().onRefresh()的下一行代码就是发布ContextRefreshedEvent事件。

所以我们可以知道在Spring容器启动的最后,发布ContextRefreshedEvent事件的前一步就会启动Tomcat。

不过Spring容器启动过程中,在调用finishRefresh()之前其实会先调用onRefresh()方法,而Spring Boot中重写了onRefresh()方法,并且在该方法中也会做和Tomcat相关的事情,那就是初始化Tomcat,比如生成出Tomcat对象、配置端口等,但没有真正进行启动Tomcat。

所以,结合我们常见的一些知识点,我最后得出结论,整个过程是这样的:

  1. 启动Spring Boot
  2. 执行ApplicationContextInitializer等
  3. 启动Spring容器
    1. 解析配置类(扫描、解析自动配置类)
    2. 调用onRefresh(),初始化Tomcat
    3. 实例化非懒加载的单例Bean
    4. 调用finishRefresh()
      1. 调用WebServerStartStopLifecycle中的start()
        1. 启动Tomcat
        2. 发布ServletWebServerInitializedEvent,从而触发Nacoo进行服务注册
      2. 发布ContextRefreshedEvent事件

我是爱读源码的大都督周瑜,欢迎关注我的公众号:Hoeller。公众号里有更多高质量干货系列文章和精品面试题。

记得点赞、分享哦!!!

相关推荐
Tigshop开源商城21 小时前
『物流设置+SEO优化』Tigshop开源商城系统 JAVA v5.8.26 版本更新!
java·开源商城系统·tigshop
Tigshop开源商城1 天前
『订单税率+收货地址校验国家字段』功能上新|跨境运营更高效,Tigshop开源商城系统 JAVA v5.8.23 版本更新
java·开源商城系统·tigshop
珠海西格电力1 天前
零碳园区的能源供给成本主要包括哪些方面?
大数据·分布式·微服务·架构·能源
养肥胖虎1 天前
Docker学习笔记:后端、数据库和反向代理怎么一起跑起来
后端·nginx·docker·postgresql·go·部署
REDcker1 天前
C++变量存储与ELF段布局详解 从const全局到rodata与nm_readelf验证实践
java·c++·面试
晓杰'1 天前
从0到1实现 Balatro 游戏后端(2):NestJS框架搭建与项目结构设计
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs
无所事事O_o1 天前
二次验证码TOTP 使用说明
后端·二次验证码·谷歌验证器
ltl1 天前
Multi-Head Attention:为什么要分多个头
后端
kobesdu1 天前
【ROS2实战笔记-19】ROS2 生命周期节点的启动顺序、状态转换陷阱与热备方案
java·前端·笔记·机器人·ros·ros2
neo_Ggx231 天前
Maven 版本管理详解:SNAPSHOT、Release 与 Nexus 仓库的区别和影响
java·maven