微服務生態環境全解析,使開發更具彈性和容錯能力,你期待嗎? 前言 容器中的分離機制 存儲分佈 隔離——使用生態系統防止故障的出現 斷路器 總結

前言

在軟件開發過程中,總會出現一些我們所不瞭解的內容。例如,軟件最終是否會成功運行?當編寫應用程序並將其置入產品中時,也會產生各種問題,其間也會伴隨着失敗。

有人曾指出,零bug的軟件是不存在的。充其量,軟件只是存在未知的bug。這一說法並非謬論,甚至是100%正確的。

通常,應用程序包含較高的測試覆蓋率;另外,領域業務還涉及自動化測試以及集成測試。顯然,一切工作良好。但是,當談及微服務時,還會涵蓋一些潛在的風險, 例如網絡連接、負載平衡中的錯誤,以及外部服務使用過程中的故障。

微服務中的問題可能源自開發團隊中產生的bug,或者是與其他服務集成後造成的不良後果。毫無疑問,應用程序中的缺陷將導致產品的一種良性失敗。

這裏,大家可能會產生疑問,難道還會存在一種良性失敗?答案是肯定的。良性失敗意味着,問題不包含不可用服務,或者無效內容僅呈現爲局部特性,而不涉及整個系統。除此之外,快速發現問題同樣是一種“成功” 的標誌。

對於故障的良性結果,我們將採取一些措施, 並在工作堆棧中解決此類問題。

容器中的分離機制

爲了阻止故障的發生,重要的是需要了解產生故障的方式。下面考察單體應用程序中較爲常見的操作,此類也會出現於微服務架構中,進而有助於我們理解故障前的響應方式。

種較 爲常見的方法是將應用程序的整體結構置於單一存儲庫中。也就是說,軟件代碼、緩存以及應用程序的所有其他特性均位於同一臺機器設備中,這種情況屢見不鮮。

如下圖所示,顯示了緩存、數據庫、API以及業務邏輯層位於同一位置時的情形。初看之下,這一配置行爲並無問題。在同一臺機器中,諸如延遲、包丟失和部署複雜性等問題都被簡化了。

在當前方案中,假設容器出現故障,那麼,將難以分辨容器故障所對應的組件。這一辨識過程將佔用大量的時間。另外,緩存中的某個缺陷可能會導致應用程序崩潰。故障的產生過程往往是以漸進方式進行的,當意識到容器的系統故障時,可能爲時已晚。

由於所有組件均處於連接狀態,並且無法對其進行單獨處理,因而整個應用程序將被重新啓動。針對此類行爲,尚不存在一種優雅的 系統可對此提供支持。除了導致某種程度的不一致結果之外,還可能丟失數據。

之前的一些積極因素現在被證明是一種很容易破壞彈性的選擇方案,例如部署複雜性的降低,以及延遲和包丟失的減少。將整個應用程序堆棧保存在一個物理組件中, 意味着所有軟件層都將使用組件的相同特性。此時,故障的緩解往往與自身方案的選取有關。

這種系統故障不僅影響了應用程序的可用性,而且還可能影響產品在客戶和投資者眼中的可信度。下圖顯示了緩存出現錯誤且數據庫過載時發生的故障,進而導致系統崩潰。

這依然不是最糟糕的情況。對於應用程序來說,最不希望聽到的即是“成功”一詞,成功意味着應用程序的擴展。也就是說,在不提升彈性的情況下,成倍地增加可用性。高可用性將與所支持的命中數量有關,而不是保持可用性,即使發生內部故障。

這可能並不是當前應用程序的選擇方案。通常,在較差的微服務生態系統中,單體架構呈現爲重複狀態。對於一些軟件工程師來說,微服務架構僅僅是將業務邏輯與應用程序分離開來。這種認知是錯誤的一應用程序的組件 也必須以可伸縮的方式進行設計,因此,在極端情況下,必須可實現快速修復或重置行爲。

分層服務架構

對於應用程序所採取的分離機制,這是一種十分常見的反模式。一些工程師常會在堆棧組件的分離與邏輯層的分離之間產生混淆,這樣就產生了一個缺少業務表達的貧血域。

下圖顯示了一-種較差的實現方式。其中,軟件由3個不需要的物理組件組成。同時,應用程序的分離具有較細的粒度。此外,編排器和數據訪問並不具備實際意義。

基本的業務內容需要對數據庫進行訪問。很快,構建這種分離機制便沒有邏輯可言了。另一點需要注意的是,如果編排器直接訪問數據,也就意味着,它在應用業務方面具有一定的智能。另外,編排器也是完全不必要的一一僅存在一個業務層, 因而並不存在數據可供編排。

另外,當前粒度是微服務架構中非常常見的錯誤,同時也是我們不惜一切代價所要避免的。該粒度會提升系統的複雜度,同時增加維護工作物理組件的成本,以及多層間的通信成本。

存儲分佈

