三合一唉!!!! 超級尊享版!!!!! 肝了一天唉!!!!
Eureka
關(guān)鍵類
# 服務(wù)注冊
1.EurekaClientAutoConfiguration
注冊了眾多的 bean
一部分用于和 Eureka Server 交互
一部分和 Commons 項目對接
注冊了(EurekaClient/EurekaAutoServiceRegistration/ApplicationInfoManager/EurekaRegistration)
2.EurekaClient
與 Eureka Server 端交互
負(fù)責(zé)向 Eureka Server 端注冊/注銷服務(wù)實例
在構(gòu)造方法和 shutdown 方法中根據(jù)配置處理自動注冊和自動注銷.
3.InstanceInfoReplicator
負(fù)責(zé)定義一個注冊或更新服務(wù)實例的任務(wù)
負(fù)責(zé)管理任務(wù)執(zhí)行器
4.RestTemplateEurekaHttpClient
負(fù)責(zé)根據(jù)服務(wù)實例信息構(gòu)造注冊/注銷的 Http 請求
使用 RestTemplate 發(fā)送請求
5.EurekaAutoServiceRegistration
負(fù)責(zé)管理服務(wù)實例的自動注冊/注銷, 與容器生命周期掛鉤
6.ApplicationInfoManager
負(fù)責(zé)管理服務(wù)實例狀態(tài)變更事件(即管理監(jiān)聽者并在適當(dāng)時機觸發(fā)他們)
7.ApplicationInfoManager.StatusChangeListener
狀態(tài)改變事件監(jiān)聽者類
8.EurekaHealthCheckHandler
服務(wù)實例狀態(tài)健康檢查類, 注冊/注銷服務(wù)實例前會調(diào)用此類進(jìn)行檢查其狀態(tài)
# 服務(wù)發(fā)現(xiàn)
1.BlockingLoadBalancer (Commons 項目上)
負(fù)責(zé)調(diào)用實際的負(fù)載均衡器去選擇一個服務(wù)實例
負(fù)責(zé)調(diào)度負(fù)載均衡整個過程, 觸發(fā)相應(yīng)的生命周期.
2.RoundRobinLoadBalancer (Commons 項目上)
實際的負(fù)載均衡策略算法類
負(fù)責(zé)連接 ServiceInstanceListSupplier 從其中獲取服務(wù)實例列表
3.ServiceInstanceListSupplier
定義了獲取服務(wù)實例實例列表的接口(任意方式)
4.DiscoveryClient
定義了從服務(wù)端獲取服務(wù)實例列表的接口(更明確了)
4.DiscoveryClientServiceInstanceListSupplier
ServiceInstanceListSupplier 的實現(xiàn)類
連接 ReactiveDiscoveryClient 類, 將服務(wù)端獲取道德服務(wù)實例列表返回出去
5.EurekaDiscoveryClient
實現(xiàn)了 DiscoveryClient 接口
負(fù)責(zé)調(diào)用 EurekaClient 從 Eureka Server 端獲取服務(wù)實例列表.
6.EurekaClient
與 Eureka Server 端交互
負(fù)責(zé)向 Eureka Server 端獲取服務(wù)實例列表
在構(gòu)造方法和 shutdown 方法中根據(jù)配置處理自動注冊和自動注銷.
7.EurekaServiceInstance
服務(wù)實例信息對象
實現(xiàn) ServiceInstance, 與 Commons 對接
# 總結(jié)
實現(xiàn)了 Commons 的 DiscoveryClient 接口(即 EurekaDiscoveryClient), 于是服務(wù)發(fā)現(xiàn)實現(xiàn)了;
實現(xiàn)了 Commons 的 ServiceRegisty 接口(即 EurekaServiceRegistry), 于是服務(wù)注冊也實現(xiàn)了.
再總結(jié): Commons 大發(fā)好.
服務(wù)注冊流程
# EurekaClient 構(gòu)造方法觸發(fā)(前提 shouldRegisterWithEureka 為 true)
1.EurekaClientAutoConfiguration 讓容器里注冊了一個 EurekaClient
2.EurekaClient 這個類在構(gòu)造方法中的 initScheduledTasks() 生成了一個 InstanceInfoReplicator 對象
3.然后調(diào)用其 instanceInfoReplicator.start(), 邏輯是添加一個定時任務(wù)(僅執(zhí)行一次), 定時任務(wù)執(zhí)行 run()
4.run() 里面會執(zhí)行 discoveryClient.register() 也就是注冊實例信息到 Eureka 上去.
5.register() 里面會調(diào)用 RestTemplateEurekaHttpClient#register()
6.這個方法是構(gòu)造一個 HTTP 請求, 地址為 serviceUrl + "apps/" + info.getAppName(), Method 為 POST, 即 Eureka server 的ip/apps/服務(wù)實例名稱(如spring.application.name), 當(dāng)然請求 body 還會帶上實例信息 info 對象.
# 根據(jù)容器生命周期觸發(fā)
1.EurekaClientAutoConfiguration 讓容器里注冊了一個 EurekaAutoServiceRegistration.
2.這個類即是 SmartLifecycle(與容器生命周期綁定), 也是 SmartApplicationListener(監(jiān)聽容器加載/關(guān)閉事件)
3.因此其對應(yīng)的 start()/stop() 和 onApplicationEvent() 都實現(xiàn)了對應(yīng)的邏輯.
4.如 start() 的邏輯為調(diào)用 EurekaServiceRegistry#register()
5.register() 先修改本地服務(wù)實例的裝填, 再通過 com.netflix.discovery.DiscoveryClient#registerHealthCheck() 來往任務(wù)管理器中提交一個任務(wù), 后臺執(zhí)行, 任務(wù) InstanceInfoReplicator#onDemandUpdate()
6.此任務(wù)就是最終也會調(diào)用前面提到的 (4) 中的 run(). 然后就注冊上去了.
# PS
(6) 中的任務(wù), 與狀態(tài)監(jiān)聽是一致的
總結(jié): 容器注冊 EurekaClient, 調(diào)用構(gòu)造方法完成大量初始化工作后, 另起一個線程調(diào)用 restTemplate 發(fā)送 HTTP 請求將當(dāng)前服務(wù)實例信息發(fā)送給 Eureka server. 完成服務(wù)注冊工作.
graph TB
A(EurekaClientAutoConfiguration)
A1(EurekaClient)
A2(InstanceInfoReplicator)
A3(EurekaClient#register)
A4(RestTemplateEurekaHttpClient)
A5(RestTemplate)
A6(Eureka Server)
A7(EurekaHttpResponse)
A--讓容器里注冊一個-->A1
A1--在構(gòu)造方法中生成一個-->A2
A2--在 run 方法中調(diào)用-->A3
A3--又把服務(wù)實例信息交給-->A4
A4--根據(jù)服務(wù)實例信息構(gòu)造HTTP請求-->A5
A5--將服務(wù)實例信息發(fā)送給-->A6
A6--返回一個-->A7
服務(wù)注銷流程
# 容器關(guān)閉(配置了容器 shouldUnregisterOnShutdown=true)
1.在 DiscoveryClient#shutdown() 中根據(jù) shouldUnregisterOnShutdown 判斷是否需要注銷
2.然后在 unregister() 中調(diào)用 RestTemplateEurekaHttpClient#cancel()
3.cancel() 中使用 restTemplate 構(gòu)造 Method 為 DELETE, URL 為 serviceUrl + "apps/" + appName + '/' + id 的請求并發(fā)送給 Eureka server 告知其注銷 appName 下對應(yīng)的服務(wù)實例(根據(jù) id).
# 接收到容器關(guān)閉事件
1.EurekaClientAutoConfiguration 讓容器里注冊了一個 EurekaAutoServiceRegistration.
2.這個類即是 SmartLifecycle(與容器生命周期綁定), 也是 SmartApplicationListener(監(jiān)聽容器加載/關(guān)閉事件)
3.因此其對應(yīng)的 start()/stop() 和 onApplicationEvent() 都實現(xiàn)了對應(yīng)的邏輯.
4.如 stop() 的邏輯為調(diào)用 EurekaServiceRegistry#deregister()
5.deregister() 的作用是執(zhí)行 ApplicationInfoManager#setInstanceStatus() 將狀態(tài)改為 DOWN
6.因為 ApplicationInfoManager 這個類專門管理狀態(tài)變化事件, 因此還會將事件發(fā)布出去.
7.而在 initScheduledTasks 中就添加了這樣的一個監(jiān)聽者, 起作用為調(diào)用 InstanceInfoReplicator#onDemandUpdate()
8.接著會調(diào)用 InstanceInfoReplicator#run(), 第一行代碼 refreshInstanceInfo() 會確保狀態(tài)為最新的(那也還是 DOWN)
9.但是其最終并不是調(diào)用 cancel 注銷, 而 register 注冊, 不過其中的狀態(tài)是 DOWN, 因此會有 server 那邊判斷; 所以容器關(guān)閉事件并不會觸發(fā) cancel, 但效果應(yīng)是一樣的.
總結(jié): 要么是 shutdown() 中 unregister 調(diào)用了 cancel(), 發(fā)出了 Method 為 DELETE 的請求來注銷; 要么就是 EurekaClientAutoConfiguration 中與容器生命周期做關(guān)聯(lián), 全程使用 register 接口來更新狀態(tài).
graph TD
A(容器關(guān)閉#close)
A1(DiscoveryClient#shutdown)
A2(DiscoveryClient#unregister)
A3(RestTemplateEurekaHttpClient#cancel)
A4(RestTemplate)
A5(Eureka Server)
A--觸發(fā)-->A1
A1--調(diào)用-->A2
A2--調(diào)用-->A3
A3--構(gòu)造HTTP請求交給-->A4
A4--發(fā)送注銷申請給-->A5
服務(wù)發(fā)現(xiàn)流程
1.Spring Cloud Commons 中注冊了一個 ServiceInstanceListSupplier, 具體為(DiscoveryClientServiceInstanceListSupplier)
2.這個類的作用是借助 ReactiveDiscoveryClient 的 getInstances(String serviceId) 方法向 LoadBalancer 提供從具體的 server(如Eureka) 獲取服務(wù)實例對象列表, 這樣只要實現(xiàn) ReactiveDiscoveryClient 并放入容器就可以和 Spring cloud LoadBalancer 對接了.
3.在 EurekaDiscoveryClientConfiguration 中讓容器注冊了一個 DiscoveryClient(具體為 EurekaDiscoveryClient).
4.因為 Spring Cloud Commons 做了大量的預(yù)備對接工作, 所以對接其實就結(jié)束了.
5.那再簡單說下 EurekaDiscoveryClient 的實現(xiàn), 即注入一個 EurekaClient eurekaClient, 然后調(diào)用 DiscoveryClient#getInstancesByVipAddress() 就獲取到了 ServiceInstance 列表.
總結(jié): Commons 中準(zhǔn)備好了對接的方式: 實現(xiàn) DiscoveryClient 接口, 接著我們的確實現(xiàn)了 DiscoveryClient 接口, 即 EurekaDiscoveryClient, 而這這個類則會調(diào)用 EurekaClient 的 getInstancesByVipAddress 從 Eureka Server 端獲取注冊了的服務(wù)實例信息.
graph TD
A1(BlockingLoadBalancerClient#位于Commons項目)
A2(RoundRobinLoadBalancer#位于Commons項目)
A4(LoadBalancerClientConfiguration#位于Commons項目)
A5(DiscoveryClientServiceInstanceListSupplier)
A6(EurekaDiscoveryClient#getInstances)
A7(EurekaClient#getInstancesByVipAddress)
A9(Eureka Server)
A8(ServiceInstance 集合)
A1--choose 方法調(diào)用-->A2
A2--choose 方法調(diào)用-->A5
A4--注入一個-->A5
A5--調(diào)用-->A6
A6--調(diào)用-->A7
A7--從-->A9
A9--獲取-->A8
A10(EurekaDiscoveryClientConfiguration)--注入一個-->A6
Consul
關(guān)鍵類
# 服務(wù)注冊
1.ConsulAutoServiceRegistrationAutoConfiguration
負(fù)責(zé)添加自動注冊/注銷相關(guān)的 bean
注冊了 ConsulAutoServiceRegistration/ConsulAutoServiceRegistrationListener/ConsulAutoRegistration
2.ConsulServiceRegistryAutoConfiguration
負(fù)責(zé)注冊服務(wù)注冊相關(guān)的 bean
注冊了 ConsulServiceRegistry
3.ConsulDiscoveryClientConfiguration
負(fù)責(zé)注冊服務(wù)發(fā)現(xiàn)相關(guān)的 bean
注冊了 ConsulDiscoveryClient
4.ConsulServiceRegistry
負(fù)責(zé)與 ConsulClient 對接, 再提供注冊/注銷功能
5.ConsulClient
與 Consul Server 端交互
負(fù)責(zé)向 Consul Server 端注冊/注銷服務(wù)實例
6.AgentConsulClient
負(fù)責(zé)根據(jù)服務(wù)實例信息構(gòu)造注冊/注銷的 Http 請求
7.ConsulAutoServiceRegistration/ConsulAutoServiceRegistrationListener
負(fù)責(zé)管理服務(wù)實例的自動注冊/注銷, 與容器生命周期掛鉤
# 服務(wù)發(fā)現(xiàn)
1.BlockingLoadBalancer (Commons 項目上)
負(fù)責(zé)調(diào)用實際的負(fù)載均衡器去選擇一個服務(wù)實例
負(fù)責(zé)調(diào)度負(fù)載均衡整個過程, 觸發(fā)相應(yīng)的生命周期.
2.RoundRobinLoadBalancer (Commons 項目上)
實際的負(fù)載均衡策略算法類
負(fù)責(zé)連接 ServiceInstanceListSupplier 從其中獲取服務(wù)實例列表
3.ServiceInstanceListSupplier
定義了獲取服務(wù)實例實例列表的接口(任意方式)
4.DiscoveryClient
定義了從服務(wù)端獲取服務(wù)實例列表的接口(更明確了)
4.DiscoveryClientServiceInstanceListSupplier
ServiceInstanceListSupplier 的實現(xiàn)類
連接 ReactiveDiscoveryClient 類, 將服務(wù)端獲取道德服務(wù)實例列表返回出去
5.ConsulDiscoveryClient
實現(xiàn)了 DiscoveryClient 接口
負(fù)責(zé)調(diào)用 ConsulClient 從 Consul Server 端獲取服務(wù)實例列表.
6.ConsulClient
與 Consul Server 端交互
負(fù)責(zé)向 Consul Server 端獲取服務(wù)實例列表
7.ConsulServiceInstance
服務(wù)實例信息對象
實現(xiàn) ServiceInstance, 與 Commons 對接
# 總結(jié)
實現(xiàn)了 Commons 的 DiscoveryClient 接口(即 ConsulDiscoveryClient), 于是服務(wù)發(fā)現(xiàn)實現(xiàn)了;
實現(xiàn)了 Commons 的 ServiceRegisty 接口(即 ConsulServiceRegistry), 于是服務(wù)注冊也實現(xiàn)了.
再總結(jié): Commons 大發(fā)好.
服務(wù)注冊流程
1.ConsulAutoServiceRegistration 調(diào)用 org.springframework.cloud.consul.serviceregistry.ConsulServiceRegistry#register() 完成注冊
2.接著 register() 調(diào)用 ConsulClient#agentServiceRegister()
3.然后會調(diào)用 AgentConsulClient#agentServiceRegister()
4.生成并發(fā)送請求, 請求地址為 /v1/agent/service/register
總結(jié): 其實和 Eureka 差不多, 只是我跳過了一點點細(xì)節(jié). 基本是就是 與 ServiceRegistry 交互了, 非常的配合 Commons 項目, 就像一個人寫的一樣...
服務(wù)發(fā)現(xiàn)流程
1.首先是 DiscoveryClientServiceInstanceListSupplier 會調(diào)用 ConsulDiscoveryClient#getInstances()
2.接著 getInstances() 會調(diào)用 ConsulClient#getHealthServices()
3.然后就是發(fā)送 HTTP 請求了, 請求地址為 /v1/health/service/, 返回的對象封裝處理下得到 ServiceInstance 的實現(xiàn)類 ConsulServiceInstance.
總結(jié): 這次真的是和 Eureka 類似, 從 Commons 到 ConsulDiscoveryClient, 流程是一樣的. 而其實 ConsulDiscoveryClient 的邏輯也和 EurekaDiscoverClient 類似... 只能說其實服務(wù)注冊發(fā)現(xiàn)這個框架, 我們關(guān)注的功能其實并不是難點. 難點是 server 端的管理.
Nacos
關(guān)鍵類
# 服務(wù)注冊
1.NacosServiceRegistryAutoConfiguration
負(fù)責(zé)添加自動注冊/注銷相關(guān)的 bean
注冊了 NacosAutoServiceRegistration/NacosServiceRegistry/NacosRegistration
2.NacosDiscoveryClientConfiguration
負(fù)責(zé)注冊服務(wù)發(fā)現(xiàn)相關(guān)的 bean
注冊了 NacosDiscoveryClient
3.NacosServiceRegistry
負(fù)責(zé)與 NamingService 對接, 再提供注冊/注銷功能
4.NamingService
與 Nacos Server 端交互
負(fù)責(zé)向 Nacos Server 端注冊/注銷服務(wù)實例
調(diào)用 NamingProxy
5.NamingProxy
負(fù)責(zé)根據(jù)服務(wù)實例信息構(gòu)造注冊/注銷的 Http 請求
6.NacosAutoServiceRegistration
負(fù)責(zé)管理服務(wù)實例的自動注冊/注銷, 與容器生命周期掛鉤
7.NacosRegistration
本地實例對象, 相比服務(wù)實例數(shù)據(jù)更多
# 服務(wù)發(fā)現(xiàn)
1.BlockingLoadBalancer (Commons 項目上)
負(fù)責(zé)調(diào)用實際的負(fù)載均衡器去選擇一個服務(wù)實例
負(fù)責(zé)調(diào)度負(fù)載均衡整個過程, 觸發(fā)相應(yīng)的生命周期.
2.RoundRobinLoadBalancer (Commons 項目上)
實際的負(fù)載均衡策略算法類
負(fù)責(zé)連接 ServiceInstanceListSupplier 從其中獲取服務(wù)實例列表
3.ServiceInstanceListSupplier
定義了獲取服務(wù)實例實例列表的接口(任意方式)
4.DiscoveryClient
定義了從服務(wù)端獲取服務(wù)實例列表的接口(更明確了)
4.DiscoveryClientServiceInstanceListSupplier
ServiceInstanceListSupplier 的實現(xiàn)類
連接 ReactiveDiscoveryClient 類, 將服務(wù)端獲取道德服務(wù)實例列表返回出去
5.NacosDiscoveryClient
實現(xiàn)了 DiscoveryClient 接口
負(fù)責(zé)調(diào)用 NamingService 從 Nacos Server 端獲取服務(wù)實例列表.
6.NamingService
與 Nacos Server 端交互
負(fù)責(zé)向 Nacos Server 端獲取服務(wù)實例列表
7.NacosServiceInstance
服務(wù)實例信息對象
實現(xiàn) ServiceInstance, 與 Commons 對接
# 總結(jié)
實現(xiàn)了 Commons 的 DiscoveryClient 接口(即 NacosDiscoveryClient), 于是服務(wù)發(fā)現(xiàn)實現(xiàn)了;
實現(xiàn)了 Commons 的 ServiceRegisty 接口(即 ConsulServiceRegistry), 于是服務(wù)注冊也實現(xiàn)了.
再總結(jié): Commons 大發(fā)好.
服務(wù)注冊流程
1.NacosAutoServiceRegistration 調(diào)用 NacosServiceRegistry#register 完成注冊
2.接著 register() 調(diào)用 NacosNamingService#registerInstance()
3.然后會調(diào)用 NamingProxy#registerService()
4.生成并發(fā)送請求, 請求地址為 /nacos/v1/ns/instance
總結(jié): 這和 Consul 差不多; 還是繼承 AbstractAutoServiceRegistration 來與 ServiceRegistry 交互了, 也是非常的配合 Commons 項目啊!!
服務(wù)發(fā)現(xiàn)流程
1.首先是 DiscoveryClientServiceInstanceListSupplier 會調(diào)用 NacosDiscoveryClient#getInstances()
2.接著 getInstances() 會調(diào)用 NacosServiceDiscovery#getInstances()
3.然后就是發(fā)送 HTTP 請求了, 請求地址為 /nacos/v1/ns/instance/list, 返回的對象封裝處理下得到 ServiceInstance 的實現(xiàn)類 NacosServiceInstance.
總結(jié): 依然是和 Eureka/Consul 類似, 從 Commons 到 NacosDiscoveryClient, 流程是一樣的. 而其實 NacosDiscoveryClient 的邏輯也和 ConsulDiscoveryClient/EurekaDiscoverClient 類似, 最終總是發(fā)起 HTTP 查詢 server 端, 所以 server 端的代碼才比較有趣啊.
Nacos Config
Nacos Config Client 加載 nacos server 配置原理
關(guān)鍵類
1.NacosConfigBootstrapConfiguration
注冊了一個 NacosPropertySourceLocator
2.NacosPropertySourceLocator
用于從 nacos server 上加載 dataId 對應(yīng)的配置文件到 environment 的 PropertySource 集合中.
3.ConfigService
用于與 nacos server 通信, 獲取配置文件, 乃至訂閱更新
4.NacosPropertySourceBuilder
借助 ConfigService 與 NacosDataParserHandler 從 nacos 獲取 NacosPropertySource
5.NacosDataParserHandler
用于序列化(轉(zhuǎn)碼)服務(wù)端的配置文件數(shù)據(jù)為一個 PropertySource(即 NacosPropertySource)
# 自動刷新
1.NacosConfigAutoConfiguration
2.NacosContextRefresher
總結(jié): ConfigService 與 NacosDataParserHandler 是兩個干事的, 其他都是渣渣!!!
從 server 獲取配置流程
1.在 NacosConfigBootstrapConfiguration 中注冊了一個 PropertySourceLocator(即 NacosPropertySourceLocator), 可用于為 environment 添加 PropertySource
2.然后實現(xiàn) locate 方法, 即 NacosPropertySourceLocator#locate()
3.在 locate() 的最后面, 調(diào)用了 loadApplicationConfiguration()
4.這個方法通過多次調(diào)用 loadNacosDataIfPresent() 加載 dataId 和不同文件后綴以及profile 組和得到不同的 dataId.
5.loadNacosDataIfPresent() 調(diào)用了 loadNacosPropertySource()
6.其最終調(diào)用了 NacosPropertySourceBuilder#loadNacosData()
7.loadNacosData() 調(diào)用 ConfigService#getConfig()
8.getConfig() 會使用 ClientWorker#getServerConfig() 發(fā)起 HTTP 請求從 nacos server 獲取配置文件. 其請求地址是 /v1/cs/configs
還是挺簡單的, 發(fā)個 HTTP 獲取配置文件, 然后轉(zhuǎn)化成一個 PropertySource, 再在 NacosPropertySourceLocator 的 locate 中扔進(jìn) environment 中去. Bingo!!!
從 server 實時更新原理
1.在 NacosConfigAutoConfiguration 注冊了一個 NacosContextRefresher
2.其實現(xiàn)了 ApplicationListener<ApplicationReadyEvent>, 也就是會在容器準(zhǔn)備事件觸發(fā)后調(diào)用 NacosContextRefresher#registerNacosListenersForApplications()
3.在進(jìn)行兩個配置判斷后(即 isRefreshEnabled() 和 PropertySource 的 isRefreshable), 調(diào)用 NacosContextRefresher#registerNacosListener()
4.這個方法通過 NacosConfigService#addListener() 注冊一個 AbstractSharedListener 監(jiān)聽者到 cacheData.
5.而 ClientWorker#checkConfigInfo() 中添加的 LongPollingRunnable 任務(wù), 會在 run() 中執(zhí)行 getServerConfig() 獲取服務(wù)配置文件, 然后與當(dāng)前緩存對比 md5, 若更新則通過 cacheData 取出剛剛添加的 listener, 觸發(fā)事件(即 receiveConfigInfo() )
6.觸發(fā)后又會再發(fā)布一個 RefreshEvent 事件
7.接著流轉(zhuǎn)到 RefreshEventListener.onApplicationEvent()
8.然后又會調(diào)用 ContextRefresher.refresh(), 這個方法中的 refreshEnvironment() 會調(diào)用 addConfigFilesToEnvironment(), 這方法先復(fù)制一個 StandardEnvironment, 將其放入到 SpringApplication 中, 再調(diào)用 SpringApplication 的 run() 走一遍, 會觸發(fā) NacosPropertySourceLocator.locate(), 于是復(fù)制的 environment 里面有新數(shù)據(jù)了, 再將其拷貝替換到當(dāng)前的 environment. 完成 environment 的刷新.
9.接著發(fā)布一個 EnvironmentChangeEvent 事件, 用來刷新 @ConfigurationProperties 注解的 Bean. (這么喜歡事件?不愧是寫出 RocketMQ 的阿里啊!!!)
10.接著在 ConfigurationPropertiesRebinder 中接收到這個事件, 觸發(fā) onApplicationEvent()
11.然后會執(zhí)行 ConfigurationPropertiesRebinder#rebind()
12.其邏輯是將 postProcessBeforeInitialization 中存起來的 ConfigurationPropertiesBean 類型的 map 遍歷, 進(jìn)行重新綁定(即 destroyBean 后再 initializeBean, 則會從新配置重新賦值).
總結(jié): 有的地方(NacosContextRefresher)監(jiān)聽容器初始化添加配置更新的監(jiān)聽者, 自己不干活只發(fā)布配置刷新事件;
有的地方(ClientWorker)添加輪詢?nèi)蝿?wù)獲取服務(wù)端配置文件與本地 cacheData 對比 md5 來判斷是否觸發(fā)配置更新的監(jiān)聽者;
然后有的地方(RefreshEventListener)監(jiān)聽配置刷新事件然后用 SpringApplication.run() 通過觸發(fā) NacosPropertySourceLocator#locate() 為一個復(fù)制出來的 environment 對象添加從服務(wù)端獲取最新配置, 再拷貝到當(dāng)前的 environment 對象, 完成 environment 配置的刷新, 之后發(fā)布 environment 刷新事件;
有的地方(ConfigurationPropertiesRebinder)監(jiān)聽 environment 刷新事件, 然后為 @ConfigurationProperties 注解生成的 bean 重新綁定配置.
再總結(jié): 挺好的, 奧運火炬手啊這是, 不停傳遞!!!!!
關(guān)鍵類
1.NacosConfigAutoConfiguration
注冊一個監(jiān)聽容器初始化事件的 bean (NacosContextRefresher)
2.NacosContextRefresher
再初始化事件觸發(fā)后添加一個 AbstractSharedListener 監(jiān)聽者到 cacheData
3.AbstractSharedListener
當(dāng)服務(wù)端配置與本地不同后觸發(fā)
觸發(fā)后發(fā)布 RefreshEvent 事件
4.CacheData
管理配置更新事件監(jiān)聽者
對比服務(wù)端配置與本地配置的 md5
md5 不同則觸發(fā) AbstractSharedListener
5.RefreshEvent
通過容器發(fā)布
當(dāng)服務(wù)端配置發(fā)送更新觸發(fā)
觸發(fā)后調(diào)用 ContextRefresher#refresh()
6.LongPollingRunnable
輪詢?nèi)蝿?wù), 獲取服務(wù)端最新配置, 與 cacheData 對比
7.ClientWorker
管理輪詢?nèi)蝿?wù)(LongPollingRunnable)
負(fù)責(zé)與 nacos 服務(wù)端通信
8.ContextRefresher
監(jiān)聽 RefreshEvent 事件
負(fù)責(zé)獲取服務(wù)端最新配置到 environment 對象中
發(fā)布 EnvironmentChangeEvent 事件
9.EnvironmentChangeEvent
通過容器發(fā)布
當(dāng) environment 對象更新后觸發(fā)
觸發(fā)后調(diào)用 ConfigurationPropertiesRebinder#rebind()
10.ConfigurationPropertiesRebinder
監(jiān)聽 EnvironmentChangeEvent 事件
負(fù)責(zé)重新綁定用了 @ConfigurationProperties 注解的 Bean 的值
graph TB
A1(NacosConfigAutoConfiguration)
A2(NacosContextRefresher)
A3(ApplicationReadyEvent)
A4(NacosContextRefresher#registerNacosListener)
A5(NacosConfigService#addListener)
A7(ClientWorker)
A8(LongPollingRunnable#輪詢?nèi)蝿?wù))
A9(NacosConfigService#getServerConfig)
B1(AbstractSharedListener#配置更新事件的監(jiān)聽者)
B2(cacheData)
B3(RefreshEvent)
B4(RefreshEventListener)
B5(ContextRefresher#refresh)
B6(ContextRefresher#refreshEnvironment)
B7(ContextRefresher#addConfigFilesToEnvironment)
B8(SpringApplication#run)
B9(StandardEnvironment)
C1(NacosPropertySourceLocator#locate)
C2(當(dāng)前容器的 environment 對象)
C3(EnvironmentChangeEvent)
C4(ConfigurationPropertiesRebinder#onApplicationEvent)
C5(ConfigurationPropertiesRebinder#rebind)
C6(用了 ConfigurationProperties 注解 Bean)
A1--注冊了一個-->A2
A2--監(jiān)聽了-->A3
A3--事件觸發(fā)后調(diào)用-->A4
A4--通過-->A5
A5--注冊了一個-->B1
A7--構(gòu)造方法中創(chuàng)建了一個-->A8
A8--run 方法中調(diào)用-->A9
A9--獲取最新配置與-->B2
B2--比較 md5, 不同則觸發(fā)-->B1
B1--事件觸發(fā)后發(fā)布-->B3
B4--監(jiān)聽了-->B3
B3--事件觸發(fā)后調(diào)用-->B5
B5--調(diào)用-->B6
B6--調(diào)用-->B7
B7--利用-->B8
B7--復(fù)制當(dāng)前容器 environment 對象到-->B9
B8--觸發(fā)-->C1
C1--獲取了最新配置到-->B9
B9--復(fù)制最新配置回到-->C2
C2--更新完配置后發(fā)布-->C3
G1(ConfigurationPropertiesRebinder)--監(jiān)聽了-->C3
C3--事件觸發(fā)后調(diào)用-->C4
C4--調(diào)用-->C5
C5--獲取新配置綁定到-->C6