程序员过关斩将--解决分布式session问题
微信搜一搜
架構(gòu)師修行之路
session
說到 session,我相信每個程序員都不陌生,或多或少在項目中使用過。session 這個詞,其實是一個抽象的概念,它不像 Cookie 那樣有著明確的定義。當大多數(shù)程序員談論 session 的時候,可能指的是服務端存儲數(shù)據(jù)的 session 對象,例如,用戶登錄成功之后把用戶信息存儲在 session 中,類似于這樣的程序
?Session["UserName"]?=?new?User();public?class?User{public?int?UserId?{get?;set?;}public?string?UserName?{get?;set;}}而在計算機中,尤其是網(wǎng)絡(luò)應用中,session 被定義為“會話”,可以把它看做客戶端和服務端的一條通道連接,同一個用戶的請求使用同一個 session 會話。在大多數(shù)應用中,主要用于用戶的識別,通俗來講,服務端可以通過 session 來記錄每一個用戶的狀態(tài)信息。那我們就以最常用的服務端 session 對象來啰嗦幾句
單機 session
session 是存儲在服務端的,這是一個很重要的概念。這意味著它需要占用服務器的內(nèi)存,并且它需要一種釋放的機制來保證服務器內(nèi)存不會被撐爆(例如 LRU)。
在項目初期,為了快速上線,服務器的部署很多情況下只有一臺服務器,記錄用戶的登錄狀態(tài)普遍使用 session 機制。請不要說這樣做不合理,至少在項目初期這種做法是最簡單而且最快速的方案。隨著項目的不斷迭代升級,用戶量的不斷增加,你會發(fā)現(xiàn)單機系統(tǒng)成為了項目的最大性能瓶頸,這個時候多數(shù)架構(gòu)師會選擇水平擴展方案。
其實說到底,系統(tǒng)性能的提升都圍繞著一個“分”字,無論是數(shù)據(jù)庫的分庫分表,還是現(xiàn)在興起的微服務,始終在圍繞著一個領(lǐng)域進行切分
當單機的 session 機制進行水平擴展就面臨著必須要要解決的問題:session 的親和性(粘性)要怎么樣去解決?
分布式 session
一個單機系統(tǒng)擴展為一個分布式系統(tǒng),就會面臨著分布式 CAP 理論中 AP 和 CP 的選擇,具體可以查看之前的文章:
晦澀難懂的 CAP,是否完全正確?
談到分布式 session 的一致性問題,其實主要是要解決用戶 session 的親和性,同一個用戶的請求怎么樣才能保證到達正確存儲 session 信息的服務器呢?
session 復制
最初的方案是采用 session 復制方案,整體的流程非常簡單:假設(shè)現(xiàn)在有三臺服務器,當一個 session 在其中一臺服務器上被創(chuàng)建,則同時把這個 session 復制到其他兩臺服務器上。這樣當用戶的請求無論到達哪臺服務器,都會有相應的 session 數(shù)據(jù)。
這種方案的優(yōu)勢在于服務器可以任意水平擴展,每個服務器都保留著所有的 session 信息,當加入一臺服務器只需要把所有的 session 信息復制過去即可。但是劣勢更加明顯
每個服務器上都保存著全部的 session 信息,服務器占用的資源大大增加。
session 同步需要占用網(wǎng)絡(luò)帶寬,最重要的是如果采用的異步復制方式,數(shù)據(jù)會有短暫性的不一致,可能會導致用戶訪問失敗。
session 復制的方案現(xiàn)在已經(jīng)很少有人使用了
負載均衡方案
當一臺服務器擴展為多臺服務器,目前最常用的方案是在流量的入口添加負載均衡器,大體的部署圖是這樣的
image如果負載均衡器能夠利用某種手段來實現(xiàn) session 的粘性就能實現(xiàn)分布式 session。目前主流的 nginx 可以根據(jù)“hash_ip”算法將同一個 IP 的請求固定到某臺服務器,這樣來自于同一個 ip 的 session 請求總是請求到同樣的服務器。
這種方式比 session 同步方式要好很多,每臺服務器只存儲對應的 session 數(shù)據(jù),這大大節(jié)省了內(nèi)存資源,而且服務器之間沒有數(shù)據(jù)同步過程。當有新服務器加入的時候,只需要修改負載均衡器的配置即可,這樣很方便就支持了服務器水平擴展。但是,同時也面臨著一些不足
服務器重啟意味著對應的 session 信息丟失,這在一些重要的業(yè)務場景中是不允許的
服務器的水平擴展需要修改負載均衡器的配置,修改之后可能會導致之前的 session 重新分布,這樣會導致一部分用戶路由不到正確的 session
session 剝離
現(xiàn)在應用更廣泛的分布式 session 技術(shù)是把 session 數(shù)據(jù)徹底從業(yè)務服務器中剝離,單獨存儲在其他外部設(shè)備中,而這些外部設(shè)備可以采用主備或者主從,甚至集群的模式來達到高可用。比如現(xiàn)在最常用的方案是把 session 數(shù)據(jù)存儲在 redis 中,雖然從 redis 讀寫 session 數(shù)據(jù)需要花費一定的網(wǎng)絡(luò)耗時,但是對于一般的應用來說在可以接受范圍之內(nèi)。
這種方案好處是整體架構(gòu)更加清晰,也更加靈活,應用的服務器整體擴展能力再也不用考慮 session 的影響,而 session 的問題被轉(zhuǎn)移到外部設(shè)備,通常可以利用內(nèi)存性 NOSql 來解決性能問題,而這些外部設(shè)備一般都會有對應的分布式集群方案,例如 redis,可以利用主從或者哨兵模式甚至集群來提供更大規(guī)模的數(shù)據(jù)支撐能力。
imageActor 模型
很少有人會提及 Actor 模型,我在之前的文章中介紹過 actor 模型,大家可以去看一下
分布式高并發(fā)下 Actor 模型如此優(yōu)秀
Actor 模型解決這種用戶粘性問題會更加優(yōu)雅,它天生就自帶了對象識別功能,簡單來說,同一個 key 的請求,總能到達正確的 actor 實例,這不是我們想要的結(jié)果嗎?而且 actor 模型下不用加鎖就能處理并發(fā)問題,為什么沒人用呢?而且采用 acotr 模型就可以利用進程內(nèi)緩存的形式,比請求局域網(wǎng) redis 的網(wǎng)絡(luò)延遲要低很多。
寫在最后
當然如果只是針對用戶登錄這個應用場景,session 方案并不是唯一的解決方案,可以參考菜菜之前的文章
程序員過關(guān)斬將--更加優(yōu)雅的 Token 認證方式 JWT
END
●程序員修神之路--為什么我會了SOA,你們還要逼我學微服務?
●程序員過關(guān)斬將--數(shù)據(jù)庫的樂觀鎖和悲觀鎖并非真實的鎖
●程序員修神之路--設(shè)計一套RPC框架并非易事
●程序員過關(guān)斬將--要想獲取我的用戶信息,就得按照規(guī)矩來
●程序員過關(guān)斬將--更加優(yōu)雅的Token認證方式JWT
●程序員過關(guān)斬將--cookie和session的關(guān)系其實很簡單
●程序員修神之路--用NOSql給高并發(fā)系統(tǒng)加速
●程序員修神之路--高并發(fā)系統(tǒng)設(shè)計負載均衡架構(gòu)
●程序員過關(guān)斬將--你為什么還在用存儲過程?
●程序員修神之路--問世間異步為何物?
●程序員修神之路--提高網(wǎng)站的吞吐
長按添加菜菜好友
關(guān)注后回復:“大禮包”和“福利”,領(lǐng)取驚喜
總結(jié)
以上是生活随笔為你收集整理的程序员过关斩将--解决分布式session问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET Core:跨平台和开源,让我在
- 下一篇: 中移动完成透镜天线远距覆盖和降本增效试点