在任何應用程序中,數據保存均是一個十分重要的話題,微服務也不例外。利用分佈式應用程序,數據的分佈將變得更加靈活。

在處理微服務中的存儲問題時,存在一些較好的實踐方案。 顯然,CQRS模式十分有效,但在性能方面仍有所欠缺。對於應用程序的健康問題,數據的區域化和折舊(depreciation)較爲有用。

在容器的創建過程中,可以看到,除了應用程序自身的容器之外,在UsersService中,還有兩個數據存儲層的特定容器,並作爲數據庫本身的緩存。

折舊數據

在科學計算年代,數據分析佔有重要地位。刪除數據聽起來是十分不合理的。是的,這的確有些荒謬。類似地,擁有100萬數據行的數據庫會生成越來越慢的查詢操作。

這裏的問題是,如果無法從數據庫中刪除數據,我們應該如何處理才能不影響查詢操作?這個問題的答案是數據折舊。數據的折舊包括劃分活動數據和非活動數據,並將非活動數據移至與實時應用程序層無關的存儲中。

下面考察門票銷售這一示例。其中,每天都會有事件被創建和執行,並完成基於事件的門票購買活動。這也是該應用程序的主要例程。

一段時間後,我們將對與事件相關的數據進行審計,或者僅用於數據分析。對於最近的事件、仍處於活動狀態的事件,以及已超出1年的事件,保存其數據並無意義。

但是,隨着時間的推移,將所有數據保存在一個存儲設施中,會產生較慢的查詢操作,以及更爲嚴重的數據遷移或修改問題。針對於此,數據折舊是一種較好的方法,特別是在實現自動化時。

區域化數據

在應用程序中,通常的做法是將其部署到服務器中,併爲應用程序提供訪問點——如果應用程序在全球範圍內使用,則重複執行這- - -處理過程。但通常情況下,服務器的部署一般在距新產品市場最近的地理位置處進行,這一做法完全正確,但問題也會隨數據庫服務器的地理位置而出現。

查看依據地理位置分佈的應用程序是一類常見操作,但需要從幾公里之外的服務器處獲取數據。因此,在不同的地理位置處,這將帶來不同的體驗。例如,如果服務器設在美國,那麼,美國終端用戶的體驗要遠遠好於澳大利亞的用戶,其中會涉及物理距離、延遲以及可能出現的數據表丟失問題。針對這一類問題,數據的區域化則是較好的方法。

對於新聞門戶網站來說,歐洲用戶一般僅關注本地的新聞,而不是南非等國家。這意味着,當編輯出版系統發佈一個新詞時,必須首先按區域存儲數據,然後在類似CQRS處理過程中對數據進行標準化。稍後,考慮到當前主題的特殊性,對應數據無須進行標準化處理。

田元基於地理位置劃分的數據分佈策略涵蓋了多樣化特徵,但不應忽視數據的區域化問題,因爲它將直接影響到應用程序的性能。

隔離——使用生態系統防止故障的出現

以下內容討論了故障的防止措施,以及如何開發具有高可用性和彈性的應用程序。本節將考察相關結構的保護方式。

冗餘設計

儘管我們的應用程序僅在單實例中執行,並且由於組件在容器中的分佈而變得更加靈活,但仍然會受到系統故障的影響。

冗餘是解決上述問題的一種有趣的方法。使用冗餘方案時,即使應用程序的一個節點丟失,其他節點仍可以繼續響應。

負載平衡器是微服務冗餘的一個很好的例子,它使用一.種策略來重定向請求。其中,我們可以創建大量節點,如果某個節點出現故障,仍然有另一個節點可以響應。

在下圖中,請求發送至負載平衡器中,並根據開發團隊和相關操作制定的某些規則,定向至業務層的實現中。

應用程序的所有節點一般不會出現整體崩潰。如果出現任何不穩定現象,我們仍有時間升至某個新版本;或者簡單地識別和修復所遇到的錯誤。

下面針對當前應用程序使用負載平衡器。對於大型應用程序來說,一種較好的方法是使用HAProxy。但在當前示例中,我們令Nginx擔負起這一職責 。

臨界分區

在理解我們正在開發的應用程序時,重要的一點是瞭解應用程序最常使用的訪問點,而此類訪問點往往難以發現。

假設我們正與某個在線銷售系統協同工作,在該應用程序類型中包含了多種組件,但顯然,最爲重要的組件是完成產品銷售部分的組件。然而,全部銷售流程與業務邏輯之間處於耦合狀態,這種耦合源自銷售界面直至付款。

上述在線商店似乎工作良好。只要向服務器羣添加更多的機器,系統的所有壓力都可以通過水平可擴展性得到解決。這裏的問題是,針對當前應用程序類型,水平擴展並非絕對可行。其中,許多資源處於共享狀態並可立即獲得。對於-一些難以令人滿意或者是無關緊要的結果,過程中的延遲將會造成很高的代價。再次強調,對於應用程序來說,“成功”一詞往往意味着嚴峻的問題。例如,網上商店在“黑色星期五”時即會面臨此類問題。

