利用Spring Session和redis對(duì)Session進(jìn)行共享詳解
前言
我們?cè)诖罱ㄍ昙涵h(huán)境后,不得不考慮的一個(gè)問題就是用戶訪問產(chǎn)生的session如何處理。
session的處理有很多種方法,詳情見轉(zhuǎn)載的上篇博客:集群/分布式環(huán)境下5種session處理策略
在這里我們討論其中的第三種方法:session共享。
redis集群做主從復(fù)制,利用redis數(shù)據(jù)庫的最終一致性,將session信息存入redis中。當(dāng)應(yīng)用服務(wù)器發(fā)現(xiàn)session不在本機(jī)內(nèi)存的時(shí)候,就去redis數(shù)據(jù)庫中查找,因?yàn)閞edis數(shù)據(jù)庫是獨(dú)立于應(yīng)用服務(wù)器的數(shù)據(jù)庫,所以可以做到session的共享和高可用。
不足:
1.redis需要內(nèi)存較大,否則會(huì)出現(xiàn)用戶session從Cache中被清除。
2.需要定期的刷新緩存
初步結(jié)構(gòu)如下:

但是這個(gè)結(jié)構(gòu)仍然存在問題,redis master是一個(gè)重要瓶頸,如果master崩潰的時(shí)候,但是redis不會(huì)主動(dòng)的進(jìn)行master切換,這時(shí)session服務(wù)中斷。
但是我們先做到這個(gè)結(jié)構(gòu),后面再進(jìn)行優(yōu)化修改。
Spring Boot提供了Spring Session來完成session共享。
官方文檔:http://docs.spring.io/spring-session/docs/current/reference/html5/guides/boot.html#boot-sample
首先創(chuàng)建簡單的Controller:
@Controller
public class UserController {
@RequestMapping(value="/main", method=RequestMethod.GET)
public String main(HttpServletRequest request) {
HttpSession session = request.getSession();
String sessionId = (String) session.getAttribute("sessionId");
if (null != sessionId) { // sessionId不為空
System.out.println("main sessionId:" + sessionId);
return "main";
} else { // sessionId為空
return "redirect:/login";
}
}
@RequestMapping(value="/login", method=RequestMethod.GET)
public String login() {
return "login";
}
@RequestMapping(value="/doLogin", method=RequestMethod.POST)
public String doLogin(HttpServletRequest request) {
System.out.println("I do real login here");
HttpSession session = request.getSession();
String sessionId = UUID.randomUUID().toString();
session.setAttribute("sessionId", sessionId);
System.out.println("login sessionId:" + sessionId);
return "redirect:/main";
}
}
簡單來說就是模擬一下權(quán)限控制,如果sessionId存在就訪問主頁,否則就跳轉(zhuǎn)到登錄頁面。
那么如何實(shí)現(xiàn)session共享呢?
加入以下依賴:
<!-- spring session --> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>1.3.0.RELEASE</version> </dependency> <!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
增加配置類:
@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
}
這個(gè)配置類有什么用呢?
官方文檔:
The Spring configuration is responsible for creating a Servlet Filter that replaces the HttpSession implementation with an implementation backed by Spring Session.
也就是說,這個(gè)配置類可以創(chuàng)建一個(gè)過濾器,這個(gè)過濾器支持Spring Session代替HttpSession發(fā)揮作用。
The @EnableRedisHttpSession annotation creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter. The filter is what is in charge of replacing the HttpSession implementation to be backed by Spring Session. In this instance Spring Session is backed by Redis.
@EnableRedisHttpSession注解會(huì)創(chuàng)建一個(gè)springSessionRepositoryFilter的bean對(duì)象去實(shí)現(xiàn)這個(gè)過濾器。過濾器負(fù)責(zé)代替HttpSession。
也就是說,HttpSession不再發(fā)揮作用,而是通過過濾器使用redis直接操作Session。
在application.properties中添加redis的配置:
spring.redis.host=localhost spring.redis.password= spring.redis.port=6379
這樣,就完成了Session共享了。是不是很簡單?業(yè)務(wù)代碼甚至不需要一點(diǎn)點(diǎn)的修改。
驗(yàn)證:

一開始redis數(shù)據(jù)庫是空的。
運(yùn)行項(xiàng)目:

訪問頁面之后,可以在redis中看到session的信息。
隨便登陸之后:

進(jìn)入到了main中。說明當(dāng)前這個(gè)session中是存在sessionId的。
我們查看當(dāng)前頁面的cookie。也就是說,這個(gè)cookie是存在sessionId的。

再運(yùn)行一個(gè)新的項(xiàng)目,端口為8081。在原本的瀏覽器中直接打開一個(gè)新的標(biāo)簽頁,我們知道,這個(gè)時(shí)候cookie是共享的。訪問localhost:8081/main

我們直接訪問新的項(xiàng)目成功了!!同一個(gè)cookie,可以做到session在不同web服務(wù)器中的共享。
最后再次強(qiáng)調(diào):
HttpSession的實(shí)現(xiàn)被Spring Session替換,操作HttpSession等同于操作redis中的數(shù)據(jù)。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
使用Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)
這篇文章主要介紹了使用Spring啟動(dòng)時(shí)運(yùn)行自定義業(yè)務(wù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java多線程實(shí)現(xiàn)同步鎖賣票實(shí)戰(zhàn)項(xiàng)目
本文主要介紹了java多線程實(shí)現(xiàn)同步鎖賣票實(shí)戰(zhàn)項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01
Java設(shè)計(jì)模式之依賴倒轉(zhuǎn)原則精解
設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用。設(shè)計(jì)模式是軟件開發(fā)人員在軟件開發(fā)過程中面臨的一般問題的解決方案。本篇介紹設(shè)計(jì)模式七大原則之一的依賴倒轉(zhuǎn)原則2022-02-02
詳解Java設(shè)計(jì)模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu)
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的Flyweight享元模式的開發(fā)結(jié)構(gòu),享元模式能夠最大限度地重用現(xiàn)有的同類對(duì)象,需要的朋友可以參考下2016-04-04
Spark調(diào)優(yōu)多線程并行處理任務(wù)實(shí)現(xiàn)方式
這篇文章主要介紹了Spark調(diào)優(yōu)多線程并行處理任務(wù)實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08
java實(shí)現(xiàn)動(dòng)態(tài)代理方法淺析
這篇文章主要介紹了java實(shí)現(xiàn)動(dòng)態(tài)代理方法淺析,很實(shí)用的功能,需要的朋友可以參考下2014-08-08
JavaBean和SpringBean的區(qū)別及創(chuàng)建SpringBean方式
這篇文章主要介紹了JavaBean和SpringBean的區(qū)別及創(chuàng)建SpringBean方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
基于MyBatis的關(guān)聯(lián)查詢優(yōu)化與應(yīng)用實(shí)踐
在實(shí)際項(xiàng)目開發(fā)中,關(guān)聯(lián)查詢是一種常見的需求,尤其是當(dāng)涉及到多個(gè)表之間的數(shù)據(jù)統(tǒng)計(jì)、關(guān)聯(lián)查詢以及嵌套對(duì)象的構(gòu)建時(shí),如何確保數(shù)據(jù)的準(zhǔn)確性和實(shí)時(shí)性,是開發(fā)者必須面對(duì)的挑戰(zhàn),本文將介紹基于MyBatis的關(guān)聯(lián)查詢優(yōu)化與應(yīng)用實(shí)踐,需要的朋友可以參考下2024-12-12

