分散式配置中心 duic 設計 & 實現
什麼是配置?
配置(Configuration)對於技術人員來說應該都不陌生,通常配置都是以 key-value 的形式存在於配置檔案當中。例如執行緒池大小、資料庫連線、邏輯開關及業務引數等等。
那麼為什麼我們需要提供各種各樣的配置呢?其本質原因是我們無法在開發時決定軟體在執行時的一切,為保證程式的靈活性我們需要在開發過程中提供各種配置,以便軟體在執行時能動態調整程式的行為。
配置即程式在執行時動態調整行為的能力!
靈感
之前我們一直是使用 spring-cloud-config
來管理服務端配置,但是 spring-cloud-config
並不支援熱載入,按需獲取配置,且因為是 spring 體系中的專案對於其它語言或非 spring 體系的專案支援較弱。
建立在這種基礎之上,我們借鑑了 spring-cloud-config
的配置管理方式以及應用環境管理方式開發了全新的分散式配置中心 duic。
簡介

duic 簡介
專案地址: ofollow,noindex">https://github.com/zhudyos/duic
duic 的誕生從來都不只是為了解決服務端的配置問題,你同樣可以使用它對 APP、WEB 應用的配置進行管理。因採用 HTTP 的方式拉取配置資料,對於多語言,多應用型別的配置支援非常的好。
如果你的 APP、WEB、Server 有一部分公共配置,你也可以完全使用 duic 來解決,而不需要在每個應用單獨配置(冗餘),修改一處配置對於所有應用都有效。並且 duic 支援多配置合併功能,你可以將公共配置與私有配置分開管理,在拉取配置時進行合併,能有效的進行配置許可權管理。同時 duic 支援按需獲取配置,這個功能對於 WEB、APP 非常有用,有時你可能只需要某個配置引數,而不是拉取整個配置資料。
設計目標是統一不同應用的配置管理方式,打造更人性化的配置編輯方式,提供更靈活的配置獲取方式。
另外 duic 完全相容 spring-boot 及 spring 並且對程式碼沒有任何浸入。
duic 配置管理方式與其它配置中心是完全不一樣的,我們放棄採用 key-value 兩個輸入框來編輯配置的方式。轉而採用 YAML 語法 + 線上編輯器的方式來管理編輯配置。這樣可以輕鬆支援結構化配置並且你可以得到高度視覺化配置編輯方式,如下圖:

配置示例
這種管理方式所帶來的好處是:
- 配置可讀性高
- 支援語法高亮
- 支援資料型別
- 支援文件註釋
- 結構化配置為按需獲取配置提供了基礎
通常配置都是以 key-value
的形式存在,比如 VIP 的價格 vip.price=100
,是否啟用某個功能 game.enabled=true
。大多數情況是如此,但是實際情況中還是有很多場景需要結構化的配置支援,目前使用較多來處理這種場景的方法是使用 key=json string
來解決。 例:將圖示和點選的 URL 採用 JSON 的方式配置,可以在資料結構中維持配置的關係且配置易於理解。
{ "logo": { "top": { "icon": "icon url", "url": "link url" }, "special": { "icon": "icon url", "url": "link url" }, "goddess": { "icon": "icon url", "url": "link url" } } }
特性
- 集中配置管理,多應用多環境配置
- 配置實時更新
- 支援配置資料型別/資料格式
- HTTP 方式拉取配置
- 配置狀態檢查
- 配置狀態監控
- 多配置合併
- 按需獲取配置
- 配置許可權管理
- 支援配置 IP 訪問限制
- 支援配置 TOKEN 校驗
- 支援 SQL/">MySQL 儲存配置
- 支援 Oracle 儲存配置
- 支援 PostgreSQL 儲存配置
- 支援 MongoDB 儲存配置
- 支援 Docker 部署
- 完美支援 Spring Boot如果你的專案是採用 spring-boot 開發,那 duic 不僅能管理你業務的配置,還能管理所有 spring-boot 相關的配置,配置方式與 application.yml 檔案編輯無差異
系統架構
部署

