博客 / 詳情

返回

如何設計一個分佈式配置中心?

這是小卷對分佈式系統架構學習的第7篇文章,前面已經講了很多理論知識,今天結合具體的中間件來講分佈式配置中心

1.面試官提問

面試官:假設你是公司的基礎架構部門,現在需要設計內部的配置中心中間件,你要怎麼設計?

我:設計客户端和服務端,客户端集成到業務項目中,項目啓動時從服務端pull配置加載到本地,並且定時check服務端和本地配置是否一致,服務端如有更新,再pull到本地

面試官:那如果有幾萬台服務器,都是這樣定時去check,服務端壓力豈不是很大,要怎麼解決呢?

我:那改成用服務端push的方式???

面試官:......

面試官:那今天就到這裏吧,你回去等通知吧......

2.為什麼需要分佈式配置中心

不瞭解底層原理的小卷只好回家後苦心專研分佈式配置中心的原理,一定要弄清楚底層邏輯,下次要吊打面試官。

先來簡單理解為什麼需要配置中心?

我們開發的服務都是單體架構時,配置文件就和代碼放在一起,如springboot的application.yml文件,對配置的修改只需要修改這一個文件就行。到分佈式服務中,一個服務會有多台機器,不可能每個機器都單獨修改配置文件,然後重新部署的。

這就要用到配置中心了,以nacos為例,下圖是配置修改時和服務器間的操作:

在這裏插入圖片描述

3.開源框架

這裏列舉4種分佈式配置中心的中間件,我們直接從一箇中間件的原理來學習配置中心。

工作這麼多年,應該得了解一些開源組件,大大小小的都行:

1、Apollo

2016年5月,攜程開源的配置管理中心,具備規範的權限、流程治理等特性。

GitHub地址:https://github.com/apolloconfig/apollo

2、spring cloud config

2014年9月開源,Spring Cloud 生態組件,可以和Spring Cloud體系無縫整合。

3、Nacos

2018年6月,阿里開源的配置中心,也可以做DNS和RPC的服務發現。

4、Diamond

Diamond 出自淘寶,開源地址 【https://github.com/takeseem/diamond】 ,阿里集團內部的配置中心仍然用的diamond,只是開源版本不再維護

面試時可能會問到為什麼選擇Apollo作為配置中心?不用其他的配置中心呢?

很多人用的時候就是看別人也這麼用,或者大家都這麼用,就選擇了這個中間件。這裏如果遇到了的話,就可以提到開源社區的活躍性,因為Apollo 的社區生態活躍,且使用的公司特別多,常見的坑基本都被踩完了,所以選用Apollo。

4. Apollo工作原理

4.1基礎模型

Apollo文檔:Apollo配置中心設計,工作原理比較簡單:

  1. 用户在配置中心對配置進行修改併發布
  2. 配置中心通知Apollo客户端有配置更新
  3. Apollo客户端從配置中心拉取最新配置,更新本地配置並通知到應用

在這裏插入圖片描述

4.2架構模塊

在這裏插入圖片描述

解釋下各個模塊的功能:

  • Config Service提供配置的讀取、推送等功能,服務對象是Apollo客户端
  • Admin Service提供配置的修改、發佈功能,服務對象是Apollo Portal(管理界面)
  • Config Service和Admin Service都需要註冊到Eureka並保持心跳;
  • Meta Server是對Eureka做了一層封裝,封裝的是服務發現接口;
  • Client通過域名訪問Meta Server獲取Config Service服務列表,即獲取IP+端口,然後通過IP+端口訪問服務,同時Client端自己做負載均衡,錯誤重試;
  • Portal訪問Meta Server獲取Admin Service服務列表,也是獲取IP+端口,然後訪問服務,Portal側也做負載均衡;

5. 使用Apollo

官方有提供快速部署使用文檔:Quick Start

具體操作步驟可以自行查看官方文檔,這裏我們主要通過簡單使用Apollo來理解配置中心。部署完成後,登陸Apollo的管理界面,然後創建個應用,發佈後再創建個配置,接着再次發佈,如下圖:

在這裏插入圖片描述

這裏我是在本地啓動的,訪問http://localhost:8080/可以查看已註冊的實例

在這裏插入圖片描述

然後創建一個Springboot應用連接到Apollo配置中心,這裏不寫那麼具體了,可以自行參考官方的Java客户端使用指南

Mac電腦需先在本地的/opt/settings/server.properties文件中配置環境env=DEV,然後在application.properties文件中配置Apollo相關的內容如下:

# 接入Apollo配置
app.id=multi_function
apollo.meta=http://localhost:8080
# Apollo本地緩存路徑
apollo.cache-dir=/Users/longbig/log
# 指定Apollo配置文件的環境
env=DEV
# 配置訪問秘鑰
apollo.accesskey.secret=4c61a00512ad4cc09ef8a0e1ee672d89
apollo.bootstrap.enabled=true

為了測試客户端接收到配置中心配置變更的事件,我們參考官方文檔的代碼寫個監聽器的代碼如下:

