動態

詳情 返回 返回

Nacos源碼分析-更新配置時服務端做了什麼 - 動態 詳情

在 Nacos console 修改了配置以後,服務端底層怎麼存儲配置?客户端怎麼知道配置修改了?怎麼通知集羣其他節點?讓我來揭開它神秘的面紗。

服務端接收配置更新請求

在控制枱頁面更新一項配置,看看控制枱發送了什麼請求給服務端。

image-20241120140118352

image-20241120140048490

控制枱發送了一個 POST 請求:/nacos/v1/cs/configs,在官方 API指南 可以找到 API 定義。

image-20241120140623329

我 Nacos 源碼是 2.* 版本,但是打開的 console 發佈配置請求的 API 還是 1.* 版本。

官方説v2 是兼容 v1 的,在源碼的 Controller 層面兩個版本使用的也是相同的 Service 類處理請求。

請求進入com.alibaba.nacos.config.server.controller.ConfigController#publishConfig,封裝配置和請求信息後調用ConfigOperationService#publishConfig

image-20241120142833789

publishConfig方法做了兩件事:

  • 添加或更新配置信息到數據庫
  • 發佈配置數據變更事件ConfigDataChangeEvent,這是客户端能感知配置更新的根本原因

image-20241121165044280

ConfigDataChangeEvent被兩個類監聽:

  • DumpService: 將配置信息轉存到本地磁盤
  • AsyncNotifyService:通知集羣其他節點和訂閲配置的客户端配置發生變更

DumpService:轉存配置信息到本地

DumpService在構造函數中訂閲了ConfigDataChangeEvent事件,當監聽到事件發生,調用handleConfigDataChange方法。

image-20241121170312201

我回看了一下前面的文章,一些不重要的方法也有截圖,白白的浪費了大家的閲讀時間,對於簡單的方法後面只會列出方法調用鏈路
  • 👇handleConfigDataChange(Event event)
  • 👇dumpFormal(String dataId, String group, String tenant, long lastModified, String handleIp)

dumpFormal方法作用是轉存正式數據到本地磁盤和緩存,向任務管理器dumpTaskMgr提交了一個 任務DumpTask

image-20241121171502273

dumpTaskMgrTaskManager實例,在DumpService的構造函數中創建了該實例,並且為它指定了DumpProcessor作為默認任務處理器

image-20241122095303734

TaskManager類本身沒有實現執行任務的方法,而是繼承自 NacosDelayTaskExecuteEngine, 來看下他是如何執行任務的。

在構造函數中創建單線程執行器,確保任務都能處理成功,並且每個任務之間間隔 100 毫秒。

image-20241121173736126

執行器會一直執行ProcessRunnableProcessRunnable實現了Runnable接口,在run方法內調用processTaks方法處理任務

image-20241121174039790

通過 taskKey 獲取NacosTaskProcessor類型的處理器,調用處理器的process方法處理任務。這裏實現了任務重試機制,如果執行失敗則放入隊列稍後執行。

NacosTaskProcessor是一個處理器接口,他有很多實現,分別用來處理不同的任務

image-20241121175209799

dump 正式數據的任務則是由上面指定的默認任務處理器DumpProcessor來處理。

調用處理器的的process方法處理任務:

  • 👇process(NacosTask task)
  • 👇DumpConfigHandler.configDump(build.build())
  • 👇ConfigCacheService.dump(dataId, group, namespaceId, content, lastModified, event.getType(), event.getEncryptedDataKey())
  • 👇dumpWithMd5(dataId, group, tenant, content, null, lastModifiedTs, type, encryptedDataKey)
  • 👇ConfigDiskServiceFactory.getInstance().saveToDisk(dataId, group, tenant, content)

調用saveToDisk方法保存到磁盤;updateMd5方法更新本地緩存的 md5和最後更新時間,併發布LocalDataChangeEvent事件。

image-20241122173301754

進入com.alibaba.nacos.config.server.service.dump.disk.ConfigRawDiskService#saveToDisk, 可以看到targetFile就是nacos.home下要更新的文件路徑,向文件寫入新的配置信息。image-20241122173750569

繼續看updateMd5方法,更新本地JVM緩存中的配置以後,發佈LocalDataChangeEvent本地數據變更事件,這個事件的目的就是告訴客户端:配置發生了變更

image-20241122174633242

AsyncNotifyService:通知集羣節點

上面DumpService把修改後的配置更新到數據庫,磁盤,JVM緩存了,但是這都是在本機發生的,集羣的其他節點還是舊的配置呢,那麼如何同步呢?

AsyncNotifyService異步通知服務就發揮作用了,它也監聽了ConfigDataChangeEvent事件,所以當配置變更時,它就負責通知其它節點。