在幾分鐘內,可以查看到大量的點擊量,但是銷售額卻未見明顯增長,這絕非是正常現象。幾分鐘之後,就會發現問題所在。在這種情況下,主要問題來自處於耦合狀態的購買流。其間,在線商店的訪問量巨大,以至於阻塞了後續的購買流程。也就是說,搜索價格的用戶屏蔽了廣告產品的實際購買者。如果這種偏差十分嚴重,那麼,應採取相關措施處理此類問題。

這種在實時交互性應用程序中的故障是非常常見的。通常,點擊量-般大於完成整個交互性流程的用戶數量。此類場景適用於任何類型的實時應用程序,無論是在線商店、遊戲還是政府的稅務申報系統。所有這些應用程序均包含相同的特徵:高季節性訪問以及低轉換率。產品的轉換數量- -般不會受到影響,即使該數量低於點擊量。

當處理各類問題時,微服務架構通常十分有效。當採用DDD對每個域內的邊界上下文進行設置,以及執行關鍵評估時,應用程序每個部分可選取更加智能的架構。

在下圖中,採用臨界劃分這概念可使處理操作升至當前最爲需要的程序段。據此,軟件的其他部分則可避免外部大量點擊操作造成的壓力。

隔離設計

我們已經看到,通過臨界狀態分離應用程序包含了諸多優點。然而,一個常見的誤解是組件的複用。對此,一種較差的資源共享示例是數據庫的複用。無論負載平衡器、應用程序線程級別或者域劃分方式如何優化,如果應用程序僅依賴於單一物理組件,那麼,系統崩潰遲早會到來。

下圖中的設計方式即顯得較爲草率。其中,應用程序的全部組件(暫不考慮相關域)均依賴於同一物理組件,在該示例中則是數據庫。這一類錯誤 也常出現於緩存和消息代理中。

需要注意的是,無論是否緩存,數據也是微服務域中的一部分內容。物理組件應儘可能地處於獨立狀態,如下圖所示。

上圖中所示的物理組件方案則更爲常見,進而可簡化遷移和數據庫的分片機制。

快速故障

本節將考察一個在線商店示例。如前所述,就可用性來說,臨界劃分並遵循DDD邊界包含諸多優點。除此之外,前述內容還討論了軟件與物理組件依賴關係中蘊含的風險。

相應地,其中的大部分內容可消除系統架構中的故障或風險。有時,問題並不取決於我們,外部服務也會在生態系統中使用微服務。

對於在線商店的支付流程,微服務則是購物流程中的最後-一個步驟,並負責完成產品的付款操作。付款過程必須通過信用卡網關進行通信,如果這些網關中的任何一個出現中斷,鑑於外部服務的依賴關係,微服務可能會出現故障。

在下圖中,微服務支付過程嘗試構建與計費系統間的通信,但其中存在某些問題。

這裏,我們不能簡單地等待連接被恢復;相反,此處應產生快速故障並及時進行下一步處理。對於此類問題,斷路器是-一種較好的方法。據此,可以設置超時或連接故障策略,進而提供其他形式的付款方式,或者以友好的方式向用戶提供故障信息,從而避免了由於資源耗盡而導致的系統故障。

斷路器

斷路器是當出現過載或短路時自動關閉的操作開關。與電路保險絲類似,斷路器的目的是實現快速故障和保護電力裝置。對於微服務來說,斷路器可保護應用程序的整體完整性。

假設微服務的運行速度變得十分緩慢。其中,各方請求不斷出現並開始排隊。在某種程度上,這可視爲一種間接損害。特別是對於依賴於與其他微服務通信的微服務,此時需要使用斷路器。

斷路器的概念較爲簡單,其中僅包含了兩種狀態,如下所示。

開:釋放對外部依賴關係的調用。

關:即刻放棄當前調用,並執行先前配置的操作。

在實際操作過程中,斷路器自身將置於調用中,而不是微服務直接訪問外部依賴項。對於基於預定義參數產生的任何故障,例如超時,斷路器將中斷與故障依賴項間的通信。當然,微服務方面也可以採取一些措施。斷路器的行爲如下圖所示。

一些框架可以幫助實現斷路器。目前,較爲突出的框架是由Netflix開發團隊創建的Hystrix。Hystrix 最初是在Java 中開發的,但是已經有其他編程語言實現了該算法,如Go語言。

總結

本篇討論了一些較好的實踐方案,以使微服務更具彈性和容錯能力。另外,還介紹了應用程序中容器的使用方式,並使用Nginx開發UsersService 集羣。

以上只是小編個人的想法,如果哪裏有不準確的地方,還請多多評論指出,咱們共同學習進步。如果覺得對自己的學習有幫助,請多多哦點贊評論轉發,關注小編,你們的支持就是小編創作最大的動力~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章