Nacos-技術專題-配置中心實現

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"什麼是 Nacos"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":"是阿里發起的開源項目,地址:"},{"type":"link","attrs":{"href":"https://link.zhihu.com/?target=https%3A//github.com/alibaba/nacos","title":null},"content":[{"type":"text","text":"https://github.com/alibaba/nacos"}]},{"type":"text","text":"。"},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":"主要提供兩種服務,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"一是配置中心,支持配置註冊、變更下發、層級管理等,意義是不停機就可以動態刷新服務內部的配置項;"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":"二是作爲命名服務,提供服務的註冊和發現功能,通常用於在 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":"RPC"}],"marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":" 框的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":"Client"}],"marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":" 和 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":"Server"}],"marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#9254DE","name":"purple"}},{"type":"strong"}],"text":" 中間充當媒介,還附帶有健康監測、負載均衡等功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 本文聚焦於 "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 的第一塊功能,即配置中心的實現。先敘述一個配置中心通常需要哪些組成部分,再結合 "},{"type":"codeinline","content":[{"type":"text","text":"Nacos 1.1.4"}]},{"type":"text","text":" 的源碼,探究一下這些設計是如何反映在源碼上的。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"配置中心的架構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 配置中心本身並不複雜,前提是你先將 "},{"type":"codeinline","content":[{"type":"text","text":"CAP"}]},{"type":"text","text":" 的取捨問題晾在一邊的話。"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"配置中心最基礎的功能就是存儲一個鍵值對,用戶發佈一個配置("},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"configKey"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"),然後客戶端獲取這個配置項("},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"configValue"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":");進階的功能就是當某個配置項發生變更時,將變更告知客戶端刷新舊值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"下方的架構圖,簡要描述了一個配置中心的大致架構,用戶可以通過管理平臺發佈配置,通過 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"HTTP"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":" 調用將配置註冊到服務端,服務端將之保存在 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"MySQL"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"等持久化存儲引擎中;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":" 用戶通過客戶端 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"SDK"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"訪問服務端的配置,同時建立 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"HTTP"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"的長輪詢監聽配置項變更,同時爲了減輕服務端壓力和保證容災特性,配置項拉取到客戶端之後會保存一份快照在本地文件中,SDK 優先讀取文件裏的內容。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 例如配置"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"分層設計,權限校驗,客戶端長輪詢的間隔設置"},{"type":"text","text":",服務端每次查詢都需要訪問 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"MySQL"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"麼,配置變更是主動推送還是等定時輪詢觸發等,"},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"還有就是運維高可用方面的工作(私以爲這個是配置中心的精華),例如節點跨地域部署,網絡分區時配置如何保證可寫可推送變更等。"},{"type":"text","marks":[{"type":"strong"}],"text":"真正實現一個高質量的配置中心,還是需要長時間打磨的。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7c2bac31e8bfcde6e89f547a316cb1be.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Nacos 使用示例"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下文涉及的源碼均基於 Nacos 1.1.4 版本"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"官方代碼示例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 先看一下官方文檔中對於 "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 的 "},{"type":"codeinline","content":[{"type":"text","text":"API"}]},{"type":"text","text":" 使用的示例代碼,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 第一步是傳遞配置,新建 "},{"type":"codeinline","content":[{"type":"text","text":"ConfigService"}]},{"type":"text","text":" 實例,第二步可以通過相應的接口獲取配置和註冊配置監聽器。使用方式非常簡單易懂,不再贅述。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"try {\n // 傳遞配置\n String serverAddr = \"{serverAddr}\";\n String dataId = \"{dataId}\";\n String group = \"{group}\";\n Properties properties = new Properties();\n properties.put(\"serverAddr\", serverAddr);\n // 新建 configService\n ConfigService configService = NacosFactory.createConfigService(properties);\n String content = configService.getConfig(dataId, group, 5000);\n System.out.println(content);\n // 註冊監聽器\n configService.addListener(dataId, group, new Listener() {\n @Override\n public void receiveConfigInfo(String configInfo) {\n System.out.println(\"recieve1:\" + configInfo);\n }\n @Override\n public Executor getExecutor() {\n return null;\n }\n \t});\n\t} catch (NacosException e) {\n // TODO -generated catch block\n e.printStackTrace();\n\t}"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Properties 解讀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"serverAddr"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 傳遞的是配置中心服務端的地址列表,被內部名爲 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ServerListManager"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 的類解析成地址列表進行管理,進行 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"HTTP"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 調用時會從中選擇存活的機器拼接成 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"URL"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 完成調用,一旦在調用時該地址拋異常,則客戶端會有一些處理措施,例如轉換下次選擇的節點等。"},{"type":"text","text":"值得注意的是,通常在實踐中不會採取這種硬編碼的方式,可以將其配置在 "},{"type":"codeinline","content":[{"type":"text","text":"Zookeeper"}]},{"type":"text","text":" 或者註冊發現中心上,在啓動時動態拉取。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置項的層級設計"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 官方給出了這樣的設計圖:"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a4/a4ebc2f6eede79b7efb7ae090bf590ee.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"dataId"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 可以理解爲用戶自定義的配置健,"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"group"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 可以理解爲配置分組名稱,這個屬於配置層級設計的概念。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","marks":[{"type":"strong"}],"text":"簡單來說,配置中心會通過層次設計,來支持不同的分區,以此區分不同的環境、不同的分組、甚至不同的開發者,滿足在開發過程中灰度發佈、測試等需求。"},{"type":"text","text":"因此怎樣設計都可以,只要有含義就好,例如下圖也不是不可以。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28d3bcbac84f1945497b1f1a44bbf061.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Nacos 客戶端解析"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"獲取配置"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 獲取配置的主要方法是 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"NacosConfigService"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"類的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"getConfigInner"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"方法,通常情況下該方法直接從本地文件中取得配置的值,如果本地文件不存在或者內容爲空,則再通過 "},{"type":"codeinline","content":[{"type":"text","text":"HTTP GET"}]},{"type":"text","text":" 方法從遠端拉取配置,並保存到本地快照中。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2a/2a3c7cd6d6638a1b89a234f916d41985.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 當通過 "},{"type":"codeinline","content":[{"type":"text","text":"HTTP"}]},{"type":"text","text":" 獲取遠端配置時,"},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 提供了兩種熔斷策略,一是超時時間,二是最大重試次數,默認重試三次。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"註冊監聽器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 配置中心客戶端對某個配置項註冊監聽器是很常見的需求,達到在配置項變更時候執行回調的功能。"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"iconfig.addListener(dataId, group, ml);\niconfig.getConfigAndSignListener(dataId, group, 1000, ml);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 可以通過以上方式註冊監聽器,它們內部的實現均是調用 "},{"type":"codeinline","content":[{"type":"text","text":"ClientWorker"}]},{"type":"text","text":" 類的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"addCacheDataIfAbsent"}]},{"type":"text","text":"。其中 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"CacheData"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"是一個維護配置項和其下注冊的所有監聽器的實例,私以爲這個名字取得並不好,不容易理解。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 所有的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"CacheData"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"都保存在 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ClientWorker"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"類中的原子 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"cacheMap"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"中,其內部的核心成員有:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/89/89f9d49d9ae7492ae199817cc1289f78.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 其中,"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"content"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 是配置內容,"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"MD5"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 值是用來檢測配置是否發生變更的關鍵,內部還維護着一個若干監聽器組成的數組,一旦發生變更則依次回調這些監聽器。"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置長輪詢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ClientWorker"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 通過其下的兩個線程池完成配置長輪詢的工作,一個是單線程的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"executor"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":",每隔 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"10ms"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 按照每 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"3000"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 個配置項爲一批次撈取待輪詢的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"cacheData"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 實例,將其包裝成爲一個 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"LongPollingTask"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 提交進入第二個線程池 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"executorService"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" 處理。"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3f/3f6e89ce92795090eede18c58051db2b.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該長輪詢任務內部主要分爲四步:"}]},{"type":"blockquote","content":[{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"檢查本地配置,忽略本地快照不存在的配置項,檢查是否存在需要回調監聽器的配置項"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"如果本地沒有配置項的,從服務端拿,返回配置內容發生變更的鍵值列表"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"每個鍵值再到服務端獲取最新配置,更新本地快照,補全之前缺失的配置"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"檢查 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":"MD5"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}}],"text":" 標籤是否一致,不一致需要回調監聽器"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 如果該輪詢任務拋出異常,等待一段時間再開始下一次調用,減輕服務端壓力。"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"另外,"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"Nacos"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":" 在 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"HTTP"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":" 工具類中也有限流器的代碼,通過多種手段降低輪詢或者大流量情況下的風險。下文還會講到,如果在服務端沒有發現變更的鍵值,那麼服務端會夯住這個 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"HTTP"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":" 請求一段時間(客戶端側默認傳遞的超時是 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"30s"}],"marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"bgcolor","attrs":{"color":"#FDED8A","name":"yellow"}}],"text":"),以此進一步減輕客戶端的輪詢頻率和服務端的壓力。"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Nacos 服務端解析"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置 Dump"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 服務端啓動時就會依賴 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"DumpService"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"的 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"init"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"方法,從數據庫中 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"load"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"配置存儲在本地磁盤上,並將一些重要的元信息例如 "},{"type":"codeinline","content":[{"type":"text","text":"MD5"}]},{"type":"text","text":" 值緩存在內存中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 服務端會根據心跳文件中保存的最後一次心跳時間,來判斷到底是從數據庫 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 全量配置數據還是部分增量配置數據(如果機器上次心跳間隔是 "},{"type":"codeinline","content":[{"type":"text","text":"6h"}]},{"type":"text","text":" 以內的話)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 全量 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 當然先清空磁盤緩存,然後根據主鍵 "},{"type":"codeinline","content":[{"type":"text","text":"ID"}]},{"type":"text","text":" 每次撈取一千條配置刷進磁盤和內存。增量 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 就是撈取最近六小時的新增配置(包括更新的和刪除的),先按照這批數據刷新一遍內存和文件,再根據內存裏所有的數據全量去比對一遍數據庫,如果有改變的再同步一次,相比於全量 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 的話會減少一定的數據庫 "},{"type":"codeinline","content":[{"type":"text","text":"IO"}]},{"type":"text","text":" 和磁盤 "},{"type":"codeinline","content":[{"type":"text","text":"IO"}]},{"type":"text","text":" 次數。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"配置註冊"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 服務端是一個 "},{"type":"codeinline","content":[{"type":"text","text":"SpringBoot"}]},{"type":"text","text":" 實現的服務,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 註冊配置主要代碼位於 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ConfigController"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"ConfigServletInner"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"中。服務端一般是多節點部署的集羣,因此請求一開始只會打到一臺機器,這臺機器將配置插入 "},{"type":"codeinline","content":[{"type":"text","text":"MySQL"}]},{"type":"text","text":" 中進行持久化,這部分代碼很簡單不再贅述。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 因爲服務端並不是針對每次配置查詢都去訪問 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"MySQL"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"的,而是會依賴 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 功能在本地文件中將配置緩存起來。因此當單臺機器保存完畢配置之後,需要通知其他機器刷新內存和本地磁盤中的文件內容,因此它會發佈一個名爲 "},{"type":"codeinline","content":[{"type":"text","text":"ConfigDataChangeEvent"}]},{"type":"text","text":" 的事件,這個事件會通過 "},{"type":"codeinline","content":[{"type":"text","text":"HTTP"}]},{"type":"text","text":" 調用通知所有集羣節點(包括自身),觸發本地文件和內存的刷新。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ed/edb8f649074416f895aa0ee88d53e69b.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"處理長輪詢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 上文提到,客戶端會有一個長輪詢任務,拉取服務端的配置變更,那麼服務端是如何處理這個長輪詢任務的呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 源碼邏輯位於 "},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":"LongPollingService"}]},{"type":"text","marks":[{"type":"color","attrs":{"color":"#F5222D","name":"red"}},{"type":"strong"}],"text":" "},{"type":"text","text":"類,其中有一個 "},{"type":"codeinline","content":[{"type":"text","text":"Runnable"}]},{"type":"text","text":" 任務名爲 "},{"type":"codeinline","content":[{"type":"text","text":"ClientLongPolling"}]},{"type":"text","text":",服務端會將受到的輪詢請求包裝成一個 "},{"type":"codeinline","content":[{"type":"text","text":"ClientLongPolling"}]},{"type":"text","text":" 任務,該任務持有一個 "},{"type":"codeinline","content":[{"type":"text","text":"AsyncContext"}]},{"type":"text","text":" 響應對象("},{"type":"codeinline","content":[{"type":"text","text":"Servlet 3.0"}]},{"type":"text","text":" 的新機制),通過定時線程池延後 "},{"type":"codeinline","content":[{"type":"text","text":"29.5s"}]},{"type":"text","text":" 執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲什麼比客戶端 "},{"type":"codeinline","content":[{"type":"text","text":"30s"}]},{"type":"text","text":" 的超時時間提前 "},{"type":"codeinline","content":[{"type":"text","text":"500ms"}]},{"type":"text","text":" 返回是爲了最大程度上保證客戶端不會因爲網絡延時造成超時"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/6f/6f1812fb0ef619d07060b7171e4cbc26.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 這裏需要注意的是,在 "},{"type":"codeinline","content":[{"type":"text","text":"ClientLongPolling"}]},{"type":"text","text":" 任務被提交進入線程池待執行的同時,服務端也通過一個隊列 "},{"type":"codeinline","content":[{"type":"text","text":"allSubs"}]},{"type":"text","text":" 保存了所有正在被夯住的輪詢請求,這是因爲在配置項被夯住的期間內,如果用戶通過管理平臺操作了配置項變更、或者服務端該節點收到了來自其他節點的 "},{"type":"codeinline","content":[{"type":"text","text":"dump"}]},{"type":"text","text":" 刷新通知,那麼都應立即取消夯住的任務,及時通知客戶端數據發生了變更。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了達到這個目的,"},{"type":"codeinline","content":[{"type":"text","text":"LongPollingService"}]},{"type":"text","text":" 類繼承自 "},{"type":"codeinline","content":[{"type":"text","text":"Event"}]},{"type":"text","text":" 接口,實際上本身是個事件觸發器,需要實現 "},{"type":"codeinline","content":[{"type":"text","text":"onEvent"}]},{"type":"text","text":" 方法,其事件類型是 "},{"type":"codeinline","content":[{"type":"text","text":"LocalDataChangeEvent"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當服務端在請求被夯住的期間接收到某項配置變更時,就會發佈一個 "},{"type":"codeinline","content":[{"type":"text","text":"LocalDataChangeEvent"}]},{"type":"text","text":" 類型的事件通知(注意同上文中的 "},{"type":"codeinline","content":[{"type":"text","text":"ConfigDataChangeEvent"}]},{"type":"text","text":" 區別),之後會將這個變更包裝成一個 "},{"type":"codeinline","content":[{"type":"text","text":"DataChangeTask"}]},{"type":"text","text":" 異步執行,內容就是從 "},{"type":"codeinline","content":[{"type":"text","text":"allSubs"}]},{"type":"text","text":" 中找出夯住的 "},{"type":"codeinline","content":[{"type":"text","text":"ClientLongPolling"}]},{"type":"text","text":" 請求,寫入變更強制其立即返回。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此完整的流程如下,如果非接收請求的節點,那麼忽略第一步持久化配置後開始:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/84/842ad28bb22c885dce3c4ebdf2242042.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"全文總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文聚焦於 "},{"type":"codeinline","content":[{"type":"text","text":"Nacos"}]},{"type":"text","text":" 作爲配置中心的源碼實現,包含了客戶端和服務端兩部分,內容基本覆蓋了配置中心功能的關鍵點,既作爲學習總結,也希望對閱讀的朋友有所幫助。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章