image-20241124205802480

當事件發生時,調用handleConfigDataChangeEvent()

  • 獲取除了自己以外的集羣成員
  • 為每個成員創建一個NotifySingleRpcTask放入同一隊列中
  • 創建一個AsyncRpcTask去處理隊列中的任務

image-20241124205934568

AsyncRpcTask調用executeAsyncRpcTask()處理任務

  • 依次從隊列取出任務
  • 構建集羣同步請求體ConfigChangeClusterSyncRequest,集羣其他節點收到此類型請求,會進行數據同步
  • 檢查集羣成員節點健康狀態
  • 調用configClusterRpcClientProxy.syncConfigChange()通知配置變更

image-20241124210552163

集羣成員收到請求會做什麼呢

ConfigChangeClusterSyncRequestHandler繼承了RequestHandler,處理ConfigChangeClusterSyncRequest類型的請求。

  • 從請求體中拿到配置修改的關鍵信息,比如dataId,group
  • 調用DumpService.dump()
  • dump()調用dumpFormal()轉存配置信息到本地

image-20241124211050621

通知客户端配置變更

上面在DumpService小節中講到,更新了本地JVM緩存以後,發佈了LocalDataChangeEvent本地數據變更事件。

一共有兩處監聽了LocalDataChangeEvent本地數據變更事件

  • RpcConfigChangeNotifier
  • LongPollingService

RpcConfigChangeNotifier

RpcConfigChangeNotifier訂閲了LocalDataChangeEvent事件,事件發生時調用configDataChanged方法通知配置的監聽者。

image-20241122175117634

  • 首先獲取監聽配置的客户端列表、
  • 創建通知請求ConfigChangeNotifyRequest,客户端收到這個請求後,會重新獲取配置
  • 為每個客户端創建RpcPushTask

image-20241124192118777

然後將任務放入執行器準備執行。

  • 👇ConfigExecutor.scheduleClientConfigNotifier(retryTask, retryTask.getTryTimes() * 2, TimeUnit.SECONDS)

Rpc推送任務

RpcPushTask實現了Runnable接口,它的run方法做了兩件事:

  • 檢查TPS,避免服務器資源耗盡。官方稱為反脆弱,可參考反脆弱插件image-20241124185123706
  • 推送請求並設置回調

調用RpcPushService.pushWithCallback()發送GRPC請求通知客户端配置變更

image-20241124190040297

在客户端的ClientWorker中註冊了ConfigChangeNotifyRequest的處理器,當收到請求後調用notifyListenConfig()更新配置。

在《nacos源碼分析-客户端啓動與配置動態更新的實現細節》中講過ClientWorker 主要用於封裝與 Nacos 配置服務的交互邏輯,提供配置的獲取、監聽和更新等功能。

image-20241124192540984

LongPollingService

Nacos1.*版本 是基於長輪詢機制監聽配置變更,客户端調用/nacos/v1/cs/configs/listenerapi監聽配置(參考API指南)。

當配置發生變更時,也需要通知那些通過長輪詢監聽的客户端。

從下圖可以看到本地數據變更事件LocalDataChangeEvent發生時,創建了一個DataChangeTask放入執行器執行。

image-20241124201940623

DataChangeTask遍歷全部訂閲者,如果客户端監聽了修改的配置 key,那麼移除和客户端的訂閲關係,因為客户端即將接收響應,然後向客户端發送響應

image-20241124203348938

調用sendResponse()響應客户端請求

調用generateResponse()返回修改的配置 key 給客户端

image-20241124203703381

原理圖

ProcessOn 地址:www.processon.com/diagraming/…

Nacos配置變更通知集羣節點和客户端.png

總結

現在可以來解答開頭的問題了。

1.服務端底層怎麼存儲配置?

  • 添加或更新配置信息到數據庫
  • 發佈配置數據變更事件ConfigDataChangeEvent
  • DumpService監聽到ConfigDataChangeEvent事件,將配置保存到本地磁盤和JVM緩存

2.客户端怎麼知道配置修改了?

  • 本地緩存更新後,發佈LocalDataChangeEvent事件
  • RpcConfigChangeNotifier監聽到本地數據變更事件LocalDataChangeEvent
  • 創建通知請求ConfigChangeNotifyRequest,發起 GRPC調用通知客户端
  • 通過長輪詢監聽的客户端,則由LongPollingService攜帶更新的配置 key 響應客户端

3.怎麼通知集羣其他節點?

  • AsyncNotifyService監聽配置數據變更事件ConfigDataChangeEvent
  • 監聽到事件發生後,向集羣成員發送ConfigChangeClusterSyncRequest,告知變更的配置信息

Add a new 評論

Some HTML is okay.