HttpSession是通過Servlet容器建立和管理的,像Tomcat/Jetty都是儲存在記憶體中的。而如果我們把web伺服器搭建成分布式的叢集,然後利用LVS或Nginx做負載均衡,那麼來自同一使用者的Http請求将有可能被分發到兩個不同的web站點中去。那麼問題就來了,如何保證不同的web站點能夠共享同一份session資料呢?
最簡單的想法就是把session資料儲存到記憶體以外的一個統一的地方,例如Memcached/Redis等資料庫中。
那麼問題又來了,如何替換掉Servlet容器建立和管理HttpSession的實作呢?
設計一個Filter,利用HttpServletRequestWrapper,實作自己的 getSession()方法,接管建立和管理Session資料的工作。spring-session就是通過這樣的思路實作的。
使用Spring Session和Redis的組合來代替原有的HttpSession實作Session在不同項目之間的共享
在springboot中內建Spring Session
引入spring session相關依賴
<!-- spring session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
添加 SpringSession 配置類
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class HttpSessionConfig {
//不需要寫任何代碼
}
配置類中最關鍵的就是
@EnableRedisHttpSession
@EnableRedisHttpSession
注解建立了一個名為
springSessionRepositoryFilter
的
bean
,負責替換
httpSession
,同時由
redis
提供緩存支援
為了做到全部替換,我們要確定Servlet容器(Tomcat)對于某個請求都使用這個Filter,這個由SpringBoot負責
(具體是這樣的:
@EnableRedisHttpSession
注解通過
Import
,引入了
RedisHttpSessionConfiguration
配置類。該配置類通過
@Bean
注解,向Spring容器中注冊了一個
SessionRepositoryFilter
(SessionRepositoryFilter的依賴關系:SessionRepositoryFilter --> SessionRepository --> RedisTemplate --> RedisConnectionFactory,有興趣可以檢視源碼)
maxInactiveIntervalInSeconds
:設定
Session
失效時間,使用
Redis Session
之後,原
springboot
server.session.timeout
屬性不再生效
添加驗證的接口
在
yml
或者
properties
檔案中可以通過server.port設定端口
@Value("${server.port}")
String port;
@GetMapping("/session")
public Object getSession(HttpServletRequest request){
Map<String, Object> map = new HashMap<String, Object>();
map.put("SessionId", request.getSession().getId());
map.put("ServerPort", "服務端口号為 "+port);
return map;
}
通路
http://localhost:8080/sessionPaste_Image.png
我們看下redis緩存的資料
可以發現
sessionId
已經緩存在redis資料庫中
下面我們換個端口再通路一次看看
這次我把端口換成了8888 通路:
http://localhost:8888/session重新整理了redis資料庫,緩存的資料也沒變
結果中的SessionId是一緻的,卻是由兩個不同項目工程來提供服務。這樣子,SpringSession 利用攔截器 Filter 幫我們在每個請求前進行了同步設定,達到了分布式系統中 session 共享。