前言
通常我們的session保存在服務器內存中,那麼當我們部署多臺服務器時,如何實現session共享?
傳統的單點登錄示例
- 新建一個springboot 項目,引入web-starter、lombok配置依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
- 添加模擬登錄登出的代碼
package com.summergao.sessiontest.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/sessiontest")
@Slf4j
public class SessionTestController {
/**
* 登錄
* @param request
* @param name
* @return
*/
@GetMapping("/login")
public String login(HttpServletRequest request, String name) {
log.info("登錄:{}", name);
HttpSession session = request.getSession();
session.setAttribute("name", name);
return name + " login success";
}
/**
* 登出
* @param request
* @return
*/
@GetMapping("/logout")
public String logout(HttpServletRequest request) {
HttpSession session = request.getSession();
String name= session.getAttribute("name") + "";
session.removeAttribute("name");
log.info("登出:{}", name);
return name + " logout success";
}
/**
* 獲取用戶名
* @param request
* @return
*/
@GetMapping("/getusername")
public String getUsername(HttpServletRequest request) {
HttpSession session = request.getSession();
return "hello," + session.getAttribute("name") + "";
}
}
3. 啓動服務器測試demo是否正常。可以看到登錄成功,並且能夠獲取到用戶名。
http://localhost:8081/sessiontest/login?name=SummerGao
http://localhost:8081/sessiontest/getusername
4. 模擬多服務器,此時在8082再啓動一臺服務器
-Dserver.port=8082
http://localhost:8082/sessiontest/getusername
可以發現8082這臺服務器並不能獲取到用戶名,說明session未在多服務器之間共享。server1登錄後,server2無法獲取到server1的登錄信息(session)。
如何解決多服務器session共享的問題?
我們只需要將session存儲到DB裏面,兩臺server共享DB,即可實現sesssion共享。
springboot提供了mongodb、redis、jdbc、hazelcast(一款分佈式緩存)等方案。
這裏我們選擇redis作爲session共享方案,redis作爲noSQL數據庫,其性能非常優秀。由於採用了外部的DB存儲,所以多節點的部署可以既可以在同一臺物理機器上,也可以在不同的物理機器上,沒有地理的限制。
那麼具體如何實現session共享呢?
由於選擇了redis的方案,所以需要先擁有一臺可用的redis服務器。如果已經有redis,可以忽略。
安裝方案:
安裝redis
- docker安裝redis的方案:
docker run -d --name redis -p 6379:6379 -d redis:latest
- windows安裝redis方案
一路next即可,沒啥難度。可參考: https://www.redis.com.cn/redis-installation.html
- 管理redis的可視化工具
RedisDesktopManager
一款桌面的redis管理工具,可以輔助用戶查看redis裏面的數據,對數據進行操作和管理。
session共享實踐
下面我們基於前面的代碼對項目進行修改,使其實現多服務器之間的session共享。
- 導入依賴
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 配置spring-session方案
spring:
# session配置
session:
timeout: 86400 # 設置session失效時間
store-type: redis # 修改spring-session存儲配置,默認存儲到服務器內存中,現在設置存到redis中(關鍵)
# redis配置
redis:
port: 6379 # redis的端口號(這裏是我的redis容器在docker中對應的端口號)
host: localhost # redis服務器ip
password: # 密碼(默認爲空)
database: 0 # 設置存入redis的哪一個庫(默認是0)
3. 兩個服務都啓動,在其中一臺服務器進行登錄測試,如果兩臺服務器都能獲取到username即視爲session實現了共享。
到此,基於redis的多服務器session共享成功。
登出
執行登出操作,可以看到兩個服務器都不能獲取username,則表明單點登出,多節點都會登出。
http://localhost:8081/sessiontest/logout