分佈式系統關注點:「無狀態」 詳解

收藏待读

分佈式系統關注點:「無狀態」 詳解

如果第二次看到我的文章,歡迎點上方 ▲藍字 關注喲~

本文長度為 2728字 ,建議閱讀 8 分鐘。

堅持原創,每一篇都是用心之作~

分佈式系統關注點:「無狀態」 詳解

前面聊完的2個章節「數據一致性」和「高可用」其實本質是一個通過提升複雜度讓整體更完善的方式。

接下去我們開始聊一些讓系統更簡單,更容易維護的東西——「 易伸縮 」,首當其衝的第一篇文章就是「 stateless 」,也叫「 無狀態 」。

z哥帶你先來認識一下「狀態」是什麼。

初識「狀態」

之前在「負載均衡」的第四篇( 分佈式系統關注點——做了「負載均衡」就可以隨便加機器了嗎? )中提到過一個例子,我們再翻出來一下。

開發Z哥對運維Y弟喊:「Y弟,現在系統好卡,剛上了一波活動,趕緊幫我加幾台機器上去頂一下。」

Y弟回復說:「沒問題,分分鐘搞定」。

然後就發現數據庫的壓力迅速上升,DBA就吼了:「Z哥,你丫的搞什麼呢?數據庫要被你弄垮了」。

然後客服那邊接框也爆炸了,越來越多的用戶說剛登陸後沒多久,操作着就退出了,接着登陸,又退出了,到底還做不做生意了。

這個案例中的問題,產生的根本原因是因為系統中存在着大量「有狀態」的業務處理過程。

「有狀態」和「無狀態」

