本文內容較多,分為如下部分
無狀態(tài)服務和有狀態(tài)服務定義
無狀態(tài)服務應用場景
有狀態(tài)服務應用場景
有無狀態(tài)倆種服務的架構質量對比
實現有狀態(tài)服務的挑戰(zhàn)
有狀態(tài)服務重構成無狀態(tài)服務
警告:任何情況下都優(yōu)先考慮使用無狀態(tài)服務,即使你閱讀本文,包括以后寫的有狀態(tài)服務實現模式后。
高可用4大原則我自認為分別是 端到端[1],有狀態(tài)vs無狀態(tài)[2],可觀測,萬一,其他微服務框架,容器化我認為是次要的。
分布式系統里,節(jié)點服務包含了業(yè)務處理和數據倆部分, 如果數據是從持久化系統加載,則認為是此節(jié)點是狀態(tài)的。 如果數據常駐于節(jié)點內,業(yè)務邏輯是從外部加載進來的,則認為是有狀態(tài)節(jié)點。
![]()
state_definition
絕大多數系統都是無狀態(tài)服務,服務不需要在服務本地(JVM)記錄同一個客戶端(如用戶,設備,或者其他作為客戶端的服務等)請求的的歷史狀態(tài)或者記錄客戶端的信息,服務需要的這些數據將從數據庫或者Redis,MongDB加載。這些服務類似如下
登錄服務,服務節(jié)點根據請求用戶名,從數據庫查詢用戶名和HASH后的密碼,然后判斷請求的密碼是否與數據庫查詢的密碼一致
搜索服務 ,服務節(jié)點會把查詢條件轉為ElasticSearch查詢JSON,請求Elastic Search查詢結果。
電商中庫存查詢,服務節(jié)點會根據商品SKU以及查詢所在的地區(qū),查詢存放在Redis的庫存信息。同時庫存服務節(jié)點也會請求商品查詢服務查詢商品的名稱和價格等信息。
物聯網中查詢家庭設備的狀態(tài),服務節(jié)點會查詢倆個Redis,一個是緩存設備信息的Redis,一個設備在離線狀態(tài)的Redis
無狀態(tài)服務的技術實現非常成熟,如果你的服務是無狀態(tài),僅僅需要增加節(jié)點既可實現系統高可用和高性能。 如下圖,代理服務采用輪詢(Round Robin)或則隨機,對于客戶端C,每個服務節(jié)點都能提供服務。如果需要擴容,簡單增加服務節(jié)點4即可。
![]()
state_stateless
需要注意的是,隨著流量增加,而服務節(jié)點增加,導致代理服務和數據庫本身可能是高可用高性能的瓶頸。因此代理服務和數據庫節(jié)點也應該支持擴展節(jié)點
無狀態(tài)服務的主要優(yōu)點
優(yōu)點
實現簡單
像編寫CURD代碼那樣簡單
擴容簡單
增加節(jié)點即可,代理服務可以使用輪詢,隨機等路由技術實現。
與無狀態(tài)是服務對應的是有狀態(tài)服務,要求服務端記錄同一個客戶端的交互歷史數據和狀態(tài)數據,有狀態(tài)服務通常從外部加載執(zhí)邏輯而不是數據。比如
用戶前端流程表單在最終提交前保留在服務端的臨時表單數據
用戶登錄后的會話信息放在Session,Session信息存放在JMV內存里
物聯網的接入網關,需要記錄長鏈接到此網關的設備的版本信息。網關服務有可能加載規(guī)則腳本,規(guī)則引擎會根據設備上報的數據來執(zhí)行特定的業(yè)務邏輯
游戲服務,一群用戶會被路由到特定的地圖服務器上協作完成游戲
通過互聯網協作編寫一份文檔
導航系統,用戶的信息和旅程信息都在一個服務器上。實時導航信息也在這一臺服務器上更新
Zookeeper 內存中保持的有ZTree數據結構,它通過ZAB協議保持所有節(jié)點數據都一致。
Redis 本身也是有個有狀態(tài)服務節(jié)點,它支持在內存存儲各種數據結構以及加載執(zhí)行腳本。它通過從節(jié)點來實現高可用
有狀態(tài)需要客戶端始終連接到同一個服務節(jié)點(持久化連接),需要客戶端或者代理服務采取的路由策略保證同一個客戶任何請求都在同一個節(jié)點執(zhí)行,比如來自同一個IP的用戶
![]()
state_statefull.png
有狀態(tài)服務的主要優(yōu)點如下
優(yōu)點
高性能
主要避免了從數據庫加載數據的延遲; 也避免了從持久化系統反復加載數據,比如節(jié)點1加載數據執(zhí)行業(yè)務邏輯完畢后,當請求路由到節(jié)點2的時候,又會在節(jié)點2再次加載同樣數據
任意狀態(tài)數據結構
不再需要持久化系統保存狀態(tài)數據,其狀態(tài)不限制于JSON,表數據結構。
CAP
分布式常見的AP能得到滿足外,由于數據在同一個節(jié)點,其C也能滿足。需要注意,但是如果主節(jié)點宕機(或者客戶端認為C宕機路由到從節(jié)點),從節(jié)點接管,則C不成立。
避免使用性能不佳的數據庫
無狀態(tài)服務的擴容,也會導致其用的數據庫節(jié)點擴容。無狀態(tài)節(jié)點因為不需要數據庫,則省去了數據庫的使用成本,以及其導致的性能瓶頸
有狀態(tài)服務不難實現,但要達成高可用目標,則難得多。 下圖是一個通常有狀態(tài)服務高可用的部署架構。
![]()
state_statefull_slave
這個架構里,有非常多的技術挑戰(zhàn)性,列表如下。 這些挑戰(zhàn)是分布式系統高可用的深水區(qū),后面章節(jié)簡述如何攻克這些挑戰(zhàn)。
難點
舉例
持久連接
代理服務采用何種策略讓客戶端保持持久連接到同一個節(jié)點,包括客戶端的每次請求,客戶端重啟,服務節(jié)點重啟,以及擴容縮容后通知客戶端重連新節(jié)點。如果沒有代理服務,則需要客戶端實現持久化連接
設備總是鏈接到同一個網關,游戲玩家總是鏈接到同一個區(qū)域服務器
復制
為節(jié)點增加一個從節(jié)點,數據從主節(jié)點備份到從節(jié)點。當節(jié)點宕機,,從節(jié)點接替主節(jié)點工作。從節(jié)點也可收接收只讀服務請求,緩解主節(jié)點負載
Redis主從數據復制,Zk的主從數據復制
讀寫一致
如果從節(jié)點提供讀服務,如何保證客戶端讀到最新寫到此分區(qū)的數據。
阿里云Redis可以配置只有主分片提供服務以避免讀寫不一致。
分區(qū)
把有狀態(tài)服務分成多個節(jié)點,解決有狀態(tài)服務的存儲瓶頸和訪問性能。
物聯網網關集群,Redis數據分片,數據庫分區(qū),Kafka分區(qū)等
分區(qū)再平衡
在擴容時候,如果不差錢,每個分區(qū)都再次拆分2個分區(qū),此分區(qū)數據只需要復制到倆分區(qū)。這種擴容成本極高但數據均勻。如果擴容時候是增加少量節(jié)點。則因為持久化連接緣故,新增節(jié)點負載長期都較低。擴容效果不能立即顯現出來。
阿里云的Redis擴容采用的是成倍增長,支持4,8,16....2048個分片。不支持增加少量分區(qū)
分布式共識
數據備份給多個節(jié)點,當主節(jié)點掛掉,采用那個節(jié)點的數據為主節(jié)點。另外,新當選的的主節(jié)點需要繼續(xù)同步數據到從節(jié)點
ZK通過ZAB實現分布式共識
集群管理
需要感知整個集群的節(jié)點狀態(tài),其主從的工作狀態(tài)。代理服務需要獲取這些數據以實現路由
物聯網設備的主網關,Redis集群的代理
業(yè)務邏輯加載
如果有狀態(tài)服務的業(yè)務邏輯通過外部加載實現,如果管理這些業(yè)務邏輯
物聯網的規(guī)則鏈,通過配置其規(guī)則節(jié)點和執(zhí)行邏輯實現設備在離線,屬性上報等執(zhí)行邏輯。Reids作為有狀態(tài)節(jié)點,可以加載和執(zhí)行Lua腳本
基于無狀態(tài)服務和有狀態(tài)服務各自的優(yōu)缺點,下表總結了倆種服務的架構質量
架構特點
關注點
無狀態(tài)服務
有狀態(tài)服務
高可用
API網關
API網關可以實現各種負載均衡策略。如隨機,輪詢,負載
API網關實現較為復雜,需要識別客戶端,保證每次路由到同一個有狀態(tài)服務,依據信息可能是客戶端IP和端口,也可能是HTTP頭中包含的客戶ID
故障恢復
重啟服務即可恢復故障
有狀態(tài)服務重啟后,狀態(tài)丟失。需要重新建立這些狀態(tài),如設備的版本信息,用戶的購物車。因此故障恢復較慢這些狀態(tài)需要從持久化系統加載,或者依賴客戶端重置狀態(tài)。
故障容錯
無狀態(tài)服務支持重試,當服務宕機,API網關可以將請求路由到另外其他服務。不存在單機故障
有狀態(tài)服務宕機,狀態(tài)丟失。需要在其他節(jié)點重建狀態(tài),比如有狀態(tài)服務保持的有用戶登錄信息,當宕機后,用戶再訪問其他服務前,需要再次登錄。
高性能
啟動性能
無狀態(tài)服務不需要加載狀態(tài),因此重啟后訪問較快。
有狀態(tài)服務,服務重啟后,需要初始化端狀態(tài),訪問性能第一次較慢。重啟可能造成服務響應延遲
訪問性能
無狀態(tài)服務通常將狀態(tài)維護在Redis中,性能稍微慢一些,但考慮到Redis這些延遲都是毫秒級的,整體性能很高。如果狀態(tài)數據維護在傳統數據,則延遲較高
有狀態(tài)服務在JVM內存在保持狀態(tài),訪問速度更快。如游戲地圖,所有玩家都在一個游戲地圖(JVM)里有狀態(tài)服務適合游戲,導航等場景。
可修改
易于修改
通常無狀態(tài)服務,不需要在JVM里維護狀態(tài),實現更簡單,易于修改。
在JVM里維護的狀態(tài),數據結構私有,難以修改,另外涉及到并發(fā)訪問,編寫代碼容易出錯
熱發(fā)布
無狀態(tài)服務可以采用任何熱發(fā)布技術,而沒有風險
有狀態(tài)服務,需要把狀態(tài)數據遷移到熱發(fā)布后的結構,難度較大
可伸縮性
節(jié)點擴容
集群環(huán)境,無狀態(tài)服務可以任意增加或者減少,無狀態(tài)服務擴容后,API網關可以通過負載均衡策略,讓負載少的這些擴容節(jié)點能很快達到滿負荷
容易造成單點過載。有狀態(tài)服務擴容后,由于持久化鏈接需要,至少少量請求能立即路由到新的節(jié)點。擴容后需要較長時間新老節(jié)點的容量才能達到一個均衡值。
既然無狀態(tài)服務有如此多的優(yōu)點,除非有高性能要求,架構中應該優(yōu)先使用無狀態(tài)服務,如果是有狀態(tài)的服務,需要改成無狀態(tài)服務,這里有4個辦法
有狀態(tài)服務的狀態(tài)數據存放在Redis等更為可靠的存儲介質。比如用戶Session,訂單,購物車等信息存放到redis,一些長期存在的數據存放到數據庫。 一些高可用基礎設施的改進采用了此方案,比如Kafka新一代方案 Diskless Kafka [3] 的實現AutoMQ ,消息本地存儲變成存儲到對象存儲服務里。 Nacos將配置數據放到Mysql數據庫中。
服務端的狀態(tài)每次都回傳給客戶端,客戶端下次調用攜帶這些狀態(tài),比如JWT,Cookie
有狀態(tài)服務把有狀態(tài)部分單獨隔離出來,把其他部分放在無狀態(tài)服務里。
使用Zookeeper,數據庫等強一致工具來實現投票,元數據管理,二階段提交等,而無需自己實現
下圖是Spring Boot提供的Session實現方式,代替?zhèn)鹘y的保存會話到內存,Spring Boot 配置spring.session.store-type
spring.session.store-type=Redis配置后,存在內存中的的用戶會話數據將序列化后存放在redis中。
![]()
state_springsession
其他配置還允許使用數據庫,Hazelcast等 存儲系統。需要注意,必須確保存儲系統的高性能和高可用。在我的一個項目里,用此方案把有狀態(tài)服務改成無狀態(tài)服務,額外引入了一個512分片,容量是1024G的Redis集群以避免系統性能問題。
另外一種把有狀態(tài)服務改成無狀態(tài)服務的方法是服務器每次把狀態(tài)回傳給客戶端。適合用戶狀態(tài)數據較少情況。
![]()
state_request
交互過程如下
服務在接受客戶端請求后,將數據放回到Cookie中,如加密的用戶的信息,或者訂單信息
客戶端下次請求,服務端從Cookie中取出的狀態(tài)數據,處理請求后新的狀態(tài)數據再次保存到Cookie中
在還沒有無紙辦公時代,我在派出所辦理業(yè)務的時候,按照要求需要跑多次派出所和其他相關單位,派出所工作人員會把需要跑的單位記錄在一張紙上,每次交互后的結果和剩下需要辦的事情都有記錄,再次去派出所,其他工作人員即使沒有給我辦理過業(yè)務,也會讓我拿出這張紙查看,以了解我辦理的進度
第三種是在設計有狀態(tài)服務時候,拆分有狀態(tài)服務。這樣好處是讓大部分功能保持在無狀態(tài)服務里。
![]()
state_split
第四種方法與第三種類似,使用zookeeper來管理有狀態(tài),相比于自己實現有狀態(tài)服務,zookeeper/etcd這些基礎中間件更為可靠。
![]()
有狀態(tài)服務的高可用涉技術實現包括大量內容,本書將用后續(xù)一節(jié)內容說明實現有狀態(tài)服務的高可用有哪些模式,再次警告,有狀態(tài)服務高可用實現難度較大。類似你正在實現一個Redis,Kafka這樣的中間件。你需要承擔為了性能引入的復雜性。
參考資料
端到端: https://my.oschina.net/u/567839/blog/19641027
有狀態(tài)vs無狀態(tài): https://my.oschina.net/u/567839/blog/19658871
Diskless Kafka: https://www.toutiao.com/article/7580613850573636096/
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.