快速實現 Tomcat 叢集 Session 共享
前言
在應對巨大的使用者流量的網際網路場景中, 搭建 Tomcat 叢集是緩解 Web 伺服器負載的解決方式中必不可少的,而隨之帶來的會話資訊即 Session 不同步的問題也暴露出來: 使用者剛登入後,再次操作卻提示需要重新登入,嚴重影響著使用者體驗. 本文主要研究如何使用 Spring Session 框架來解決 Tomcat 叢集會話共享問題.若有補充,歡迎斧正.
正文
環境準備
- 3個 Tomcat 例項
- Redis
專案結構
專案比較簡單,除了啟動類之外,就只有一個控制器類.
控制器實現
UserController 主要有兩個請求方法, 一個接受使用者登入,另一個獲取登入資訊的;當呼叫 login
介面後將請求資料存在當前的 Session 中,然後在 Session 有效的期間內呼叫 getUserInfo
介面都能獲取到對應登入時的資料.
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/login")
public String login(HttpSession session, HttpServletRequest request) {
String id = request.getParameter("id");
String name = request.getParameter("name");
HashMap<Object, Object> userInfo = new HashMap<>(16);
userInfo.put("id", id);
userInfo.put("name", name);
session.setAttribute("USER_INFO", userInfo);
return userInfo + " 成功儲存到會話中";
}
@RequestMapping("/getUserInfo")
public String getUserInfo(HttpSession session, HttpServletRequest request) {
Object user_info = session.getAttribute("USER_INFO");
if (user_info == null) {
return "請先登入,再讀取會話資料";
}
return "從會話中讀取資料 " + user_info;
}
}
複製程式碼
現在我們將3個 Tomcat 例項搭建成叢集,然後都運轉這個專案; 如果我們針對一個 Tomcat 例項傳送登入請求,然後再次傳送獲取使用者資訊請求,此時這個 Tomcat 是能夠正確返回之前登入後儲存的資訊;而當我們在另一個 Tomcat 例項嘗試獲取使用者資訊時,則會返回 "請先登入,再讀取會話資料";這說明這兩個 Tomcat 例項的會話資訊是獨立存在的.
使用 Spring Session
現在想要讓這些 Tomcat 間能夠對會話資訊共享,只要登入一次,就可以在其他叢集例項上訪問資料,就可以使用 Spring Session 框架實現,它能在對程式無任何侵入的情況 實現 Session 的共享. 首先我們要做 POM 檔案引入 Spring Session 相關的庫
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
複製程式碼
從依賴的庫可以看到 Spring Session 利用記憶體資料庫 Redis 來儲存會話資訊,以此達到叢集間會話的共享.
引入後依賴庫之後,我們就需要在 application.properties 檔案上進行 Session 的配置.
server.servlet.session.timeout=3600 //1
spring.session.redis.flush-mode=IMMEDIATE //2
spring.session.redis.namespace=spring:session //3
// 4
spring.redis.host=127.0.0.1
spring.redis.password=
spring.redis.port=6380
複製程式碼
先簡單對檔案新增的配置進行簡單的說明:
- 限定 Session 超時時間,預設單位為秒
- 設定 Session 重新整理模式,有 ON_SAVE 和 IMMEDIATE; IMMEDIATE 表示立即寫到 Redis 中;而 ON_SAVE 表示只有當執行
SessionRepository.save(org.springframework.session.Session)
時才會寫入到 Redis. - 存放到 Redis 中會話的名稱空間.
- 連線 Redis 資料庫
然後在將專案打包到各個 Tomcat 之後再次呼叫登入請求,然後在 Redis 中查詢下當前所有 KEYS
從圖裡就可以看出快取中對 Session 資料的命名就是以前配置檔案中的名稱空間來的,我們取一下里面的 KEY 檢視它的內容,裡面就有我們所存的使用者資訊然後我們再對另個 Tomcat 請求獲取使用者資訊,就可以發現返回結果不再是之前的"請先登入,再讀取會話資料",而能正常返回在之前一臺 Tomcat 例項上登入的會話資料資訊.這也說明了 Tomcat 叢集間的會話共享實現了, 是不是很簡單呢?