1. 程式人生 > >分散式叢集Session共享 簡單多tomcat8+redis的session共享實現

分散式叢集Session共享 簡單多tomcat8+redis的session共享實現

什麼是Session/Cookie

        使用者使用網站的服務,基本上需要瀏覽器與Web伺服器的多次互動。HTTP協議本身是無狀態的,當用戶的第一次訪問請求結束後,後端伺服器就無法知道下一次來訪問的還是不是上次訪問的使用者。我們需要基於HTTP協議支援會話狀態的機制,這樣的機制可以使Web伺服器從多次單獨的HTTP請求中知道哪些請求是來自哪個會話的。        Session與Cookie的作用都是為了保持訪問使用者與後端伺服器的互動狀態。理解Cookie        Cookie的作用通俗的說就是當一個使用者通過HTTP協議訪問一個伺服器的時候,這個伺服器會將一些Key/Value鍵值對返回給客戶端瀏覽器,並給這些資料加上一些限制條件,在條件符合時這個使用者下次訪問這個伺服器的時候,資料又被完整地帶回給伺服器。理解Session
        Cookie可以讓服務端程式跟蹤每個客戶端的訪問,但是每次客戶端的訪問都必須傳回這些Cookie,如果Cookie很多,這無形地增加了客戶端與服務端的資料傳輸量,而Session的出現正是為了解決這個問題。        同一個客戶端每次和服務端互動時,不需要每次都傳回所有的Cookie值,而是隻要傳回一個會話標識(SessionId),這個ID是客戶端第一次訪問伺服器的時候生成的,而且每個客戶端是唯一的。這樣每個客戶端就有一個唯一的ID,客戶端只要傳回這個ID就行了,這個ID通常是NAME為JSESIONID的一個Cookie。在Web伺服器上,各個會話獨立儲存儲存不同會話的資訊。如果遇到禁用Cookie的情況,一般的做法就是把這個會話標識放到URL的引數中。總結
        Cookie和Session都是為了保持使用者訪問的連續狀態,之所以要保持這種狀態,一方面是為了方便業務實現,另一方面就是簡化服務端程式設計,提高訪問效能。但也帶來了一些問題,如安全問題、叢集環境下Session同步問題等

叢集遇到的問題

        當我們的應用伺服器變為多臺的時候就會遇到Session共享的問題。當我們第一次訪問網站的時候,負載均衡將本地的請求分配到Web伺服器1,那麼Session建立在Web伺服器1,第二次訪問的時候如果我們不做處理就不能保證還是會落到Web伺服器1了。

解決叢集Session共享問題

1. Session Sticky

        讓負載均衡器能夠根據每次的請求的會話標識來進行請求的轉發,這樣就能保證每次都能落到同一臺伺服器上面,這種方式稱為Session Sticky方式。如下圖:        
        存在問題:        1. 如果這一臺Web伺服器宕機或者重啟了,伺服器上的會話資料會丟失,使用者需要重新登陸等。        2. 會話標識是應用層的資訊,那麼負載均衡器要將同一個會話的請求都儲存到同一個Web伺服器上的話,就需要進行應用層的解析,這個開銷比第四層交換(LVS負載均衡器屬於第四層)要大。        3. 負載均衡器變為一個有狀態的節點,要將會話儲存到具體的Web伺服器的對映。和無狀態的節點相比,記憶體消耗會更大,容災方面會更麻煩。        打個比方,比如Web伺服器是飯店,會話資料是碗筷。要保證每次吃飯都用自己的碗筷的話,我就把餐具存在某一家飯店,並且每次都去這家店吃飯。

2. Session Replication

        還是以吃飯的例子,如果我們在每個飯店都存放一套自己的碗筷,就可以自己的選擇去哪家吃飯了。這就是Session Replication。如下圖:                此方案不用再要求負載均衡器保證同一個會話的多次請求必須到同一個Web伺服器上了。我們在Web伺服器之間增加了會話資料的同步,通過同步就保證了不同Web伺服器之間Session資料的一致。一般應用容器都支援Session Replication方式,與Session Sticky方案相比,Session Replication方式對負載均衡器沒有那麼多的要求。        存在問題:        1. 同步Session資料造成了網路頻寬的開銷。只要Session資料有變化,就需要將資料同步到所有其他機器上,機器越多,同步帶來的網路頻寬開銷就越大。        2. 每臺Web伺服器都要儲存所有Session資料,如果整個叢集的Session資料很多(很多人同時訪問網站)的話,每臺機器用於儲存Session資料的內容佔用會很嚴重。        這個方案是靠應用容器來完成Session的複製從而解決Session的問題的,應用本身並不關心這個事情。這個方案不適合叢集機器數多的場景。如果只有幾臺機器,用這個方案是可以的

