1. 程式人生 > >基於ZooKeeper,Spring設計實現的引數系統

基於ZooKeeper,Spring設計實現的引數系統

 一、簡介

  基於ZooKeeper服務端、ZooKeeper Java客戶端以及Spring框架設計的用於系統內部進行引數維護的系統。

二、設計背景

  在我們日常開發的系統內部,開發過程中最常見的一項工作便是常用引數的維護,從我學習Java以來,引數的配置多樣化,最常見的方式是properties配置檔案或者是xml配置檔案,高深點的用法是JMX MBean進行引數管理以及資料庫引數配置。我們對現有的引數配置方式進行分析,詳見下表:

配置方式常見問題備註
 程式碼內字元竄字面量配置  每次引數的修改都需要重新編譯
 properties配置檔案/xml配置檔案  普通用法雖然將引數從程式碼內抽離出來,但是無法隨時更新生效  Spring提供的ReloadableResourceBundleMessageSource工具類可以實現熱載入Properties檔案,將引數配置檔案從程式碼分離可以做到不停機不重啟做引數維護,並被程式載入,但是仍需系統重新從檔案資源內獲取新的引數值
 JMX引數配置  標準MBEAN是有侵入性的,他要管理的物件是符合JAVA BEAN規範的物件。但是要作為標準MBEAN而被管理,就需要實現一個介面。這個介面的名稱必須是類名加上MBean。  Spring支援將普通Bean通過配置MBeanExporter生成MBean
 資料庫引數配置  需要從資料庫內載入引數,每次引數的使用需要連線資料庫進行資料庫查詢。後來出現了程式碼快取資料庫引數,在一次使用後將引數資訊放置快取內,但是這種做法無法感知資料庫引數的變化。  程式碼快取資料庫引數方式,可以新增程式碼設計用於重新整理快取引數,從庫內重新讀取放置快取內,也不失為一種方便的引數管理。

  基於上述各類引數配置分析,一番思考設想,設計出如下結構的[引數中心繫統](詳細設計連結),設計說明檢視下一節:

  

三、系統設計說明

  引數中心繫統,顧名思義,主要是將引數集中化,在實際開發中,一個業務的實現需要幾個甚至數十個模組聯合完成,每個模組都需要進行引數的更新維護,一個模組的引數更新設計缺陷,在進行引數維護時,就可能導致某個業務的中斷,故需要將多引數管理統一化管理;統一化的引數管理方式,便可能涉及到了引數資料的統一儲存,統一之後便出現了效能瓶頸需求,不然所有雞蛋裝一個籃子裡,一出問題全部碎掉;在集中化後,各個模組有自己的引數,有些引數可能僅限單個系統訪問,便需要安全的引數訪問方式;在引數使用過程中,常見的功能之一便是引數的實時維護;在專案投產過程中,經常因為引數配置問題比如配置錯誤等情況導致業務中斷,故需要一個引數檢查表來確認引數的正確性;引數管理整合後,需要方便的操作來實現管理功能。概括一下,引數中心繫統需要滿足以下技術需求:

  • 多系統、多模式、安全、動態維護的引數配置
  • 個性化話引數配置(普通字元竄,JSON字元竄,陣列竄)
  • 低侵入
  • 快捷的引數匯入匯出功能
  • 便捷的管理方式
  • 上線引數檢查表,用於上線時各類引數的檢查,防止出錯
  • 高可用
  • 安全控制

  根據上述分析,設計之初,思考如何實現多系統多模式的引數儲存,雖然一直知道ZooKeeper這個東西,但從未詳細瞭解過,偶然機會大致學習了一下ZooKeeper,發現ZooKeeper的各類機制與引數中心繫統設計相吻合,比如:多系統多模式的引數儲存與ZooKeeper的目錄型儲存方式相似,檢視下面圖3-1展示;引數的安全方式認證與ZooKeeper的ACL機制吻合;引數實時維護可以借鑑ZooKeeper的Watcher機制;引數中心繫統的高效能高可用設計與ZooKeeper的叢集方式相吻合。這樣下來,引數中心繫統最大的問題引數儲存模組服務端得到了完美的解決。接下來的便是基於ZooKeeper設計出對應的客戶端,管理端。

圖3-1 基於ZooKeeper的引數儲存

  Java應用端常用的技術之一便是Spring框架,也符合低侵入的設計原則,在使用Spring開發過程中,常用的功能之一便是使用${}引用properties配置檔案內的引數,如此方便的引數配置方式,我決定使用類似的方式,配置方式為zk{}(zk表示ZooKeeper引數),故客戶端的設計是基於Spring的設計。

四、系統技術組合

  ZooKeeper叢集 + ZooKeeper Java客戶端 + Spring BeanFactoryPostProcessor擴充套件點 + JSON字元竄解析 + Spring SpEL表示式