基本部署方案
duic 支援 MySQL、PostgreSQL、Oracle、MongoDB 儲存配置資訊,你可以靈活的選擇配置儲存方式。依照上圖部署 duic 就能使用配置中心的全部功能,部署的方式和網路組織架構都非常的簡單。
在設計時為了能簡化部署,將 Admin 與獲取配置相關的 API 整合在一個服務當中,降低部署的複雜性。

高可用部署架構方案
duic 對於資料庫的壓力非常的小,所以基本上資料庫你只需要 master/salve 部署模式即可實現服務高可用。duic 服務你可以根據自身業務的壓力情況部署 N 個。前面可以採用 nginx 做負載均衡利用 keepalived 避免單點問題。這是我們目前使用 duic 高可用的部署方案(僅供參考)。
實現
duic 是完全採用 Reactive Programming 實現。使用技術主要有 kotlin、spring-webflux、spring-boot、vue.js、gradle。

特性
配置實時更新實現方式

配置實時更新實現方式
- 客戶端啟動時獲取配置及配置狀態。
- 使用 /apps/watches/{name}/{profiles} 介面監控服務端配置狀態變化。
- 呼叫 watches 介面時需要傳入客戶端當前狀態,服務端根據客戶端傳入的狀態判斷。
- 如果與服務端的配置狀態不一致會立即響應最新的狀態。
- 如果狀態一致請求則會延遲返回(最長30秒)。在30秒內配置狀態發生變化,服務端會立即響應。在30秒配置狀態未發生變化,服務端也會響應當前最新狀態給客戶端。
- 接收到服務端的狀態響應時客戶端需要判斷狀態是否與客戶端當前的一致,如果一致則繼續 watch 配置狀態,如果不一致則過載配置。
配置過載完全是由客戶端發起的,這是長輪詢(Long Polling)實現,利用 HTTP 長連線無需擔心重複建立連結問題,帶來的好處就是實現簡單可用性高。
配置&記憶體
在 duic 服務啟動時會將資料庫儲存的配置資料全部載入在 JVM 記憶體中,當你通過 HTTP 介面訪問配置資料時,duic 會直接將記憶體中的配置返回,中間不會有任何查詢資料庫的操作,所以你使用 duic 獲取配置時響應速度非常的快(具體請看後面的效能測試報告)。
叢集管理
在 duic 服務啟動時會將啟動服務的主機名與埠註冊到資料庫的 DUIC_SERVER
表中。當有人在控制檯修改配置時,duic 會通知 DUIC_SERVER
表中的所有服務過載最新配置。同時 duic 內部還會輪詢機制保證 JVM 記憶體永遠都是最新的配置資料。
RESTful API
獲取配置狀態
GET /apps/states/{name}/{profiles}
監控配置狀態
GET /apps/watches/{name}/{profiles}
獲取配置
GET /apps/{name}/{profiles}
按需獲取配置
GET /apps/{name}/{profiles}/{key}
效能報告
伺服器配置: E5-2620V3(12核24執行緒 主頻2.4GHz) / 16G
作業系統: Ubuntu 16.04
測試工具: wrk
Java 版本: 1.8.0_161
JVM 引數
java -server -XX:+UseG1GC -Xms8g -Xmx8g -XX:MetaspaceSize=128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=logs/ -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:logs/gc.log
測試結果,每秒 duic 可達到 24760tps ,服務的每個工作執行緒 CPU 已經達到 97% 以上,在這臺裝置中效能已經無法在繼續提高了,這是我目前手上最好的伺服器。
Running 1m test @ http://192.168.31.164:7777/api/v1/apps/meme/test-public 12 threads and 100 connections Thread StatsAvgStdevMax+/- Stdev Latency3.91ms1.01ms38.21ms93.70% Req/Sec2.07k92.012.71k83.65% 1486594 requests in 1.00m, 4.52GB read Requests/sec:24760.93 Transfer/sec:77.10MB
CPU 報告

CPU報告
GC 報告

GC 報告
結語
duic 配置中心原始碼倉庫 https://github.com/zhudyos/duic 歡迎大家提供任何意見,建議和吐槽。