微服務架構通過一種良好的服務邊界劃分,能夠有效地進行故障隔離,但就像其他分布式系統一樣,在網路、硬體或者應用級別上容易出現問題的機率會更高,服務的依賴關系,導致在任何組件暫時不可用的情況下,就它們的消費者而言都是可以接受的,為了能夠降低部分服務中斷所帶來的影響,我們需要構建一個容錯服務,來優雅地應對特定型別的服務中斷,
本文基于一些在RisingStack的顧問咨詢與開發經驗,介紹了如何運用一些最常用的技術和架構模型,去構建與維護一個高可用的微服務系統,
如果你不熟悉本文中的模式,并不意味著你做錯了什么,畢竟構建一個高可用的系統需要很多額外的付出,
*微服務架構的風險 The Risk of the Microservices Architecture
微服務的架構將應用的邏輯移動到一個服務里面,服務之間通過網路層進行通信互動,通過網路通信互動的方式取代了記憶體的呼叫,同時需要多個物理和邏輯組件之間的相互協作,給系統帶來了額外的延遲性與復雜性,分布式系統復雜性的增加,導致了特定網路故障的可能性變得更大,
微服務允許你實作優雅的服務降級,因為組件可以被單獨的設定為失敗,
團隊可以獨立地設計、開發與部署他們的服務,是微服務的最大優點之一,他們完全擁有整個服務的生命周期,這也意味著團隊無法控制他們的服務依賴,因為這些服務更有可能是不同的團隊在管理,我們需要記住,提供者的服務由于發布中斷、配置等等其他的改變而暫時不可用,他們是由別人控制,并且組件之間獨立活動,
*優雅的服務降級 Graceful Service Degradation
微服務最佳優勢之一,當某個組件單獨失敗時,你可以實作優雅的服務降級,進行故障隔離,例如,一個照片共享的應用,由于中斷,用戶可能無法上傳新的照片,但他們仍然可以瀏覽、編輯和分享他們現有的照片,

在大多數情況下,在一個分布式系統中,應用程式之間互相依賴,實作一種優雅的服務降級,這是很困難的,你需要采取多種故障切換邏輯(其中一些會在本文后面進行討論),應對臨時的故障與中斷,

*變更管理 Change management
谷歌網站的可靠性團隊(SRE)發現,大約70%的中斷是由一個實時系統的改變而引起,當你在服務中更改某些內容時——你部署了新版本的代碼或更改了一些配置——總會導致更高的失敗機率或者引入一個新的bug,
在微服務架構中,服務之間彼此依賴,這就是為什么你應該盡量減少失敗,并限制它們的負面影響,如果要處理來自變更的問題,你可以使用變更管理策略和自動升級,
例如,當需要部署新代碼或者更改某些配置時,你應該逐漸地將這些更改應用于實體的子集,監控它們,甚至當你看到關鍵指標有負面影響時,它們會自動回滾恢復,

另一個解決方案,就是運行兩個生產環境,只部署其中一個,并且在驗證新版本的運行符合預期之后,才會將負載均衡指向新版本,這被稱為藍綠色部署,或紅黑色部署,
恢復代碼不是一件壞事情,你不應該把壞的代碼留在生產中,然后再思考哪里出了問題,必要的時候,總是要恢復你的改變(回滾),越快越好,
*健康檢查與負載均衡 Health-check and Load Balancing
實體會因為失敗、部署或自動伸縮,而不斷地啟動、重新啟動和停止,這會導致服務暫時或永久不可用,為了避免問題,你的負載均衡應該跳過不健康的實體,因為它們不能滿足你的用戶或子系統的需要,
應用實體健康可以通過外部觀察來決策,你可以反復呼叫 GET /health 請求埋點或自身報告,現代服務發現解決方案,將不斷從實體中收集健康資訊,并配置負載均衡以保證健康的組件路由流量,
*自愈 Self-healing
自我修復可以幫助恢復應用程式,我們談論的自愈,是指應用程式可以做一些必要的步驟來恢復崩潰狀態,在大多數情況下,這樣的操作是經由一個外部系統來實作的,它會監控實體的健康,并在它們較長時間處于錯誤狀態的情況下,重新啟動應用程式,自愈是非常有用的,但是在某些情況下,不斷地重啟應用程式會引起麻煩,由于負載過高或者資料庫連接超時,你的應用程式不停的重啟,會導致無法提供一個正確的健康狀態,
實作一種為微妙的情況而準備的高級自我修復解決方案,可能會很棘手,比如資料庫連接丟失,在這種情況下,你需要為應用程式添加額外的邏輯來處理一些極端情況,并讓外部系統知道不需要立即重啟實體,
*故障切換快取 Failover Caching
服務通常會因為網路問題和系統的變更而失敗,由于自愈和先進的負載均衡,大多數中斷只是暫時的,然而我們還應該找到一個解決方案,讓我們的服務在這些故障中能夠正常作業,這就是故障切換快取,它可以幫助應用程式提供一些必要的資料,
故障切換快取一般使用兩個不同的過期時間,設定一個較短的時間,顯示在正常情況下可以使用多長時間的快取;設定另一個較長的時間,顯示在發生故障期間,可供使用快取資料的時間會有多久,

