session共享怎么做的(分布式如何实现session共享)?
session共享怎么做的(分布式如何實現session共享)??
問題描述:一個用戶在登錄成功以后會把用戶信息存儲在session當中,這時session所在服務器為server1,那
么用戶在 session 失效之前如果再次使用 app,那么可能會被路由到 server2,這時問題來了,server 沒有該用戶的
session,所以需要用戶重新登錄,這時的用戶體驗會非常不好,所以我們想如何實現多臺 server 之間共享 session,
讓用戶狀態得以保存。?
1、服務器實現的session復制或session共享,這類型的共享session是和服務器緊密相關的,比如webSphere
或JBOSS在搭建集群時候可以配置實現session復制或session共享,但是這種方式有一個致命的缺點,就是不好擴
展和移植,比如我們更換服務器,那么就要修改服務器配置。?
2、利用成熟的技術做session復制,比如12306使用的gemfire,比如常見的內存數據庫如redis或memorycache,
這類方案雖然比較普適,但是嚴重依賴于第三方,這樣當第三方服務器出現問題的時候,那么將是應用的災難。?
3、將 session維護在客戶端,很容易想到就是利用cookie,但是客戶端存在風險,數據不安全,而且可以存放的
數據量比較小,所以將session維護在客戶端還要對session中的信息加密。?
我們實現的方案可以說是第二種方案和第三種方案的合體,可以利用 gemfire 實現 session 復制共享,還可以將
session維護在redis中實現session共享,同時可以將session維護在客戶端的cookie中,但是前提是數據要加密。
這三種方式可以迅速切換,而不影響應用正常執行。我們在實踐中,首選 gemfire 或者 redis 作為 session 共享的載
體,一旦session不穩定出現問題的時候,可以緊急切換cookie維護session作為備用,不影響應用提供服務。?
這里主要講解 redis 和 cookie 方案,gemfire 比較復雜大家可以自行查看 gemfire 工作原理。利用 redis 做
session共享,首先需要與業務邏輯代碼解耦,不然session共享將沒有意義,其次支持動態切換到客戶端cookie模
式。redis的方案是,重寫服務器中的HttpSession和HttpServletRequest,首先實現HttpSession接口,重寫session
的所有方法,將session以hash值的方式存在redis中,一個session的key就是sessionID,setAtrribute重寫之
后就是更新 redis 中的數據,getAttribute 重寫之后就是獲取 redis 中的數據,等等需要將 HttpSession 的接口一一
實現。?
實現了HttpSesson,那么我們先將該session類叫做MySession(當然實踐中不是這么命名的),當MySession
出現之后問題才開始,怎么能在不影響業務邏輯代碼的情況下,還能讓原本的 request.getSession()獲取到的是
MySession,而不是服務器原生的session。這里,我決定重寫服務器的HttpServletRequet,這里先稱為MyRequest,
但是這可不是單純的重寫,我需要在原生的request基礎上重寫,于是我決定在filter中,實現request的偷梁換柱,
我的思路是這樣的,MyRequest的構建器,必須以request作為參數,于是我在filter中將服務器原生的request(也
有可能是框架封裝過的 request),當做參數 new 出來一個 MyRequest,并且 MyRequest 也實現了
HttpServletRequest接口,其實就是對原生request的一個增強,這里主要重寫了幾個request的方法,但是最重要
的是重寫了request.getSession(),寫到這里大家應該都明白為什么重寫這個方法了吧,當然是為了獲取MySession,
于是這樣就在filter中,偷偷的將原生的request換成MyRequest了,然后再將替換過的request傳入chan.doFilter(),
這樣 filter 時候的代碼都使用的是 MyRequest 了,同時對業務代碼是透明的,業務代碼獲取 session 的方法仍然是
request.getSession(),但其實獲取到的已經是MySession了,這樣對session的操作已經變成了對redis的操作。
這樣實現的好處有兩個,第一開發人員不需要對session共享做任何關注,session共享對用戶是透明的;第二,filter
是可配置的,通過filter的方式可以將session共享做成一項可插拔的功能,沒有任何侵入性。?
這個時候已經實現了一套可插拔的session共享的框架了,但是我們想到如果redis服務出了問題,這時我們該怎
么辦呢,于是我們延續redis 的想法,想到可以將 session 維護在客戶端內(加密的 cookie),當然實現方法還是一
樣的,我們重寫 HttpSession 接口,實現其所有方法,比如 setAttribute 就是寫入 cookie,getAttribute 就是讀取
cookie,我們可以將重寫的session 稱作 MySession2,這時怎么讓開發人員透明的獲取到 MySession2呢,實現方
法還是在filter內偷梁換柱,在MyRequest加一個判斷,讀取sessionType配置,如果sessionType是redis的,那
么getSession的時候獲取到的是MySession,如果sessionType是coolie的,那么getSession的時候獲取到的是
MySession2,以此類推,用同樣的方法就可以獲取到MySession 3,4,5,6等等。?
這樣兩種方式都有了,那么我們怎實現兩種 session 共享方式的快速切換呢,剛剛我提到一個 sessionType,這
是用來決定獲取到session的類型的,只要變換sessionType就能實現兩種session共享方式的切換,但是sessionType
必須對所有的服務器都是一致的,如果不一致那將會出現比較嚴重的問題,我們目前是將 sessionType 維護在環境變
量里,如果要切換 sessionType就要重啟每一臺服務器,完成 session共享的轉換,但是當服務器太多的時候將是一
種災難。而且重啟服務意味著服務的中斷,所以這樣的方式只適合服務器規模比較小,而且用戶量比較少的情況,當服
務器太多的時候,務必需要一種協調技術,能夠讓服務器能夠及時獲取切換的通知。基于這樣的原因,我們選用
zookeeper 作為配置平臺,每一臺服務器都會訂閱 zookeeper 上的配置,當我們切換 sessionType 之后,所有服務
器都會訂閱到修改之后的配置,那么切換就會立即生效,當然可能會有短暫的時間延遲,但這是可以接受的。?
文章來自www.wityx.com,轉載請注明出處!原文地址http://www.wityx.com/post/1321_1_1.html
總結
以上是生活随笔為你收集整理的session共享怎么做的(分布式如何实现session共享)?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP1.1与HTTP1.0的区别
- 下一篇: 谈谈分布式的场景及分布式事务的解决方案