@Configuration
@Slf4j
public class ApolloConfig {
    @Bean
    public void init() {
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                log.info("Changes for namespace " + changeEvent.getNamespace());
                for (String key : changeEvent.changedKeys()) {
                    ConfigChange change = changeEvent.getChange(key);
                    log.info(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()));
                }
            }
        });
    }
}

最後測試驗證,在管理界面增加一個配置,然後對配置修改發佈,可以看到客户端已經接收到配置變更的事件了,並且打印出日誌信息了

在這裏插入圖片描述
在這裏插入圖片描述

6. 配置發佈後實時生效設計

從上面簡單使用中可以看到,配置發佈後實時推送到客户端。下面我們簡要看一下這塊是怎麼設計實現的

在這裏插入圖片描述

配置發佈的大致過程:

  1. 用户在Portal操作配置發佈
  2. Portal調用Admin Service的接口操作發佈
  3. Admin Service發佈配置後,發送ReleaseMessage給各個Config Service
  4. Config Service收到ReleaseMessage後,通知對應的客户端

6.1發送ReleaseMessage的實現方式

從上圖看,應該是用MQ的方式比較合適,但是Apollo沒有用外部消息中間件,而是通過數據庫來實現這個簡單的消息隊列的。具體如下:

  1. Admin Service在配置發佈後會往ReleaseMessage表插入一條消息記錄,消息內容就是配置發佈的AppId+Cluster+Namespace
  2. Config Service有一個線程會每秒掃描一次ReleaseMessage表,看看是否有新的消息記錄
  3. Config Service如果發現有新的消息記錄,那麼就會通知到所有的消息監聽器(ReleaseMessageListener)
  4. 消息監聽器得到配置發佈的AppId+Cluster+Namespace後,會通知對應的客户端

在這裏插入圖片描述

我們查看數據庫的ReleaseMessageReleaseHistory表,可以查看到當前消息和歷史消息

在這裏插入圖片描述

在這裏插入圖片描述

6.2 Config Service通知客户端的實現方式

這裏能解釋説明開頭的面試題,客户端更新配置是Pull還是Push的方式?

具體實現方式如下:

  1. 客户端會發起一個Http請求到Config Service的notifications/v2接口,也就是NotificationControllerV2
  2. NotificationControllerV2不會立即返回結果,而是通過Spring DeferredResult把請求掛起
  3. 如果在60秒內沒有該客户端關心的配置發佈,那麼會返回Http狀態碼304給客户端
  4. 如果有該客户端關心的配置發佈,NotificationControllerV2會調用DeferredResult的setResult方法,傳入有配置變化的namespace信息,同時該請求會立即返回。客户端從返回的結果中獲取到配置變化的namespace後,會立即請求Config Service獲取該namespace的最新配置。

7. 客户端的工作原理

在這裏插入圖片描述

接着講講Apollo客户端的工作原理:

  1. 客户端和服務端保持了一個長連接,從而能第一時間獲得配置更新的推送。(通過Http Long Polling實現)
  2. 客户端還會定時從Apollo配置中心服務端拉取應用的最新配置。

    • 這是一個fallback機制,為了防止推送機制失效導致配置不更新
    • 客户端定時拉取會上報本地版本,所以一般情況下,對於定時拉取的操作,服務端都會返回304 - Not Modified
    • 定時頻率默認為每5分鐘拉取一次,客户端也可以通過在運行時指定System Property: apollo.refreshInterval來覆蓋,單位為分鐘。
  3. 客户端從Apollo配置中心服務端獲取到應用的最新配置後,會保存在內存中
  4. 客户端會把從服務端獲取到的配置在本地文件系統緩存一份

    • 在遇到服務不可用,或網絡不通的時候,依然能從本地恢復配置
  5. 應用程序可以從Apollo客户端獲取最新的配置、訂閲配置更新通知

8.題外話

之前在第一家公司工作過程中,遇到個問題是:對應用某個配置的變更如何通知到生產環境的所有機器?

當時的場景是前端發起HTTP請求,調用後端接口修改配置,因為負載均衡的緣故,請求只會打到1台機器上,只有1台機器的內存配置被更新,其他機器的內存配置還是舊的,當時小組一起討論解決辦法,可能認知有限,只想到MQ等等方式,沒想到配置中心的原理

後來去了阿里之後,參與過寫配置中心配置變更監聽器,實現了全量機器的內存配置更新功能

現在回想起來,當時沒解決的原因還是認知不夠,現在學了配置中心的原理又想到了這件事,分享給大家學習參考~

相信通過學習Apollo配置中心的原理,你在面試過程中如果遇到開頭的題目,應該也能説上一二了。

user avatar FatTiger4399 頭像 docker_app 頭像 ticktank 頭像 cunyu1943 頭像 yadong_zhang 頭像 redorblack 頭像 biubiubiu_5ea3ee0e6b5fd 頭像 goudantiezhuerzi 頭像 tingtr 頭像 TwilightLemon 頭像 chengxuyuanxiaohui 頭像 codingembedded 頭像
17 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.