如何利用Spring Cloud構建起自我修復型分布式系統
利用Netflix所打造的組件及各類大家熟知的工具,我們完全可以順利應對由微服務以及分布式計算所帶來的技術挑戰。
在過去一年當中,微服務已經成為軟件架構領域一個炙手可熱的新名詞,而且我們也能輕松舉出由其帶來的諸多比較優勢。然而,我們必須清醒意識到的是,一旦開始遵循微服務思路而對現有架構體系進行拆分,就意味著我們將不可避免地進入分布式系統領域。在之前的文章中我們曾經探討過分布式計算的八大認識誤區*,由此可見此類系統本身充滿著風險,而且一旦犯下這八種錯誤中的任何一種、我們都將面對災難性的后果。
在我個人看來,如果要將這些誤區總結成一句觀點,那就是:對于一套分布式系統來說,任何關于一致性或者可靠性的表達都毫無保障可言。我們需要假定系統當中的各種行為以及組件位置始終處于不斷變化狀態。由此產生的后果主要有兩點:組件有時候會導致糟糕的服務質量甚至令服務直接離線,我們則只能將其統稱為“故障”、而很難具體闡明到底是哪里出了問題。一旦沒能得到妥善處理,此類故障將引中斷與停機,這意味著系統將無法按照既定設計方案為用戶提供服務項目。
有鑒于此,為了享受微服務所帶來的諸多優勢(包括松散耦合、自治服務、分散化治理以及易于持續交付等等),我們必須避免由單一故障依次遞進而最終導致系統崩潰的恐怖狀況。關于這一點,Erlang語言之父Joe Armstrong曾經在題為《如何構建永遠運行、自我修復且可擴展的系統》一文中作出過透徹的表述。在他看來,此類系統看起來與我們所說的微服務架構非常相近,但其著重強調的是其作為自我修復系統的容錯能力。那么對我們來說,如何才能建立起這樣一套堅實可靠的系統方案?
Netflix公司在微服務架構的實施與推動方面一直扮演著先行者的角色。作為其業務構建的原則性方針之一,Netflix公司認為系統方案必須要能夠承受任意組件的突發性故障,而整體系統仍能繼續正常運轉(這意味著我們仍然能夠在該平臺上觀看電影,而Netflix也可以繼續記錄用戶的觀看喜好)。在嘗試建立這樣一套系統時,我們遭遇到以下這些常見的技術挑戰:
- 由于需要將系統拆分成多個分布式進程,我們要如何在保證一致性與可靠性的前提下將這些配置分發至這些進程當中?
- 當這些配置方案需要加以修改時,我們該如何在無需重新部署全部進程的前提下對配置內容進行更新?
- 在這樣一套系統當中,特別是對于部署于云環境內的系統,各個進程不僅內容經常變動、所在位置亦會不斷轉換(特別是在進行故障轉移的情況下)。我們要如何準確判斷那些需要進行協同的進程的具體位置?
- 一旦我們檢測到了當前進程關聯性的幾種可能位置,我們該如何選擇接下來要進行通信的進程實例?
- 假設在選定一個進程實例并與該實例進行通信的過程當中該實例出現了故障,我們該如何防止由此引發的連鎖故障?
- 在系統綜合運作行為不斷給自治服務帶來演進拓撲結構的情況下,我們要如何對其狀態保持可視化監控、從而作出有針對性的準確調整?
事實上,大家可以部署多種樣板模式及開源工具來解決上述技術挑戰。Netflix公司就構建出多種組件且加以開源,并在生產環境中進行了一系列測試。從理論角度講,我們能夠利用這些工具來建立起有能力“永遠運行、自我修復且實現規模化擴展”的系統。對剛剛著手建立分布式系統的朋友們來說,我們目前的第一要務在于理解這些實現模式、掌握Netflix組件并加以應用,而后將這些組件部署、管理并集成至自己的系統當中。由于采取任何新的技術依賴關系都會給軟件工程方案帶來前所未見的復雜性元素,因此我們建議大家最好直接采用Netflix的堆棧來盡可能減少此類潛在摩擦。
Spring工程技術團隊從建立之初至今一直在努力打造出足以應對Java復雜性的強大武器。我們的早期關注重點在于消除J2EE給企業級應用程序開發者帶來的生產效率影響。而著眼于最近一段時間,我們的主要精力則轉移到了實現云-本地應用程序構建身上,而且這方面的大部分工作成果都被納入或者圍繞著Spring Cloud項目所展開。
Spring Cloud項目的既定目標在于為Spring開發人員提供一整套易于使用的工具集,從而保證其輕松構建起自己需要的分布式系統方案。為了實現這一目標,Spring Cloud以Netflix OSS堆棧為基礎將大量實現堆棧加以整合并打包。這些堆棧而后可以通過大家所熟知的各類基于注釋的配置工具、Java配置工具以及基于模板的編程工具實現交付。下面就讓我們一起了解Spring Cloud當中的幾類常見組件。
Spring Cloud Config Server
Spring Cloud Config Server能夠提供一項具備橫向擴展能力的集中式配置服務。它所使用的數據被保存在一套可插拔庫層當中,后者目前能夠支持本地存儲、Git以及Subversion。通過利用一套版本控制系統作為配置存儲方案,開發人員能夠輕松實現版本與審計配置的內容調整。
圖一:Spring Cloud Config Server
配置內容會以Java屬性或者YAML文件的形式體現。該Config Server會將這些文件合并為環境對象,其中包含易于理解的Spring屬性模型以及作為REST API存在的配置文件。任何應用程序都能夠直接調用該REST API當中所包含的配置數據,但我們也可以將智能客戶端綁定方案添加到Spring Boot應用程序當中,并由后者自動將接收自Config Server的配置信息分配至任意本地配置當中。
Spring Cloud Bus
Spring Cloud Config Server是一套強大的配置分發機制,能夠在保障一致性的前提下將配置內容分發到多個應用程序實例當中。然而根據其設計思路的限定,我們目前只能在應用程序啟動時對其配置進行更新。在向Git中的某一屬性發送新值時,我們需要以手動方式重啟每個應用程序進程,從而保證該值被切實納入應用當中。很明顯,大家需要能夠在無需重啟的前提下完成對應用程序配置內容的更新工作。
圖二: 配備Spring Cloud Bus的Spring Cloud Config Server
Spring Cloud Bus的任務正是為應用程序實例添加一套管理背板。它目前依靠將一套客戶端綁定至一組AMQP交換與隊列當中來實現,但這一后端在設計上也實現了可插拔特性。Spring Cloud Bus為我們的應用程序帶來了更多管理端點。在圖二中,我們可以看到一個面向greeting屬性的值被發送至Git當中,而后一條請求被發送至應用A中的/bus/refresh端點。該請求會觸發以下三個事件:
- 應用A從Config Server處請求獲取最新版本的配置內容。任意注明了@RefreshScope的Spring Bean都會被重新初始化并載入新的配置內容。
- 應用A向AMQP交換機制發送一條消息,表明其已經收到更新指示。
- 通過監聽AMQP隊列而被納入Cloud Bus的應用B與應用C會獲取到上述消息,并以與應用A同樣的方式實現配置更新。
現在我們已經有能力在無需重啟的情況下對應用程序配置進行更新了。