很重要的一點是,只有當過時的資料比什么都不做要好的情況出現時,才可運行故障切換緩,
可以通過使用HTTP中的標準回應頭(response header)來設定快取和故障轉移快取,
例如,通過設定 header 引數 max-age 來指定一個資源被重繪時最大時間;也可以通過設定 header 引數 stale-if-error 來決定,在服務失敗的情況下,需要多長時間從快取獲取資料,
現代的CDN和負載均衡器提供了各種快取和故障切換的方式,你也可以為公司建立一個包含了統一的可靠性解決方案的共享標準庫,
*重試機制 Retry Logic
在某些特定的場景下,我們可能無法快取資料,或者我們想對其做出一些更改,但是我們的操作最侄訓是會失敗,在這些情況下,我們可以重新嘗試我們的操作,因為我們可以預計資源在一段時間后會恢復,或者我們的負載均衡將我們的請求轉發到一個健康的實體,
在應用程式和客戶端添加重試邏輯需保持謹慎,因為大量的重試會讓事情變得更糟,甚至會阻止應用程式的恢復,
在分布式系統中,微服務系統重試會觸發多個其他的請求或重試,引起一個級聯效應,為了盡量減少重試帶來的影響,你應該最大限度限制它們的發生次數,并使用指數補償演算法來持續增加重試之間的延遲,
重試由客戶端(瀏覽器,其他微服務等)發起,客戶端不知道這個操作是在處理請求之前失敗還是之后失敗的,你應該準備好應用程式來處理冪等性(idempotency),例如,當操作重試購買時,不應該對用戶進行重復扣費,對于每個事務,使用唯一的 冪等令牌(idempotency-key ),可以幫助處理重試,
*限流與降級 Rate Limiters and Load Shedders
限流是指在一個時間段內,特定的用戶或應用程式可以接識訓處理多少請求的技術,例如,有了限流,你就可以找出引起流量高峰的用戶和微服務,或者可以確保應用不會在負載過高情況下,發生自動擴容都不能拯救,
你還可以限制業務優先級較低的流量,以便為核心業務提供足夠的資源,

另外一種型別的限速器稱為并發請求限制,當有一些昂貴的端點不應該超過指定的呼叫次數,但你仍然希望提供流量服務時,選擇這樣的操作是很有用的,
快速降級可以確保總是有足夠的可用資源去服務關鍵的事務,它為高優先級請求保留一些資源,并且不允許低優先級事務使用所有的資源,降級與否是根據系統的整個狀態進行判斷的,而不是基于單個用戶的請求桶大小,服務降級用于幫助恢復系統,當發生一些事故時,它們可以保證核心功能仍然繼續作業,
如需獲取更多有關限流與降級的資訊,推薦前往https://stripe.com/blog/rate-limiters,閱讀Stripe的文章,
*快速且獨立地失敗 Fail Fast and Independently
在微服務體系結構中,我們希望我們的服務能夠快速、獨立地失敗,為了在服務級別上隔離問題,我們可以采用艙壁模式(bulkhead pattern),你稍后可以在這篇文章中讀到更多關于艙壁的資訊,
我們還希望我們的組件快速失敗,因為我們不想等待壞的實體超時,沒有什么比一個掛著的請求和一個沒有回應的UI更令人失望的了,這樣不僅浪費資源,而且還會對用戶體驗造成影響,我們的服務是相互呼叫的,所以更應該額外注意,在這些延遲結束之前,阻止掛起操作,
第一個想到的想法是在每個服務呼叫上運用一個較好級別的超時時間,這種方法的問題在于,你不可能真正知道什么是一個好的超時時間值,因為在某些情況下,網路故障和其他問題只會影響到一兩個操作,在這種情況下,如果只有少數幾個請求超時,你可能不想拒絕這些請求,
我們可以說,在微服務中使用超時來實作快速失敗的例子是一種反模式,你應該避免它,你可以依賴于操作成功/失敗統計資料的斷路器(circuit-breaker)模式,而不是超時,
*艙壁 Bulkheads
艙壁被用來將一艘船劃分成多個部分,這樣就可以在船體破裂的情況下對部分封閉,
隔離壁的概念可以應用于軟體開發中,做到資源隔離,
通過采用艙壁模式,我們可以保護有限的資源不被耗盡,例如,如果我們有兩種操作,它們與相同的資料庫實體互動,我們的連接數量有限,那么我們可以使用兩個連接池,而不是共享連接池,由于此客戶端資源分離,當發生超時或者過度使用連接池的操作,不會導致所有其他操作的關閉,
泰坦尼克號沉沒的主要原因之一,就是它的艙壁有一個設計上的失敗,水可以通過艙壁頂部上的甲板注入,淹沒整個船體,

