Alibaba?Nacos配置中心動態(tài)感知原理示例解析
引言
Nacos提供兩大核心功能,服務注冊發(fā)現(xiàn),配置中心。對應Nacos的架構圖,分別是Naming Service和Config Service,其中Config Service是實現(xiàn)配置中心的核心模塊。實現(xiàn)了版本管理、灰度發(fā)布、監(jiān)聽管理、推送軌跡等功能。針對配置中心,當我們通過控制臺或API修改配置之后,客戶端能動態(tài)獲取到修改后的配置,那么配置中心是如何實現(xiàn)動態(tài)感知的呢?
動態(tài)監(jiān)聽之Pull和Push
當Nacos Config Server上的配置發(fā)生變化時,需要讓相關的應用程序感知到配置的變化,這就需要客戶端對感興趣的配置實現(xiàn)監(jiān)聽。那么客戶端是如何實現(xiàn)配置變更實時更新的呢?
一般來說,客戶端與服務端的交互無非兩種:Pull模式和Push模式,一個是客戶端主動拉取,一個是服務端主動推送。
- Pull模式:服務端和客戶端之間需要維護長連接,客戶端多的情況下耗內(nèi)存、需要心跳機制檢測連接狀態(tài)。
- Push模式:客戶端需要定時拉取數(shù)據(jù),不能保證實時性,服務端長時間不更新的情況下,定時任務為無效更新,浪費資源。
Nacos的Pull模式
Nacos采用的是Pull模式,不過不是簡單的Pull,而是一種長輪詢機制。結(jié)合Pull和Push兩者的優(yōu)勢,客戶端采用長輪詢的方式發(fā)起Pull請求,檢查服務配置消息是否發(fā)生變化,如果更新,客戶端會根據(jù)變更的內(nèi)容獲取最新配置信息。

所謂的長輪詢,就是客戶端發(fā)起Pull請求之后,服務端如果發(fā)生配置變更則立即返回,如果服務端和客戶端配置是保持一致的,那么服務端會“Hold”住這個請求,在指定時間內(nèi)不返回結(jié)果,直到這段時間內(nèi)配置發(fā)生變化。這個長連接默認超時時間是30s。
服務端收到請求后,先檢查配置是否發(fā)生變化,如果沒變化,則設置一個定時任務,延期29.5s執(zhí)行,并且把當前的客戶端長輪詢連接加入allSubs隊列。這里有兩種方式觸發(fā)連接返回。
- 等待29.5s自動觸發(fā)檢查機制,無論是否發(fā)生變化,都會返回。
- 在29.5s內(nèi),通過Nacos控制臺或者API的形式對配置進行了修改,會觸發(fā)
ConfigDataChangeEvent事件。