N.Wirth曾經在它1984年出版的書中將程序的定義經典的概括為: 程序=數據結構+算法 。( 這個概括也是這本書的書名

這是一個很有意思的啟發,受它的影響,z哥認為 程序做的事情本質就是「數據的移動和組合」 ,以此來達到我們所期望的結果。而如何移動、如何組合是由「算法」來定的,所以z哥延伸出一個新的定義: 數據+算法=成果

通過程序處理所得到的「成果」其實和你平時生活中完成的任何事情所得到的「成果」是一樣的。任何一個「成果」都是你通過一系列的「行動」將最開始的「原料」進行加工、轉化,最終得到你所期望的「成果」。

分佈式系統關注點:「無狀態」 詳解

比如,你將常溫的水,通過「倒入水壺」、「通電加熱」等工作後變成了100度的水,就是這樣一個過程。

正如燒水的例子,大多數時候得到一個「成果」往往需要好幾道「行動」才能完成。

分佈式系統關注點:「無狀態」 詳解

這個時候如果想降低這幾道「行動」總的成本( 如:時間 )該怎麼辦呢?

自然就是提煉出反覆要做的事情,讓其只做一次。而這個事情在程序中,就是將一部分「數據」放到一個「 暫存區 」( 一般就是 本地內存 ),以提供給相關的「行動」共用。  

分佈式系統關注點:「無狀態」 詳解

但是如此一來,就導致了需要增加一道關係,以表示每一個「行動」與哪一個「暫存區」關聯。因為在程序里,「行動」可能是「多線程」的。

這時,這個「行動」就變成「有狀態」的了。

分佈式系統關注點:「無狀態」 詳解

題外話 共用同一個「暫存區」的多個「行動」所處的環境經常被稱作「上下文」。

我們再來深入聊聊「有狀態」。

「暫存區」里存的是「數據」,所以可以理解為「有數據」就等價於「有狀態」。

「數據」在程序中的作用範圍分為「局部」和「全局」( 對應局部變量和全局變量 ),因此 「狀態」其實也可以分為兩種,一種是局部的「會話狀態」,一種是全局的「資源狀態」

題外話 因為有些服務端不單單負責運算,還會提供其自身範圍內的「數據」出去,這些「數據」屬於服務端完整的一部分,被稱作「資源」。所以,理論上「資源」可以被每個 「會話」來使用,因此是全局的狀態

本文聊的「有狀態」都指的是「會話狀態」。

與「有狀態」相反的是「無狀態」, 「無狀態」意味着每次「加工」的所需的「原料」全部由外界提供,服務端內部不做任何的「暫存區」。並且請求可以提交到服務端的任意副本節點上,處理結果都是完全一樣的

有一類方法天生是「無狀態」,就是負責表達移動和組合的「算法」。因為它的本質就是:

  1. 接收「原料」( 入參

  2. 「加工」並返回「成果」( 出參

為什麼網上主流的觀點都在說要將方法多做成「無狀態」的呢?

因為我們更習慣於編寫「有狀態」的代碼,但是「有狀態」不利於系統的易伸縮性和可維護性。

在分佈式系統中,「有狀態」意味着一個用戶的請求必須被提交到保存有其相關狀態信息的服務器上,否則這些請求可能無法被理解,導致服務器端無法對用戶請求進行自由調度( 例如雙11的時候臨時加再多的機器都沒用 )。

同時也導致了容錯性不好,倘若保有用戶信息的服務器宕機,那麼該用戶最近的所有交互操作將無法被透明地移送至備用服務器上,除非該服務器時刻與主服務器同步全部用戶的狀態信息。

這兩個問題在負載均衡的第四篇( 分佈式系統關注點——做了「負載均衡」就可以隨便加機器了嗎? )中也有提到。

但是如果想獲得更好的伸縮性,就需要盡量將「有狀態」的處理機制改造成「無狀態」的處理機制。

「無狀態」化處理

將「有狀態」的處理過程改造成「無狀態」的,思路比較簡單,內容不多。

首先, 狀態信息前置,豐富入參 ,將處理需要的數據儘可能都通過上游的客戶端放到入參中傳過來。

分佈式系統關注點:「無狀態」 詳解

當然,這個方案的弊端也很明顯: 網絡數據包的大小會更大一些

另外,客戶端與服務端的交互中如果涉及到多次交互,則需要來回傳遞後續服務端處理中所需的數據,以避免需要在服務端暫存。

分佈式系統關注點:「無狀態」 詳解

▲橙色請求,綠色響應

這些改造的目的都是為了盡量少出現類似下面的代碼。

func(){
    return i++;
}

而是變成:

func(i){
    return i+1;
}

要更好的做好這個「無狀態」化的工作,依賴於你在架構設計或者項目設計中的合理分層。

盡量將會話狀態相關的處理上浮到最前面的層,因為只有最前面的層才與系統使用者接觸,如此一來,其它的下層就可以將「無狀態」作為一個普遍性的標準去做。

與此同時,由於會話狀態集中在最前面的層,所以哪怕真的狀態丟失了,重建狀態的成本相對也小很多。

比如三層架構的話,保證BLL和DAL都不要有狀態,代碼的可維護性大大提高。

如果是分佈式系統的話,保證那些被服務化的程序都不要有狀態。除了能提高可維護性,也大大有利於做灰度發佈、A/B測試。

題外話在這裡,提到做分層的目的是為了說明,只有將IO密集型程序和CPU密集型程序分離,才是通往「無狀態」真正的出路。 一旦分離後,CPU密集型的程序自然就是「無狀態」了。

如此也能更好的做「彈性擴容」。因為常見的需要「彈性擴容」的場景一般指的就是CPU負荷過大的時候。

最後,如果前面的都不合適,可以 將共享存儲作為降級預案來運用 ,如遠程緩存、數據庫等。然後當狀態丟失的時候可以從這些共享存儲中恢復。

所以,最理想的狀態存放點。要麼在最前端,要麼在最底層的存儲層。

分佈式系統關注點:「無狀態」 詳解

總結

任何事物都是有兩面性的,正如前面提到的,我們並不是要所有的業務處理都改造成「無狀態」,而只是挑其中的一部分。最終還是看「價值」,看「性價比」。

比如,將一個以「狀態」為核心的即時聊天工具的所有處理過程都改造成「無狀態」的,就有點得不償失了。

相關文章:

如果你喜歡這篇文章,可以點一下右下角的「 好看 」。

這樣可以給我一點反饋。: )

謝謝你的舉手之勞。

喜歡

分享

or

▶ 關於作者:張帆(Zachary)。 堅持原創,每一篇都是用心之作

本文首發於公眾號:「 跨界架構師」 (ID:Zachary_ZF)。

如果你有關於軟件架構、分佈式系統、產品、運營的困惑

可以試試點擊「 閱讀原文

分佈式系統關注點:「無狀態」 詳解

也可以讀讀這些文章 :point_down:

分佈式系統關注點:「無狀態」 詳解

分佈式系統關注點:「無狀態」 詳解

分佈式系統關注點:「無狀態」 詳解

分佈式系統關注點:「無狀態」 詳解

原文 :

相關閱讀

免责声明:本文内容来源于mp.weixin.qq.com,已注明原文出处和链接,文章观点不代表立场,如若侵犯到您的权益,或涉不实谣言,敬请向我们提出检举。