tft每日頭條

 > 科技

 > 分布式系統詳細介紹

分布式系統詳細介紹

科技 更新时间:2024-07-22 13:19:13

分布式系統詳細介紹(一張無腦清單告訴你分布式系統代碼有多複雜)1

開篇

微服務架構在當今的軟件工程領域被廣泛采用。同時,采用分布式架構的組織也發現需要考慮分布式故障的附加複雜性,而這種複雜性往往超出實際業務邏輯。

雖然分布式計算的謬誤是有據可查的,但對于組織而言并不是一件容易的事情。因此,構建大規模、可靠的分布式系統架構就成為一個難題。作為推論,當我們将網絡交互的複雜性引入其中時,在原先非分布式系統中看起來很好的代碼就有可能成為一個大問題。

在生産代碼中摸爬滾打幾年後,遭遇了各種故障模式并且發現導緻故障的根源之後,我逐漸能夠識别一些更常見的故障模式。由于不同公司以及使用不同的語言堆棧之間存在差異(取決于内部基礎設施和工具的成熟度),但是可以從産生問題的原因中總結出一些具有共性的經驗。

下面就是我從這些經驗中總結出來的一些代碼審查指南,這個指南可以形成一份清單,并用來審查分布式環境中與系統間通信相關的代碼。雖然這份清單上提到的問題并不适用所有情況,但它們覆蓋了代碼審查的基本面,可以按照這個清單将問題走查一遍,在此過程中标記缺失的項目以供進一步讨論,利用這種方式發現系統中的問題是非常行之有效的。從這個意義上來說,可以通過這個“無腦清單”來發現大多數問題。

如何調用遠程系統1、當遠程系統發生故障時會發生什麼?

無論系統設計的多麼謹慎,它都會出現故障 - 這是在生産中被印證的事實。故障的發生可能源于代碼錯誤,基礎設施問題,流量激增,系統疏于管理等,總之結果是引發故障。調用者如何處理故障将決定整個架構的彈性和健壯性。

定義錯誤處理路徑:必須在代碼中明确錯誤處理路徑,而不是讓系統在最終用戶面前崩潰。這裡需要向用戶明确指出錯誤,例如:設計良好的錯誤頁面、帶有錯誤信息的異常日志,以及帶有回退機制的斷路器等。

制定恢複計劃:考慮代碼中的每一次遠程交互,并弄清楚如何恢複被中斷的工作。思考如下價格問題:工作流程是否需要有狀态才能從故障點觸發?是否将所有失敗的有效請求發布到重試隊列/數據庫表,并在遠程系統恢複時重試請求?是否有腳本來比較兩個系統的數據庫并以某種方式使它們同步?在部署系統之前,是否有一個明确的系統的恢複計劃?

2、當遠程系統變慢時會發生什麼?

這種情況比徹底失敗更難辦,因為我們不知道遠程系統是否在工作。因此需要檢查以下事項從而處理這種情況。如果我們使用類似 Istio的服務網格技術,其中一些問題可以輕松搞定而不需要修改應用程序代碼。即便如此,我們也應該關注這些問題。

為遠程系統調用設置超時:這包括遠程 API 調用、事件發布和數據庫調用的超時時間。我在很多代碼中發現過這個問題,因此需要檢查遠程系統是否設置了合理的超時時間,從而避免該系統在無響應時調用者因為等待而浪費資源的情況發生。

超時重試:網絡和系統并不是100%可靠的,重試對于系統恢複是非常必要的。重試機制會消除系統交互中的許多“問題”。如果可能,在重試中使用某種補償機制(固定的、指數的)。在重試機制中添加一點抖動(這裡的抖動可以理解為随機重試,例如設置随機的重試時間3-5s重試一次,避免所有調用者一起地不斷地對被調用者進行重試,導緻被調用者的負載增大),這樣做可以給被調用系統一些喘息的空間,通過能夠保證調用者在負載下獲得更好的調用成功率。重試的另一面是幂等性,我們将在本文後面介紹。

使用斷路器:一些應用程序并沒有預先打包這個功能,但我看到公司内部會編寫自己的包裝器。如果你有這個需求,一定要實現它,對斷路器的投入會讓你獲益。它會提供明确的框架來定義錯誤情況下的回退策略。

不要把超時當作請求失敗來處理——超時不是失敗,而是一種不确定的場景,應該通過一種處理方式來應對這種不确定性。因此需要建立明确的處理機制,允許系統在發生超時的情況下進行同步。處理機制可以是簡單的協調腳本,也可以是有狀态的工作流,或者是通過死信隊列(消息被拒絕、消息TTL過期、隊列達到最大長度)實現。