Nacos的動態(tài)感知
前面我們已經(jīng)知道客戶端通過長輪詢請求來獲取配置變更,但是定時任務是延遲執(zhí)行的,那并沒有達到實時的目的,當通過控制臺或者API修改配置時,那Nacos是如何實時動態(tài)更新的呢?
LongPollingService 監(jiān)聽事件類
LongPollingService繼承AbstractEventListener,AbstractEventListener是事件抽象類,它有一個onEvent抽象方法,而LongPollingService實現(xiàn)了這個方法
@Override
public void onEvent(Event event) {
if (isFixedPolling()) {
// ignore
} else {
if (event instanceof LocalDataChangeEvent) {
LocalDataChangeEvent evt = (LocalDataChangeEvent)event;
scheduler.execute(new DataChangeTask(evt.groupKey, evt.isBeta, evt.betaIps));
}
}
}LongPollingService可以看到LocalDataChangeEvent事件,這個事件是服務端的配置數(shù)據(jù)發(fā)生變化時發(fā)布的一個事件。onEvent方法中通過線程池來執(zhí)行一個DataChangeTask任務。
DataChangeTask線程
DataChangeTask是一個線程,實現(xiàn)了Runnable接口,對應的run()如下:
class DataChangeTask implements Runnable {
@Override
public void run() {
try {
ConfigService.getContentBetaMd5(groupKey);
for (Iterator<ClientLongPolling> iter = allSubs.iterator(); iter.hasNext(); ) {
ClientLongPolling clientSub = iter.next();
if (clientSub.clientMd5Map.containsKey(groupKey)) {
// 如果beta發(fā)布且不在beta列表直接跳過
if (isBeta && !betaIps.contains(clientSub.ip)) {
continue;
}
// 如果tag發(fā)布且不在tag列表直接跳過
if (StringUtils.isNotBlank(tag) && !tag.equals(clientSub.tag)) {
continue;
}
getRetainIps().put(clientSub.ip, System.currentTimeMillis());
iter.remove(); // 刪除訂閱關系
LogUtil.clientLog.info("{}|{}|{}|{}|{}|{}|{}",
(System.currentTimeMillis() - changeTime),
"in-advance",
RequestUtil.getRemoteIp((HttpServletRequest)clientSub.asyncContext.getRequest()),
"polling",
clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey);
clientSub.sendResponse(Arrays.asList(groupKey));
}
}
} catch (Throwable t) {
LogUtil.defaultLog.error("data change error:" + t.getMessage(), t.getCause());
}
}
DataChangeTask(String groupKey) {
this(groupKey, false, null);
}
DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps) {
this(groupKey, isBeta, betaIps, null);
}
DataChangeTask(String groupKey, boolean isBeta, List<String> betaIps, String tag) {
this.groupKey = groupKey;
this.isBeta = isBeta;
this.betaIps = betaIps;
this.tag = tag;
}
final String groupKey;
final long changeTime = System.currentTimeMillis();
final boolean isBeta;
final List<String> betaIps;
final String tag;
}- 遍歷allSubs中的客戶端長輪詢請求。
- 比較每個客戶端長輪詢請求攜帶的groupKey,如果服務端變更的配置和客戶端請求關注的配置一致,則直接返回,這里調(diào)用
clientSub.sendResponse()方法返回。
總結(jié)
好了,最后整理下nacos實時動態(tài)感知的流程如下:
- 客戶端通過長輪詢的方式發(fā)起Pull請求服務端獲取配置變更;
- 服務端判斷如果是長輪詢請求,對比數(shù)據(jù)的MD5,如果發(fā)生變化則直接返回,否則通過延遲任務執(zhí)行
ClientLongPolling線程; - 配置中心修改配置后,會發(fā)布
ConfigDataChangeEvent事件; EventDispatcher觸發(fā)事件,通知監(jiān)聽者。LongPollingService就是監(jiān)聽者之一。- 監(jiān)聽者通過線程池開啟定時線程,遍歷客戶端的所有長輪詢的請求, 通過groupKey匹配到對應請求,直接返回。
以上就是Alibaba Nacos配置中心動態(tài)感知原理示例解析的詳細內(nèi)容,更多關于Alibaba Nacos配置中心動態(tài)感知的資料請關注腳本之家其它相關文章!
相關文章
SpringCloud基于Feign實現(xiàn)遠程調(diào)用的問題小結(jié)
這篇文章主要介紹了SpringCloud基于Feign遠程調(diào)用,通過使用 Feign 的方式,我們可以更加優(yōu)雅地進行多參數(shù)的遠程調(diào)用,避免了手動拼接URL或構建復雜的請求體,需要的朋友可以參考下2024-02-02
Spring Boot中的SpringSecurity基礎教程
Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。它實際上是保護基于spring的應用程序的標準Spring Security是一個框架,側(cè)重于為Java應用程序提供身份驗證和授權,這篇文章主要介紹了Spring Boot中的SpringSecurity學習,需要的朋友可以參考下2023-01-01
Java8 Stream對兩個 List 遍歷匹配數(shù)據(jù)的優(yōu)化處理操作
這篇文章主要介紹了Java8 Stream對兩個 List 遍歷匹配數(shù)據(jù)的優(yōu)化處理操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
kafka 啟動報錯 missingTopicsFatal is true的解決
這篇文章主要介紹了kafka 啟動報錯 missingTopicsFatal is true的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07

