在傳統的單服務架構中,只有一個服務器,那就不會存在session共享的問題,但如果在分佈式/集羣項目中,session共享則是一個必須面對的問題。
這樣就會出現一個問題,比如說,當客戶端發起了一個請求,這個請求到達Nginx之後,被轉發到了服務器A,然後在服務器A上往session保存了一份數據,下次又來一個請求,這個請求被轉發到 Tomcat B 上,此時再去 Session 中獲取數據,發現沒有之前的數據。對於這一類問題的解決,思路很簡單,就是將各個服務之間需要共享的數據,保存到一個公共的地方(主流方案就是 Redis):
當所有 Tomcat 需要往 Session 中寫數據時,都往 Redis 中寫,當所有 Tomcat 需要讀數據時,都從 Redis 中讀。這樣,不同的服務就可以使用相同的 Session 數據了。
這樣的方案,可以由開發者手動實現,即手動往 Redis 中存儲數據,手動從 Redis 中讀取數據,相當於使用一些 Redis 客戶端工具來實現這樣的功能,毫無疑問,手動實現工作量還是蠻大的。
一個簡化的方案就是使用 Spring Session 來實現這一功能,Spring Session 就是使用 Spring 中的代理過濾器,將所有的 Session 操作攔截下來,自動的將數據 同步到 Redis 中,或者自動的從 Redis 中讀取數據。
對於開發者來說,所有關於 Session 同步的操作都是透明的,開發者使用 Spring Session,一旦配置完成後,具體的用法就像使用一個普通的 Session 一樣。
演示
創建一個springboot項目
依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--用於連接redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--用於使用redis接管session-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--工具類,非必需-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.0</version>
</dependency>
application.yml
server:
port: 8088
spring:
redis:
host: localhost
port: 6379
IndexController
@RestController
@RequestMapping(value = "/")
public class IndexController {
@Value("${server.port}")
private String port;
private String username = "wangxianlin";
private String password = "123456";
private String nickname = "Halo";
@RequestMapping(value = "/login")
public Map<String, Object> getSession(HttpServletRequest request,@RequestParam("user") String user,@RequestParam("pwd") String pwd) {
Map<String, Object> map = new HashMap<>();
if (username.equals(user) && password.equals(pwd)){
request.getSession().setAttribute("user", "當前登錄用戶是:"+nickname);
map.put("msg", "登錄成功");
map.put("port",port);
}else {
map.put("msg", "賬號或密碼錯誤");
}
return map;
}
@RequestMapping(value = "/get")
public Map<String, Object> get(HttpServletRequest request) {
Map<String, Object> map = new HashMap<>();
String user = (String) request.getSession().getAttribute("user");
map.put("msg", StrUtil.isEmpty(user)?"用戶未登錄":user);
map.put("port",port);
return map;
}
}
在啓動類上加上註解
@EnableRedisHttpSession
將項目打成jar包
啓動項目
前提:redis 是否已經開啓?
將jar 分別以 8088和8089端口進行啓動
java -jar --server.port=8088
java -jar --server.port=8089
配置nginx
upstream web-server {
server localhost:8088;
server localhost:8089;
}
server {
listen 9000;
server_name localhost;
location / {
proxy_pass http://web-server;
}
}
啓動nginx
由於我這裏有三個nginx配置文件,然後我想啓動nginx-origanal.conf 咋辦呢?
命令 人家早就給我們安排好了,我們只需要這樣。
測試
未登錄狀態下
http://localhost:9000/get
登錄:
http://localhost:9000/login?user=wangxianlin&pwd=123456
http://localhost:9000/get