Apollo 4 客戶端 SDK 設計

前言

以前聊了客戶端的一些功能,例如融入 Spring, @value 註解的自動刷新實現,長輪詢等,此次從客戶端的總體設計來聊聊。spring

設計

上圖是 client 項目的包結構。bootstrap

其中,核心包就是 internals 包,包含了客戶端的主要功能邏輯。主要有如下功能: 0. 獲取 ConfigService 服務的遠程配置。緩存

  1. 長輪詢/定時輪詢 ConfigService。
  2. 監聽機制——更新後,當即通知應用程序。
  3. 兼容 Spring 各個版本(這個是在 spring 包中,但我認爲也算重要功能 ^_^)。
首先說第一個功能:獲取 ConfigService 服務的遠程配置

實現此功能的類爲:RemoteConfigRepository。該類有如下幾個重要的方法: google

  1. 構造方法:該方法裏包含了不少初始化的過程,雖然我以爲應該放在 init 之類的方法中
  2. getConfig() 根據 namespace 獲取配置
  3. onLongPollNotified() 當收到長鏈接通知時觸發響應
  4. addChangeListener() 添加監聽器
  5. removeChangeListener() 刪除監聽器

注意:setUpstreamRepository 是空的。看註釋,是個 fallback 設計。spa

其中,getConfig 方法是獲取這個 namespace 的配置,返回的是 Properties 對象(就是個 Map)。而後,從這個對象中取出對應的值,就 ok 了。設計

第二個功能:長輪詢/定時輪詢 ConfigService。

這個功能的主要實現類是:RemoteConfigLongPollService。code

該類主要的方法有 2 個,構造方法和 submit 方法。注意,這個類是單例的(由 google 的 inject 實現)。 構造方法中,作了不少的初始化工做。而 submit 方法則是開啓長輪詢,輪詢的方式是:攜帶 AppId 去請求 ConfigServcie,獲得全部的 namespace 更新通知,而後通知對應的 RemoteConfigRepository 去請求真正的數據。大概的設計以下圖:cdn

image.png

每個 namespace 在一個應用中,都對應一個 RemoteConfigRepository,全部的 RemoteConfigRepository 都歸屬 RemoteConfigLongPollService 長輪詢服務管理,當長輪詢獲得通知,便通知對應的 RemoteConfigRepository 進行服務請求以便執行更新本地緩存和通知監聽器操做。對象

通知,做爲 fallback 方案—— 定時輪詢也充當了長輪詢失效的最後屏障。blog

第三個功能:監聽機制——更新後,當即通知應用程序。

從上圖能夠看出,輪詢以後,若是有更新響應,則當即通知 RemoteConfigRepository,而後,RemoteConfigRepository 再次從配置中心拉取配置,從而更新本地 Config 對象的內容。

更新完畢後,則通知 Config 的「配置變化監聽器」。也就是 ConfigChangeListener 的 onChange 方法。這個監聽器是監聽 Config 對象的。

實際上,每一個 Config 對象在初始化的時候,都會往 RemoteConfigRepository 對象裏添加一個監聽器,實際上就是添加本身。

當 RemoteConfigRepository 發生變化的時候,觸發 onRepositoryChange 方法,onRepositoryChange 又會觸發 onChange 方法。大概的設計圖就是下面這個樣子:

上圖中,紫色的 DefaultConfig 是核心,他依賴了 RemoteConfigRepository, 而 RemoteConfigRepository 反過來組合了他,同時 DefaultConfig 也聚合了用戶實現的監聽器 ConfigChangeListener 的子類。

那麼,當遠程 Repository 變化的時候,就能夠通知 Client 的緩存 Config 對象,而 Config 緩存對象變化的時候,就能夠通知用戶的程序(監聽器)。實現總體的監聽機制。

總的來講,就是經過兩層監聽機制來實現的。其中 DefaultConfig 實現了兩個角色,既是觀察者,也是被觀察者。

第四個功能:兼容 Spring 各個版本

首先,若是沒有這個功能,Apollo 也會可以正常運行的,不過,你只能使用 API 的方式,不能使用註解,標籤等 Spring 應用熟悉的方式。

若是想用 Spring 的方式使用 Apollo ,那麼就得遵照 Spring 的約定,實現 Spring 的接口,將本身融入到 Spring 中。

其中,主要解決的問題就是,如何在 Spring 初始化的時候,Apollo 也初始化?這點咱們在以前的文章中說了,也就是 Spring 的 3 個入口。在這些入口裏初始化。

另外,將配置放置到 Spring 的環境中,也是一個工做,由於,若是不放到環境中,Spring 初始時須要的那些參數就沒法取到了。

因此,要將 Config 對象包裝成 Spring 熟悉的 ConfigPropertySource 對象,算是一個適配器模式吧。

在初始化配置的時候,會從遠程配置中心拿到配置,包裝成 ConfigPropertySource 對象,再利用 CompositePropertySource 組合屬性配置(多個 namespace)聚合全部 Config 對象。

CompositePropertySource 最後會添加到 ConfigurableEnvironment 環境對象中,spring 就能夠從這個對象 中取出配置進行初始化。

而且,在 SpringBoot 環境下,Apollo 能夠優先加載指定的配置,這些配置在 SpringContext 容器初始化的時候就開始被注入到環境中,這樣就能夠將一些系統初始化的配置也放到配置中心了,儘可能讓本地少一點配置。這個功能的啓用須要參數:apollo.bootstrap.enabled=true,配置的namespace 則是 apollo.bootstrap.namespaces = XXX

而且,該配置的優先級是最高的,Apollo 將這個配置放在了 Spring 環境對象中的第一個位置,當循環獲取配置的時候,優先獲取這個配置。

總結

好了,關於 Apollo 客戶端的設計,大概就是這些,整體來說比較簡單, 4 個功能:

  1. 獲取遠程配置
  2. 長輪詢/定時輪詢
  3. 配置更新監聽機制。
  4. 兼容 Spring。

拋出一個問題:

Apollo 彷佛沒有給用戶留擴展接口?若是能像 Spring,Mybatis 同樣,留一個或者多個切面給用戶,讓用戶可以在加載配置的時候,作一些事情啥的,或許更好。

相關文章
相關標籤/搜索