*斷路器 Circuit Breakers
為了限制操作的持續時間,我們可以使用超時,超時可以防止掛起操作并保持系統回應,然而,在微服務通信中使用靜態的、微調的超時是一種反模式,因為我們處在一個高度動態的環境中,幾乎不可能發現正確的時間限制,以確保在每個場景下都能很好地作業,
我們可以使用熔斷來處理錯誤,而不是使用小的特定事務的靜態超時,斷路器是以真實世界電子元件命名的,因為它們的行為是相同的(簡單的說,這種模式主要是參考電路熔斷,如果一條線路電壓過高,保險絲會熔斷,防止火災),你可以保護資源,幫助他們用斷路器恢復,它們在分布式系統中非常有用,因為重復的失敗會導致滾雪球效應(snowball effect),導致整個系統癱瘓,
當一個特定型別的錯誤在短時間內多次出現時,斷路器就會打開,斷路器的打開,阻止了進一步的資源請求——就像真的阻止了電流的流動,斷路器通常在一定時間后關閉,為基礎服務提供足夠的空間來恢復,
請記住,并非所有的錯誤都應該觸發斷路器,例如,你可能希望跳過客戶端問題,比如跳過 4xx 狀態碼回應的請求,但不包括 5xx 服務器端錯誤的請求,一些斷路器也可以有半開狀態,在此狀態下,服務發送第一個請求檢測系統的可用性,同時讓其他請求失敗,如果第一個請求成功,它將斷路器恢復到一個關閉狀態,并允許流量進入,否則,它就會打開,

*測驗失敗 Testing for Failures
你應該不斷地測驗你的系統以防止常見問題,以確保你的服務能夠承受住各種失敗,你應該頻繁地測驗失敗,讓你的團隊為發生事故而做好準備,
對于測驗,你可以使用一個外部服務來標識實體組,并隨機終止該組中的一個實體,有了這個,你就可以為單個實體的失敗做準備,你甚至可以關閉整個可用區來模擬云提供商的中斷,
最流行的測驗解決方案之一是由Netflix提供的ChaosMonkey彈性工具,
*結尾
實作和運行可靠的服務并不容易,這需要你付出很大的努力,也要花費你的公司很多錢,
可靠性有很多的層次和方面,所以為你的團隊找到最好的解決方案是很重要的,你應該將可靠性作為業務決策程序中的一個因素,并為它分配足夠的預算和時間,
*主要識訓
動態環境和分布式系統——比如微服務——會導致更大的失敗機率,
服務應該單獨失敗,實作優雅的降級,用以改善用戶體驗,
70%的中斷是由變更引起的,恢復代碼并不是件壞事,
快速和獨立的失敗,團隊無法控制他們服務的依賴,
架構模式和技術,如快取、艙壁、限流、熔斷,有助于建立可靠的微服務,
原文:https://blog.risingstack.com/designing-microservices-architecture-for-failure/
譯文:https://blog.csdn.net/xushuai110/article/details/77726447
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2021最新版)
2.別在再滿屏的 if/ else 了,試試策略模式,真香!!
3.臥槽!Java 中的 xx ≠ null 是什么新語法?
4.Spring Boot 2.5 重磅發布,黑暗模式太炸了!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/294841.html
標籤:其他
下一篇:requests模塊的使用
