前言
由於Http連接是無狀態的,所以使用Tomcat做服務器的時候Tomcat內部會維護一個叫做Session的東東用來保存客戶端的狀態,一般情況下每個客戶端都有一個cookie裏面保存着叫jsessionid的cookie,每次訪問tomcat的時候都會攜帶上,Tomcat可以根據這個jsessionid找到對應的session。就像你去超市買東西,門口的儲物櫃可以視作一個session容器,而打出的二維碼條就是cookie。
在分佈式系統中,對於同一個客戶端,訪問哪個Tomcat服務器就會在哪個Tomcat裏面創建session。簡單來說我做一個登錄功能,即第一次訪問Tomcat的時候需要輸入用戶名密碼,訪問成功後就在自己的sesison裏面寫入用戶名,那麼我下次訪問的時候直接檢測session裏是否有自己的用戶名來判斷自己是否處於登錄狀態了。現在問題來了,如果我第一次訪問的是TomcatA,登錄成功後由於nginx的負載均衡第二次訪問打到了TomcatB上,那麼TomcatB裏面並沒有我的用戶名信息,所以我還需要重新登錄。一個最直觀的想法就是把TomcatA和TomcatB的session抽出來放到某一個位置,這樣不管訪問TomcatA還是TomcatB都會從同一個Session裏面獲取用戶信息。
SpringBoot以一個非常簡潔易用的方式幫我們實現了分佈式Session,我們需要做的僅僅是1個註解,幾行配置,幾行代碼。
一、配置maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
</dependencies>
二、配置application.yml
spring:
session:
store-type: redis
timeout: 3600s
redis:
flush-mode: on_save
namespace: spring:session
redis:
host: 192.168.99.100
port: 6379
timeout: 5000ms
三、使用
//主類首先開啓EnableRedisHttpSession註解
@SpringBootApplication
@EnableRedisHttpSession
public class DistributeSessionApplication {
public static void main(String[] args) {
SpringApplication.run(DistributeSessionApplication.class, args);
}
}
//編寫controller,set用於向session添加屬性,get用於從session獲取屬性
package com.example.distributesession.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: 小混蛋
* @CreateDate: 2018/12/11 17:20
*/
@RestController
public class TestController {
@GetMapping("/set")
public void test(HttpServletRequest request){
request.getSession().setAttribute("message",request.getQueryString());
}
@GetMapping("/get")
public Map<String,Object> two(HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
map.put("sessionId",request.getSession().getId());
map.put("message",request.getSession().getAttribute("message"));
return map;
}
}
四、複製項目
複製項目,設置端口爲8081,
訪問8080端口的接口/set,添加queryStirng爲name='xxx'
image.png
然後訪問8081端口的接口/get,結果如下
結果
五、原理淺析
本質上利用Tomcat的Filter的實現類SpringSessionRepositoryFilter實現了對每一次請求的攔截,攔截之後把Session放到Redis裏面