HttpSession是通過Servlet容器創(chuàng)建和管理的责球,像Tomcat/Jetty都是保存在內(nèi)存中的吟策。而如果我們把web服務(wù)器搭建成分布式的集群纬凤,然后利用LVS或Nginx做負(fù)載均衡渔肩,那么來自同一用戶的Http請求將有可能被分發(fā)到兩個不同的web站點(diǎn)中去。那么問題就來了,如何保證不同的web站點(diǎn)能夠共享同一份session數(shù)據(jù)呢惠勒?
最簡單的想法就是把session數(shù)據(jù)保存到內(nèi)存以外的一個統(tǒng)一的地方秕衙,例如Memcached/Redis等數(shù)據(jù)庫中。
那么問題又來了掷匠,如何替換掉Servlet容器創(chuàng)建和管理HttpSession的實(shí)現(xiàn)呢滥崩?
設(shè)計一個Filter,利用HttpServletRequestWrapper讹语,實(shí)現(xiàn)自己的 getSession()方法钙皮,接管創(chuàng)建和管理Session數(shù)據(jù)的工作。spring-session就是通過這樣的思路實(shí)現(xiàn)的顽决。
使用Spring Session和Redis的組合來代替原有的HttpSession實(shí)現(xiàn)Session在不同項(xiàng)目之間的共享
在springboot中集成Spring Session
引入spring session相關(guān)依賴
<!-- spring session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
在啟動類中配置SpringSession
@SpringBootApplication
//spring在多長時間后強(qiáng)制使redis中的session失效,默認(rèn)是1800.(單位/秒)
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置類中最關(guān)鍵的就是 @EnableRedisHttpSession
@EnableRedisHttpSession
注解創(chuàng)建了一個名為 springSessionRepositoryFilter
的bean
短条,負(fù)責(zé)替換 httpSession
,同時由 redis
提供緩存支持
為了做到全部替換,我們要確保Servlet容器(Tomcat)對于某個請求都使用這個Filter,這個由SpringBoot負(fù)責(zé)
(具體是這樣的:@EnableRedisHttpSession
注解通過Import
才菠,引入了RedisHttpSessionConfiguration
配置類茸时。該配置類通過@Bean
注解,向Spring容器中注冊了一個SessionRepositoryFilter
(SessionRepositoryFilter的依賴關(guān)系:SessionRepositoryFilter --> SessionRepository --> RedisTemplate --> RedisConnectionFactory赋访,有興趣可以查看源碼)
maxInactiveIntervalInSeconds
:設(shè)置Session
失效時間,使用Redis Session
之后可都,原springboot
的server.session.timeout
屬性不再生效
添加驗(yàn)證的接口
在yml
或者properties
文件中可以通過server.port設(shè)置端口
@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", "服務(wù)端口號為 "+port);
return map;
}
可能遇到的問題
錯誤:java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.getConfig(Ljava/lang/String;)Ljava/util/List;
原因:出現(xiàn)這樣的錯誤應(yīng)該是引用了spring-session-data-redis
1.x版本問題,改用為2.x版本即可
解決方法缓待,引用2.1.2.RELEASE
版本
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
啟動測試
訪問 http://localhost:8080/session
我們看下redis緩存的數(shù)據(jù)
可以發(fā)現(xiàn)
sessionId
已經(jīng)緩存在redis數(shù)據(jù)庫中下面我們換個端口再訪問一次看看
這次我把端口換成了8888 訪問:http://localhost:8888/session
刷新了redis數(shù)據(jù)庫,緩存的數(shù)據(jù)也沒變
結(jié)果中的SessionId是一致的汹粤,卻是由兩個不同項(xiàng)目工程來提供服務(wù)命斧。這樣子,SpringSession 利用攔截器 Filter 幫我們在每個請求前進(jìn)行了同步設(shè)置嘱兼,達(dá)到了分布式系統(tǒng)中 session 共享国葬。