無狀態服務

數據服務,正如其名,能夠嚮應用提供數據存儲與訪問的服務。程序員

好比一個遊戲場景服務器,能夠在玩家進入的時候,向數據服務請求該玩家的數據。邏輯中處理不一樣玩家的數據交互,修改不一樣玩家的數據,而後以必定策略再將數據存回數據服務。web

同理如一個web應用服務器, client 發一個請求,應用向數據服務請求數據,修改數據,應用向數據服務存回數據。redis

換言之,若是把服務端看做一個總體,數據服務維護的是client在服務端的狀態。緩存

可是,咱們以前實現的數據服務能夠提供更強大的抽象能力。安全

咱們的數據服務的API結構能夠任意定製,基於ORM和代碼自動化生成實現了code-first,並且數據服務依賴的基礎設施——redis又被證實很是強大:不只僅是性能極佳,並且提供了多種數據結構抽象。服務器

那麼,數據服務是否能在維護client狀態以外仍有用武之地?網絡

在web開發中,用緩存維護服務狀態是一種很常規的開發思路。 而在遊戲服務端開發中,因爲場景服務的存在,這種思路一般並不靠譜。數據結構

爲何要用數據服務維護其餘服務狀態?架構

考慮這樣一個問題:app

若是服務的狀態維護在服務進程中,那麼服務進程掛掉,狀態就不存在了。可是對於咱們來講,服務的狀態是比服務進程自己更加劇要的——由於進程掛了能夠馬上重啓,哪怕耽誤個一、2s,可是狀態沒了卻意味着這個服務在整個分佈式服務端中所提供的語義已經不正確了,即便瞬間就重啓好了也沒用。

那麼爲了讓服務進程掛掉時不會致使服務狀態丟掉,只要分離服務進程的生命週期和服務狀態的生命週期就能夠了。

這種服務其實就是無狀態服務(stateless service)。

貼一下wiki:

Service statelessness is a design principle that is applied within the service-orientation design paradigm, in order to design scalable services by separating them from their state data whenever possible. 

好處:

This results in reduction of the resources consumed by a service as the actual state data management is delegated to an external component or to an architectural extension.

兩個關鍵點: design scalable services, reduction of the resources consumed。

其實描述的是同一個意思,以無狀態爲原則設計服務可讓服務的橫向擴展成本降到最低。

對於web開發程序員來講,無狀態服務並非什麼新鮮事,甚至由於無狀態服務的一些弊端而改用有狀態服務(stateful service)。不過,咱們今天並不討論哪種更好,只討論 無狀態服務 更適合 什麼情景。

無狀態服務的實現方式有不少種,在一些C++主導的互聯網公司,一般是藉助共享內存實現——經過將 服務狀態維護在共享內存裏,實現服務進程與服務狀態的分離。 可是這種作法擴展性不強,很難跨物理機,並且共享內存就這樣一個文件安全性很難保障。

不少只接觸過遊戲服務端的同窗並不瞭解無狀態服務,畢竟一直寫的是交互性極強的場景服務,並且遇事首先想到的是熱更新,不知道其餘的hotfix方式。

以前小說君跟有些同事討論無狀態服務,發現對方還會產生至關程度的誤解,把「無狀態服務」與「無狀態客戶端」混爲一談。 無狀態的遊戲客戶端意味着網絡通訊的成本跟內存數據訪問的成本同樣低——這固然是不可能實現的。

無狀態服務就是爲了scalability而出現的,無狀態服務橫向擴展的能力相比於有狀態服務大大加強,同時實現負載均衡的成本又遠低於有狀態服務。

設計分佈式系統時,咱們沒法兼顧一致性C、響應性能A,以及分區容錯P。無狀態服務更傾向於CP,有狀態服務更傾向於AP。可是,即便是如今(好比藉助各類一致性協調組件,或者gossip protocol),有狀態服務的P與無狀態服務的P所能達到的程度是不同的,後者是真的容錯,前者只能作到不把雞蛋放在一個籃子裏。

遊戲中能夠拆分爲無狀態服務的業務情景其實有不少,基本上全部服務間交互需求均可以實現爲無狀態服務。好比切場景服務,由於切場景的請求是有限的,對時延的要求也不會特別高,同理的還有分配房間服務;或者是面向客戶端的IM服務、拍賣行服務等等。

回到一開始的問題, 咱們能夠將服務狀態存放在外部設施中,好比數據服務。