五、設計實現(重點)

  根據上述設計說明等資訊,最後得出這樣一個系統,基於ZooKeeper引數儲存,Spring客戶端使用zk{}進行引數配置的引數中心繫統。

  • 服務端

  服務端設計如3-1圖所示(在實際開發過程中可能稍有變動)

  • 客戶端(重點)

  在進行引數中心繫統客戶端實現之前,我們先了解一點Spring框架的基礎知識。

  在Spring框架開發過程中,最常用的配置是<bean/>標籤的使用,在工作中,最常聽的一種說法是,在xml裡配置上就可以使用bean了,就有物件了,這種理解潛在一層含義xml直接配置成了java bean,而實際上,Spring中bean的定義最終表現為BeanDefinition物件,個人理解為xml實際配置的是bean的說明資訊,Spring將這些說明資訊轉換為了BeanDefinition物件,再由BeanDefinition生成了我們最終看到的bean,實際流程為[xml配置->BeanDefinition->Bean]而一般開發者不知道BeanDefinition,故理解含義成了[xml配置->Bean],中間缺少了重要的環節。在BeanDefinition到Bean這個過程中,Spring由BeanFactory生成實際的bean,在實際bean產生前,Spring提供了BeanFactoryPostProcessor擴充套件點(類似還有BeanPostProcessor),通過該擴充套件點可以獲取到配置的BeanDefinition資訊,用於自定義擴充套件對bean定義變更修改,實現自由控制。

  引數中心繫統引數的配置實現參考了Spring的${}引數配置,我們對${}的實現做簡單學習。${}的實現便使用了Spring擴充套件點BeanFactoryPostProcessor,開發中常見配置如下:

  

  

  上圖中採用了context:property-placeholder標籤配置,根據Spring context的xsd說明檔案,我們知道了property-placeholder對應的實際類為org.springframework.context.support.PropertySourcesPlaceholderConfigurer,context:property-placeholder配置實際為在spring容器內註冊一個擴充套件點,實現${}表示式的解析實現類圖以及呼叫流程大致如下,再詳細過程檢視原始碼,(有句話叫做師傅領進門,修行在個人):

  引數中心繫統客戶端的實現程式碼與上述實現類似,不同的是properties配置檔案變成了ZooKeeper引數儲存,${}變成了zk{}。

  客戶端專案名稱:itwatertop-pczk-client

  • 管理端

  基於H5的管理頁面設計,詳細情況還未設想(先實現了主要的服務端客戶端,管理端暫時可以使用ZooKeeper的客戶端)。 

六、客戶端設計原始碼

  客戶端程式結構如下:

  檔案說明:

  • BaseLoader.java 資料載入基類
  • ZookeeperDataLoader.java Java ZooKeeper客戶端實現資料載入,引數更新回撥。
  • PlaceholderMsg.java zk{zkexp} zk配置表示式解析結果,以及使用該表示式的bean屬性獲取SpEL表示式。
  • ParamCenterStore.java 對ZooKeeper引數服務端獲取的引數資訊做快取,並且將對應的使用該引數的Bean屬性表示式統計,方便ZooKeeper引數變更時回撥使用。
  • PczkConstants.java 系統內常量字元竄整合。
  • PczkStringValueResolver.java 表示式解析統一介面
  • PczkPropertyPlaceholderConfiguer.java 實現Spring擴充套件點BeanFactoryPostProcessor,通過該擴充套件點對BeanDefinition配置元資訊做解析以及變更。
  • PropertyPlaceholderHelper.java 具體實現zk{}表示式解析規則。
  • PczkBeanDefinitionVisitor.java 對Spring IoC容器內的BeanDefinition屬性配置資訊做解析,主要結合PczkPropertyPlaceholderConfiguer.java使用。
  • test目錄下包含一部分測試程式碼,可以自行檢視

  客戶端程式碼實現簡介:

  根據上述需求,客戶端程式碼需要具備的能力包括:

  1. 與ZooKeeper伺服器的連通,並獲取引數資訊
  2. Spring xml中zk{}表示式的解析
  3. 多樣化的引數配置,支援字元竄,物件,陣列
  4. ZooKeeper引數變更回撥,維護引數資訊

  針對上面4種要求,在開發時使用以下解決辦法:

  1. 使用ZooKeeper Java客戶端;
  2. 結合Spring的擴充套件點BeanFactoryPostProcessor;
  3. 在ZooKeeper服務端節點內設定資料時設定字元竄/JSON物件字元竄或者是JSON陣列字元竄,客戶端使用資料時分別配置為zk{param},zk{param.key},zk{param[i]},通過對zk{}表示式解析判斷ZooKeeper引數格式,若為JSON字元竄使用FastJSON對字元竄做解析,訪問對應屬性值;
  4. 針對引數變更回撥,在解析zk{}表示式時拼接出了可以訪問到對應bean屬性的SpEL表示式,通過SpEL表示式訪問bean屬性呼叫setter方法,因此屬性操作也受到SpEL表示式的限制。在個別情況下,由於引數的變更可能需要別的一下操作處理,比如重新建立連線,這個可以自行擴充套件程式碼,比如比較上一次的值和當前值是否一致,不一致做出新的操作(現在也在設想怎麼可以自動識別進行額外操作)。

  客戶端詳細實現原始碼下載:訪問GitHub專案(註釋還是比較清晰的)

七、說明