3. Session資料集中儲存

        將Session資料集中儲存,然後不同Web伺服器從同樣的地方獲取Session,如下圖:                Session資料不儲存到本機而且存放到一個集中儲存的地方,修改Session也是發生在集中儲存的地方。Web伺服器使用Session從集中儲存的地方讀取。這樣保證了不同Web伺服器讀取到的Session資料都是一樣的。儲存Session的具體方式可以是資料庫、分散式儲存系統等。這個方案解決了Session Replication方案中記憶體的問題,對於網路頻寬也比Session Replication要好。        存在問題:        1. 讀寫Session資料引入了網路操作,這相對於本機的資料讀取來說,問題就在於存在時延和不穩定性,不過我們的通訊基本都是發生在內網,問題不大。        2. 如果集中儲存Session的機器或者叢集有問題,就會影響到我們的應用。        相對於Session Replication,當Web伺服器數量比較大、Session數比較多的時候,這個集中儲存方案的優勢是非常明顯的。

4. Cookie Based

        這個方案對於同一個會話的不同請求也是不限制具體處理機器的。它是通過Cookie來傳遞Session資料的,如下圖:                從上圖可以看出,我們的Session資料放到Cookie中,然後在Web伺服器上從Cookie中生成對應的Session資料。這就好比我們每次都把自己的碗筷帶在身上,這樣去那家飯店就可以隨意選擇了。相對前面的集中儲存方案,不會依賴外部的儲存系統,也就不存在從外部系統獲取、寫入Session資料的網路時延、不穩定性了。        存在問題:        1. Cookie長度的限制。我們知道Cookie是有長度限制的,而這也會限制Session資料的長度。        2. 安全性。Session資料本來都是服務端資料,而這個方案是讓這些服務端資料到了外部網路及客戶端,因此存在安全性上的問題。我們可以對寫入的Cookie的Session資料做加密,不過對於安全來說,物理上不能接觸才是安全的。        3. 頻寬消耗。指的是我們資料中心的整體外部頻寬的消耗。        4. 效能影響。每次HTTP請求和響應都帶有Session資料,對Web伺服器來說,在同樣的處理情況下,響應的結果輸出越少,支援的併發請求就越多。生成Session資料也會影響處理速度。        這4個方案都是可用的方案,但是對於大型網站來說,Session Stick和Session資料集中儲存是比較好的方案,這兩個方案各有優劣,需要在具體的場景中做出選擇和權衡。以下我們對第三種方案Session資料集中儲存進行簡單的例子介紹

簡單tomcat8+redis的session共享實現

1. 下載開源專案2. 建立maven專案        建立專案並把src/main/java/com/orangefunction/tomcat/redissessions/複製到專案3. 支援tomcat8需修改程式碼        RedisSessionManager.java        
@@ -713,9 +713,9 @@ private void initializeSerializer() throws ClassNotFoundException, IllegalAccess
serializer = (Serializer) Class.forName(serializationStrategyClass).newInstance();serializer = (Serializer) Class.forName(serializationStrategyClass).newInstance();
Loader loader =null;Loader loader =null;
-+Context context =this.getContext();
if (getContainer()!=null) {if (context!=null) {
- loader =getContainer().getLoader();+ loader =context.getLoader();
}}
ClassLoader classLoader =null;ClassLoader classLoader =null;
4. 打包部署    將實現包和依賴包commons-pool2-2.3.jar、jedis-2.7.2.jar、tomcat8_redis_session-0.0.1-SNAPSHOT.jar拷貝到tomcat的lib下    新增tomcat context.xml配置    <Valve className="com.demo.redis_session.RedisSessionHandlerValve" />      <Manager className="com.demo.redis_session.RedisSessionManager"          host="127.0.0.1"          port="6379"          database="0"          maxInactiveInterval="60" />    資料在專案的data目錄下5. 測試        在web專案啟動tomcat並使用如下程式碼進行測試
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

HttpSession session = request.getSession();
/*
// 1. 設定Session
for (int i = 0; i < 10; i++) {
session.setAttribute("name" + i, "session_data_" + i);
}
*/
// 2. 註釋第一步,重啟tomcat後看是否還可以讀取到Session
String str = "";
for (int i = 0; i < 10; i++) {
str = str + session.getAttribute("name" + i) + "<br>";
}

response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println(str);
out.flush();
out.close();
// 訪問: http://localhost:8080/tomcat8_redis_session_web/servlet/TestRedisSessionServlet
}
參考網址:        redis + Tomcat 8 的session共享解決 【http://www.cnblogs.com/interdrp/p/4868740.html 】        用Redis儲存Tomcat叢集的Session【http://blog.csdn.net/chszs/article/details/42610365  】        分散式集群系統下的高可用session解決方案【http://tendyming.iteye.com/blog/1815136 】        《大型網站系統與Java中介軟體實踐        《深入分析Java Web技術內幕》code: