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

记得点赞、分享哦!!!

相关推荐
小蒜学长23 分钟前
编程语言在线学习平台的设计与实现(代码+数据库+LW)
java·spring boot·后端·编程语言在线学习平台·在线编码
步行cgn1 小时前
Spring MVC 执行流程详解:一次请求的完整旅程
java·spring·mvc
增量星球1 小时前
Spring AI 2.0 Tool Calling 到底怎么跑起来的:从 @Tool 到两次模型调用
java·agent·springai·ai coding
步行cgn1 小时前
Spring MVC 九大核心角色详解:请求处理链路中的每一个关键组件
java·spring·mvc
智恒百亿1 小时前
一台 5090 八卡服务器怎么分给一个团队用?多模型多任务的显存分配实战
java·大数据·人工智能
瑞码空间1 小时前
面向接口编程与开闭原则在模拟 JDBC 驱动中的应用
java·jdbc
zhoujungis2 小时前
登录态 1 小时就掉?Django + Vue 双 Token 认证实战 | 尘世栈帧
后端
Félix2512 小时前
Nacos学习笔记(SpringBoot/SpringCloud)
java·spring boot·spring
文丑颜不良啊2 小时前
LeetCode1581~1600题目解析
java·leetcode
香蕉大香蕉(06敬畏市场)2 小时前
MySQL 单点详解(06)
java·数据库·mysql·博客·实习