首頁>技術>

背景

前面兩個spring boot2.x整合nacos系列的教程裡面,我們使用docker初步搭建了nacos單機版的服務端,並且介紹了我們在平時開發的過程中,如何正確高效的使用nacos,讓nacos無限的減少我們本地的配置,本小節主要介紹我們在本地如何能夠拉取到nacos服務端的配置的原理,和原始碼基本講解,讓大家知道其中的基本流程,並且能夠掌握其中的原始碼技巧,希望大家在有的工作中,能夠有機會去使用這些優秀原始碼的技巧,讓我們平時的業務程式碼寫的更加精緻

tips:本小節大概閱讀的時間在20分鐘,分上下兩小節,如果您能靜下心來安心地讀完,相信你會覺得受益匪淺,最後希望大家一起進步~本文為原創,如果您希望,幫忙關注一下,您的支援是我寫作最大的動力~

閱讀原始碼的入口

首先,在閱讀原始碼之前,我們先找到閱讀原始碼的入口,在nacos的原始碼中,子專案example中有一個示例類ConfigExample,我們先執行一下,看下效果

com.alibaba.nacos.example.ConfigExample

原始碼示例如下

最基本的main函式nacos客戶端獲取服務端資料方法示例

執行main函式,我們可以成功的看到控制檯列印輸出如下

我們粗略地看下ConfigExample的main函式實現,我們指定了nacos服務端的地址IP,dataId,groupId,namespace 然後我們就可以獲取到服務端返回給我們的資料,從這一點可以明確一個遠端的properties的唯一標識是如下幾個因素可以確認

groupIddataIdnamespace

我們回到剛才的原始碼,我們還配置了一個listener,nacos最大的一個特性就是支援熱更新,當nacos服務端的properties更新的時候,所有的客戶端(具體說來,是訂閱過對應dataId,groupId的客戶端)都能"立即"(具體說應該是準實時)感知到服務端的資料變更,我們看下當註冊一個如下的listener的時候,我們修改一下配置中心的屬性,看看該listener能不能感知到變化

我們能發現控制檯出現了我們意料之中的變化,nacos的客戶端感知到了服務端的資料變化

本章節,我們就介紹一下如下2個內容

客戶端如何拉取服務端的配置資訊客戶端如何感知到服務端的配置變化(重點)閱讀原始碼前的知識準備

1.Servlet3.0新特性,非同步處理的支援

servlet3.0開始開始支援非同步處理,所謂的非同步處理就是,當servlet接受到請求的時候,可能需要一段耗時很長的業務處理時間,這個時候業務執行緒就是tomcat或者其他web容器的servlet執行緒,這個執行緒就會一直消耗在處理業務執行緒上來,無法得到釋放,如果大量客戶端請求到來的時候,web容器就沒有剩餘執行緒處理業務邏輯,這就會影響效能,所以servlet3.0開始有了一個新的特性:servlet的執行緒接受到請求之後,把邏輯複雜耗時時間長的操作交給非同步執行緒,自己則迴歸到web容器執行緒池中,這樣能夠快速地釋放servlet執行緒,得到資源的最大利用,非同步執行緒處理結束之後,可以返回web伺服器端的響應資料(以後有機會做一個spring boot 整個非同步處理新特性的文章)

2.ScheduledExecutorService處理類的scheduledWithFixedDelay方法的含義,四個引數

第一個Runnable,表示要執行的任務函式體第二個引數initialDelay:表示第一個任務開始執行的延遲執行時間第三個引數delay:表示上一個任務執行結束之後,下一個任務執行的時間,這邊必須要注意的是上一個任務執行完之後開始計時的延遲時間第四個引數Timeout,時間單位

3.ScheduledExecutorService處理類的schedule方法,這個方法表示延遲執行,且只調度一次,這個方法返回一個future物件,可以通過future.get,cancel等方法進行一些操作,來達到我們要的效果

以上三個就是我們在本章需要擁有的基本知識點,在接下來的核心原始碼分析中,了解這些你就會有事半功倍的效果,這邊先賣一個關子

閱讀原始碼之前的思考

我個人覺得閱讀原始碼之前,我們一定要想好為什麼要閱讀原始碼,要解決我們什麼困惑,帶著問題去看原始碼,這樣我們就能在看原始碼的過程中,不迷失方向

1.我們之前說過nacos是典型的C/S架構,如果客戶端想要獲取服務端的properties配置資訊,根據服務端的地址,dataId,groupId,namespace應該說傳送一個http請求就可以成功獲取到資訊,如果這個程式碼給我們寫是不是寫一個httpClient請求就可以完成獲取屬性的操作了?感覺這樣並不是很難

2.客戶端是如何獲取到nacos屬性變更通知的,是服務端主動通知的,還是客戶端有一個定時任務不斷地去查詢對應的dataId,group組下的properties是否有變更呢,其實這個問題就是我們經常說的推拉模型,按照目前所有主流框架的邏輯,應該不是推模式,因為如果使用服務端變更的時候,服務端主動通知客戶端屬性變更,服務端的效能會成很大的問題,首先你要保持很多的長連線,還有就是當客戶端很多的時候,監聽的節點數以萬計的時候,服務端的效能應該就是很大的問題,所以初步定論,現代優秀的nacos框架,不應該會是推模型

至於為什麼不是"推"模型,還有一個經驗問題