數據服務,能夠轉移無狀態服務的狀態維護成本,讓無狀態服務橫向擴展的能力更強大。由於狀態維護在數據服務中,因此無狀態服務開多少個都無所謂。所以無狀態服務很是適合計算密集的業務需求。

實際上,咱們講面向微服務,講服務劃分,要解決的根本問題就是讓程序員能清楚本身定義每種服務的意圖是什麼,請求響應式服務、數據同步式服務、流處理式服務,消息流與業務對CAP的需求決定了服務更適合作成無狀態仍是有狀態。

舉個遊戲開發的例子,假設業務情景是比較常見的全球同服類遊戲,那遊戲的每種服務理應是由分佈式的多個節點共同提供服務,若是服務的消息流是典型的請求響應式,那麼實現爲無狀態服務就更合適:

  • 請求上來,取相關數據,處理,直接返回。整個狀態的生命週期保持在一次RPC調用過程當中,這描述的就是請求響應的工做方式。

  • 不管是本身開發load balancer,仍是用現成的消息broker(好比前篇文章說的RabbitMQ),對無狀態服務作 round-robin work distribution都很是簡單。

  • 並且,無狀態服務本質上是沒有擴容成本的,波峯就多開,波谷就少開。

程序員能夠爲不一樣服務規劃不一樣的橫向擴展方式。對於無狀態服務,橫向擴展的觸發條件就是如今請求數量級或者是節點壓力;對於有狀態服務,橫向擴展就須要藉助第三方的服務做爲仲裁者,而這個仲裁者能夠實現爲無狀態服務。

固然,無狀態服務並非萬能的。 畫圖解釋,有狀態服務A與B:

最多見的組織形式,每一個有狀態服務都是一個進程。

無狀態服務A:

其中,節點A與節點B都是無狀態服務A的一個實例。

從這兩張圖就能簡單看出, 無狀態服務只是把無狀態性( statelessness )從服務中獨立出來。

好處天然是如前面所說,將進程的生命週期與狀態的生命週期解耦,並且咱們如今把共享狀態寄存在數據服務,那隻須要保證數據服務高可用,全部的無狀態服務都能高可用。相反,若是是有狀態服務想提供高可用保證,就須要各自實現高可用機制。

可是,無狀態服務還有兩點弊端:

  1. 節點訪問狀態從進程內變爲進程外,沒了locality,形成了資源浪費。

  2. 因爲操做同一client的數據的再也不是惟一的節點,有可能造成數據的不一致(這點下篇文章再聊)。

這也是程序員的平常抉擇之一:trade-off。

哪些服務須要設計爲無狀態服務,哪些服務須要設計爲有狀態服務,徹底是業務需求決定的。二者的設計範式(design paradigm)徹底不一樣。

  • 無狀態服務描述的是數據流,Data Shipping。後果就是全部狀態的訪問與修改都增長了內網時延,所以對於遊戲場景服務這種性能優先的服務是不可忍受的。

  • 有狀態服務描述的是功能流,Function Shipping。數據具備強局部性,所以有狀態服務很是適合場景同步這種交互密集的情景,交互的延遲僅僅是進程內方法調用的開銷。

藉助無狀態服務,咱們在架構中就能夠逐步幹掉相似切場景管理這種單點進程。無狀態服務是自然高可用的——任意掛掉一個,仍然能持續提供服務。

整個服務端理論上應該具備總體持續提供服務的能力。也就是說,隨便掛掉一個節點,不須要停服。遊戲的場景服務掛掉一個節點,不會影響其餘任何服務,只是該場景中的玩家短時間內沒法進行場景相關操做了而已。

而咱們見過的大多數遊戲服務端架構,到處皆單點,徹底不能叫可用的架構。分佈式系統中最忌增長額外的、沒必要要的單點。判斷一整個遊戲服務端是否具備可用性很簡單,隨便kill掉一個節點,若是服務端仍然能持續提供服務,即便是部分client受到了影響,也能稱爲是可用的。

小說君第一次嘗試在MMO中應用無狀態服務的時候,陷入沉思:遊戲服務端的熱更新,正確姿式應該是腳本語言reload(虛擬機語言經過改虛擬機實現函數級別的替換),仍是web開發經常使用的進程重啓?

如今能放文章跳轉連接了,簡單列一下服務端系列文章的連接,以及後續的主題(按順序閱讀更佳):

從零手寫服務端框架

面向中間件的開發模式

如何快速搭建數據服務

面向微服務的服務端架構

以消息隊列爲中心的服務端架構

聊聊無狀態服務(本篇)

轉 http://www.tuicool.com/articles/reeMBbf

相關文章
相關標籤/搜索