不要在事務中調用遠程系統——當遠程系統訪問速度變慢時,依舊會長時間保持數據庫連接,如果訪問持續而因為速度的問題一直無法完成系統的訪問,會導緻數據庫的連接也無法釋放,也就将數據庫連接用完,最終造成系統中斷的後果。

使用智能批處理:如果處理大量數據請求,可以逐個進行批量遠程調用(API 調用、數據庫讀取)從而消除網絡開銷。每個批量處理的量越大,整體延遲就會越大,可能失敗的工作單元也會越多。因此需要針對性能和容錯性優化批量大小。

如何面對調用方請求

所有 API 必須保證幂等性:幂等性是為了實現調用方API的超時重試功能。隻有API 能夠支持安全重試且不會有副作用時,調用者才能安心使用重試功能。這裡的API 是指同步 API 和任何消息傳遞接口——調用者可能會發布兩次相同的消息(或者代理可能會發送兩次)給到該API。

明确定義響應時間和吞吐量 SLA 以及遵守定義的規則:在分布式系統中,快速失敗比讓調用者等待要好得多。誠然,吞吐量 SLA 很難實現(分布式速率限制一個難題),但我們需要确保SLA在主動呼叫失敗時做好準備。另一個重要方面是了解下遊系統的響應時間,以确定系統最快的速度。

定義和限制批處理 API:如果公開批處理 API,則應明确定義最大批處理的數量,這個數量需要受到SLA的 限制,也就是需要遵守 SLA的規則定義。

預先考慮可觀察性:可觀察性意味着能夠分析系統的行為,而無需通過查看API或組件的内部來實現。預先考慮你關心的系統指标以及需要收集的數據,幫助你回答以前未提出的問題。再對系統進行檢測并獲得這些數據。執行此操作的一個強大機制是識别系統的域模型,當域中發生某個事件時進行發布事件的操作。(例如收到請求 id 123,返回請求 123 的響應——注意如何使用這兩個“域”事件會導出一個稱為“響應時間”的新指标。将原始數據轉換到預先确定的聚合中)。

一般性原則

盡量使用緩存:網絡變化無常,因此盡可能多地使用緩存,并不斷講最新的數據保存其中。當然,有可能會使用遠程緩存機制(例如,Redis 服務器運行在單獨的服務器上),但至少通過緩存的方式可以将數據帶入控制域并減少系統的負載。

考慮單元故障:如果一個 API 或一條消息代表多個工作單元(批處理),那麼需要思考單元故障意味着什麼?如果有效載荷都失敗一次意味着什麼?又或者單個單元獨立成功或失敗意味着什麼?部分成功呢,API 是否響應成功或失敗代碼?

這裡的意思是一個API調用多個工作單元,這裡的工作單元可以是一個組件或者是一個API。有可能在調用多個工作單元的時候,其中一個工作單元失敗了,或者有的工作單元成功了,這個時候作為最外層調用這些工作單元的API來說要考慮好是成功還是失敗,如果失敗如何返回失敗信息。

在系統邊緣隔離外部域對象:不允許以重用的名義在系統中使用其他系統的域對象。這将會加劇我們的系統與其他系統的實體建模的耦合,在其他系統發生更改時,我們的系統都會進行大量重構。我們應該始終構建自己的實體表示并将外部有效負載轉換為此我們系統内的模式,然後我們的系統中使用它。

安全性

在每個邊緣清理輸入:在分布式環境中,系統的任何部分都可能受到損害(從安全角度來看)。因此,在系統邊界處會對進入系統的數據進行“消毒”處理,這裡有一個假設就是這些進入系統的數據有可能不是幹淨或安全的。

永遠不要提交憑證(Credentials):永遠不要将憑證(數據庫用戶名/密碼或 API 密鑰)提交到代碼庫。雖然提交憑證到代碼庫對于某些人來說是常規操作,但我們需要摒棄這種陋習。始終遵守“憑證必須始終從外部(有安全存儲保證)加載到系統”的規則。

譯者介紹

崔皓,51CTO社區編輯,資深架構師,擁有18年的軟件開發和架構經驗,10年分布式架構經驗。曾任惠普技術專家。樂于分享,撰寫了很多熱門技術文章,閱讀量超過60萬。《分布式架構原理與實踐》作者。

來源:51CTO技術棧

作者:Kislay Verma

編譯:崔皓

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved