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

记得点赞、分享哦!!!

相关推荐
神奇的程序员3 小时前
从已损坏的备份中拯救数据
运维·后端·前端工程化
Goldn.3 小时前
Java核心技术栈全景解析:从Web开发到AI融合
java· spring boot· 微服务· ai· jvm· maven· hibernate
oden3 小时前
AI服务商切换太麻烦?一个AI Gateway搞定监控、缓存和故障转移(成本降40%)
后端·openai·api
李慕婉学姐4 小时前
【开题答辩过程】以《基于Android的出租车运行监测系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·后端·vue
m0_740043734 小时前
SpringBoot05-配置文件-热加载/日志框架slf4j/接口文档工具Swagger/Knife4j
java·spring boot·后端·log4j
编织幻境的妖4 小时前
SQL查询连续登录用户方法详解
java·数据库·sql
未若君雅裁5 小时前
JVM面试篇总结
java·jvm·面试
kk哥88995 小时前
C++ 对象 核心介绍
java·jvm·c++
招风的黑耳5 小时前
我用SpringBoot撸了一个智慧水务监控平台
java·spring boot·后端
xunyan62345 小时前
面向对象(下)-接口的理解
java·开发语言