到底是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。公众号里有更多高质量干货系列文章和精品面试题。

记得点赞、分享哦!!!

相关推荐
卷无止境14 分钟前
如何让AI Agent老老实实遵守公司代码规范——以Pi Agent为例
后端·python
行者-全栈开发16 分钟前
SpringBoot 生产环境踩坑实录:10个真实故障案例与教训
java·spring boot·后端
李福春19 分钟前
斗破Google ADK Java苍穹-第7境斗皇-御天地-Harness治理
java·人工智能·python·深圳同盟·腾讯云架构师技术同盟·adk java·harness 治理
Wang's Blog20 分钟前
Java 项目部署之 Docker工具快速入门: 容器命令练习:创建、改文件、重启到删除的完整闭环
java·开发语言·docker
我是人✓21 分钟前
Java面向对象核心特性深度解析
java·前端·算法
JaguarJack22 分钟前
免费可商用 PHP 管理后台 CatchAdmin V5.4.1 发布,新增 MCP 支持
后端·php·laravel·服务端
Wx-bishekaifayuan25 分钟前
springboot线上中医药养生平台管理系统36753-计算机课程设计、毕业设计
java·spring boot·后端·spring·课程设计·express·旅游
IT_陈寒27 分钟前
为什么我的Vue组件总是莫名其妙更新?
前端·人工智能·后端
Wx-bishekaifayuan29 分钟前
springboot大学生就业平台47691-计算机课程设计、毕业设计
java·spring boot·后端·elasticsearch·课程设计·express·旅游
BingoGo31 分钟前
免费可商用 PHP 管理后台 CatchAdmin V5.4.1 发布,新增 MCP 支持
后端·php