1.zookeeper有一個watch的機制,現在zookeeper的效能一直是大家所詬病的,當zookeeper的被watch的節點很多的時候,或者watch的客戶端很多的時候,因為zookeeper就是使用推模型,會導致zookeeper服務端效能急劇下降,推送的不及時

2.看過阿里巴巴另一個優秀的訊息中介軟體RocketMQ的人應該都知道RocketMQ使用的就是拉模型,解決的就是服務端效能的問題,使用的是"長輪詢"的方式,所以這邊猜想,同樣是阿里中介軟體團隊,應該也會考慮到這個問題(RocketMQ的整合和原始碼閱讀以後也會一起整理)

帶著如上的2個問題就開始進行原始碼的閱讀,應該會很輕鬆

第一個問題:客戶端如何獲取到服務端的配置資訊的?

回到我們剛才的測試程式碼ConfigExample的入口,可以看到原始碼中使用NacosFactory的#createConfigService方法,返回了一個ConfigService

ConfigService明顯就是一個核心介面,方法列表如下,根據方法名,有getConfig,addListener等我們需要的一些客戶端常用的方法,所以我們這邊應該深入這個方法的研究

我們可以看到這邊跟我們平時想象的不一樣,一般factory的靜態方法都是返回的一個單例,而這邊卻是使用反射建立一個繼承ConfigService的實現類的NacosConfigService

我們可以重點關注一下NacosConfigService的建構函式,原始碼如下,配置好編碼格式和namespace之後,我們重點看下下面原始碼中,紅框標註的2個部分,第一個部分是用裝飾者模式建立的帶有統計的httpclient,名字叫做ServerHttpAgent,跟我們設想的一樣,也是使用http協議與nacos服務端進行互動的,那麼獲取服務端的配置資訊,應該並不難,然後又建立了一個ClientWorker,顧名思義,感覺這個類就是nacos客戶端的總代理一樣,應該所有的核心邏輯操作就在這邊了,接下來我們應該會跟這個ClientWorker多次打交道

回到我們剛才的main函式,獲取配置屬性的地方,方法名叫做getConfig的地方

我們可以跟蹤原始碼分析,很容易就看到具體在NacosConfigService中的實現方法getConfigInner,可以看到第一段邏輯程式碼就是優先獲取本地的關於dataId,groupId,namespace的配置,如果能獲取到則優先返回,為什麼優先獲取本地呢,其實nacos也是做出了效能考慮,否則每次都去查詢伺服器端的資料,這樣會導致伺服器端的壓力負載過大,特別是成千上萬個節點連線到伺服器端的時候,所以優先讀取本地配置

如果本地檔案不存在對應的配置,相當於本地快取沒有,這種情況一般都是第一次請求的時候,本地沒有快取,這個時候就需要請求服務端資料,接著看原始碼,我們可以看到接下來就是使用worker去獲取服務端的資料了,也就是前面建構函式初始化好的clientworker

我們感覺快接近真相大白了,我們接著看clientworker的getServerConfig方法,我們之前說的使用httpClient獲取資料,這邊也是這麼做的,具體的引數,我在下圖的原始碼中都一一說明標註了一下,最後請求的地址應該是http://ip:8848/v1/cs/configs的Get請求,服務端獲取配置資訊的原始碼,我們下幾個小節分析,在這邊我們只關注獲取資料的邏輯,最後客戶端成功獲取到了伺服器端的資料,總而言之,還是比較簡單的

接下來就有一段基本的邏輯處理,大家也不要忘記,把從服務端獲取到的資料持久化到本地磁碟,這樣就可以與剛才優先獲取本地的配置做到相呼應了,可以看到當與服務端互動,服務端返回成功或者沒找到的時候,都可以將資訊持久化到本地磁碟,這樣就可以很方便地優先從本地獲取了

到此為止,客戶端獲取到服務端的配置資訊,主體流程已經分析結束了,接下來就要分析客戶端如何感知到服務端的配置變更了

小結

本小節內容相對比較簡單,也是我們閱讀原始碼的第一篇文章,主要目的就是希望大家在閱讀原始碼之前,有一些自己的思考,帶著問題,和帶著自己的想法去閱讀原始碼,想著如果是自己應該怎麼實現,這些寫中介軟體的大牛又是怎麼實現的,這樣當看到別人實現的亮點的時候,以後自己就可以去模仿,去"抄寫",慢慢地就會變得更加強大一點,與大牛的差距就會慢慢地縮小一些

廢話不多說,還是總結一下吧,nacos客戶端獲取服務端的配置資訊,還是比較簡單的,依靠clientworker去獲取,這是一個核心類,優先獲取本地的配置,如果本地沒有快取,就去獲取nacos伺服器端的資料

下一個小節,應該就是本小節的重點內容,如何客戶端如何感知到伺服器端的資料變化,客戶端感知到變化之後,更新本地磁碟的配置資訊,這樣就可以永遠保持本地磁碟是最新的配置資訊了~

如果您看到這裡,覺得我寫的不錯的話,希望您點一個關注,或者你現在沒有時間看的話,點一個收藏,在您閒暇的時候,翻閱看看,相信我們可以一起學習進步,如果寫的有不對的地方,歡迎批評指出

  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Vue3.0採用新特性Proxy來實現資料狀態的響應,它的原理是什麼?