使用Spring Session和Redis解決分布式Session跨域共享問題

前言

對(duì)于分布式使用Nginx+Tomcat實(shí)現(xiàn)負(fù)載均衡菲饼,最常用的均衡算法有IP_Hash吵护、輪訓(xùn)、根據(jù)權(quán)重达布、隨機(jī)等团甲。不管對(duì)于哪一種負(fù)載均衡算法,由于Nginx對(duì)不同的請(qǐng)求分發(fā)到某一個(gè)Tomcat黍聂,Tomcat在運(yùn)行的時(shí)候分別是不同的容器里躺苦,因此會(huì)出現(xiàn)session不同步或者丟失的問題。

實(shí)際上實(shí)現(xiàn)Session共享的方案很多产还,其中一種常用的就是使用Tomcat匹厘、Jetty等服務(wù)器提供的Session共享功能,將Session的內(nèi)容統(tǒng)一存儲(chǔ)在一個(gè)數(shù)據(jù)庫(kù)(如MySQL)或緩存(如Redis)中脐区。

在以前寫的一篇文章中:

使用Redis存儲(chǔ)Nginx+Tomcat負(fù)載均衡集群的Session:http://blog.csdn.net/xlgen157387/article/details/52024139

這一篇文章中已經(jīng)學(xué)習(xí)了一下集乔,如何使用 tomcat-redis-session-manager 開源項(xiàng)目解決分布式session跨域的問題,他的主要思想是利用Servlet容器提供的插件功能坡椒,自定義HttpSession的創(chuàng)建和管理策略扰路,并通過配置的方式替換掉默認(rèn)的策略。tomcat-redis-session-manager重寫了Tomcat的org.apache.catalina.session.ManagerBase里邊的具體寫的操作倔叼, 將tomcat的session存儲(chǔ)位置指向了Redis:

image.png

RedisSessionManager繼承了org.apache.catalina.session.ManagerBase并重寫了add汗唱、findSession、createEmptySession丈攒、remove等方法哩罪,并將對(duì)session的增刪改查操作指向了對(duì)Redis數(shù)據(jù)存儲(chǔ)的操作授霸。

有興趣可參考一篇Tomcat中session的管理機(jī)制:http://www.cnblogs.com/interdrp/p/4935614.html

不過使用過tomcat-redis-session-manager 的都應(yīng)該知道,配置相對(duì)還是有一點(diǎn)繁瑣的际插,需要人為的去修改Tomcat的配置碘耳,需要耦合Tomcat等Servlet容器的代碼,并且對(duì)于分布式Redis集群的管理并不是很好框弛,與之相對(duì)的個(gè)人認(rèn)為比較好的一個(gè)框架Spring Session可以真正對(duì)用戶透明的去管理分布式Session辛辨。

Spring Session不依賴于Servlet容器,而是Web應(yīng)用代碼層面的實(shí)現(xiàn)瑟枫,直接在已有項(xiàng)目基礎(chǔ)上加入spring Session框架來(lái)實(shí)現(xiàn)Session統(tǒng)一存儲(chǔ)在Redis中斗搞。如果你的Web應(yīng)用是基于Spring框架開發(fā)的,只需要對(duì)現(xiàn)有項(xiàng)目進(jìn)行少量配置慷妙,即可將一個(gè)單機(jī)版的Web應(yīng)用改為一個(gè)分布式應(yīng)用僻焚,由于不基于Servlet容器,所以可以隨意將項(xiàng)目移植到其他容器膝擂。

Spring Session使用

官方地址:http://projects.spring.io/spring-session/

官方文檔地址:http://docs.spring.io/spring-session/docs/1.3.0.RELEASE/reference/html5/

Spring Session提供了一套創(chuàng)建和管理Servlet HttpSession的方案虑啤。Spring Session提供了集群Session(Clustered Sessions)功能,默認(rèn)采用外置的Redis來(lái)存儲(chǔ)Session數(shù)據(jù)架馋,以此來(lái)解決Session共享的問題咐旧。

一、特性

Spring Session提供以下特性:

API和用于管理用戶會(huì)話的實(shí)現(xiàn)绩蜻;
HttpSession - 允許以應(yīng)用程序容器(即Tomcat)中性的方式替換HttpSession;
Clustered Sessions - Spring Session讓支持集群會(huì)話變得不那么繁瑣室埋,并且不和應(yīng)用程序容器金習(xí)性綁定到办绝。
Multiple Browser Sessions - Spring會(huì)話支持在單個(gè)瀏覽器實(shí)例中管理多個(gè)用戶的會(huì)話。
RESTful APIs - Spring Session允許在headers 中提供會(huì)話ID以使用RESTful API姚淆。
二孕蝉、基于XML配置方式的Spring Session案例實(shí)現(xiàn)

基于SSM框架的一個(gè)小案例,Git OS項(xiàng)目代碼地址:http://git.oschina.net/xuliugen/spring-session-demo

image.png

項(xiàng)目展示:

image.png

(1)基本環(huán)境需求

進(jìn)行使用Spring Session的話腌逢,首先的是已經(jīng)安裝好的有一個(gè) Redis服務(wù)器降淮!

(2)添加項(xiàng)目依賴(最基本的依賴使用)

<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
<version>1.3.0.RELEASE</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>biz.paluch.redis</groupId>
<artifactId>lettuce</artifactId>
<version>3.5.0.Final</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
(3)添加Spring配置文件

添加了必要的依賴之后,我們需要?jiǎng)?chuàng)建相應(yīng)的Spring配置搏讶。Spring配置是要?jiǎng)?chuàng)建一個(gè)Servlet過濾器佳鳖,它用Spring Session支持的HttpSession實(shí)現(xiàn)來(lái)替換容器本身HttpSession實(shí)現(xiàn)。這一步也是Spring Session的核心媒惕。

<context:annotation-config/>

<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>

<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>
1
2
3
4
5
上述代碼注釋:


image.png

LettuceConnectionFactory實(shí)例是配置Redis的ConnectionFactory系吩。

注意:

<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory"/>

查看源代碼可以看到,默認(rèn)的Redis鏈接配置為:


image.png

因此妒蔚,如果有自己的Redis配置穿挨,請(qǐng)修改月弛,例如下邊的配置:

<bean class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory">
<property name="hostName" value="192.168.1.149"/>
<property name="port" value="6379"/>
<property name="password" value="123456"/>
</bean>

(5)關(guān)于Error creating bean with name ‘enableRedisKeyspaceNotificationsInitializer’錯(cuò)誤的處理:

添加如下配置讓Spring Session不再執(zhí)行config命令

<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
1
如果不添加的話,會(huì)報(bào)如下錯(cuò)誤:

Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]:
Invocation of init method failed; nested exception is java.lang.IllegalStateException: Unable to configure Redis to keyspace notifications.
See http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent
Caused by: redis.clients.jedis.exceptions.JedisDataException: ERR unknown command config

(5)在web.xml中添加DelegatingFilterProxy

<filter>
<filter-name>springSessionRepositoryFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSessionRepositoryFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

DelegatingFilterProxy將通過springSessionRepositoryFilter的名稱查找Bean并將其轉(zhuǎn)換為過濾器科盛。對(duì)于調(diào)用DelegatingFilterProxy的每個(gè)請(qǐng)求帽衙,也將調(diào)用springSessionRepositoryFilter。

(6)Spring MVC controller代碼用于測(cè)試:

@Controller
@RequestMapping(value = "/spring/session", produces = {ConstString.APP_JSON_UTF_8})
public class SpringSessionDemoController {

@RequestMapping(value = "/setSession.do", method = RequestMethod.GET)
public void setSession(HttpServletRequest request, HttpServletResponse response) {
    String name = request.getParameter("name");
    String value = request.getParameter("value");
    request.getSession().setAttribute(name, value);
}

@RequestMapping(value = "/getSession.do", method = RequestMethod.GET)
public void getInterestPro(HttpServletRequest request, HttpServletResponse response) {
    String name = request.getParameter("name");
    System.out.println("------" + request.getSession().getAttribute(name));
}

@RequestMapping(value = "/removeSession.do", method = RequestMethod.GET)
public void removeSession(HttpServletRequest request, HttpServletResponse response) {
    String name = request.getParameter("name");
    request.getSession().removeAttribute(name);
}

}


image.png

(7)測(cè)試

訪問鏈接:http://localhost:8080/spring/session/setSession.do?name=xuiliugen&value=123456

使用工具查看Redis內(nèi)容:

image.png

可以發(fā)現(xiàn)已經(jīng)有值了贞绵!并且有expirations厉萝,可以看到箭頭指向的位置,是失效的時(shí)間記錄值但壮!

(8)到此冀泻,Spring Session的使用已經(jīng)完成!其他具體的細(xì)節(jié)請(qǐng)參考:http://git.oschina.net/xuliugen/spring-session-demo 項(xiàng)目源代碼蜡饵。

總結(jié)

對(duì)于分布式環(huán)境Session跨域共享的問題弹渔,不管是使用開源的框架還是使用自己開發(fā)的框架,都需要明白的一個(gè)問題是:在Tomcat容器中創(chuàng)建Session是一個(gè)很耗費(fèi)內(nèi)存的事情溯祸。因此肢专,我們?cè)谧约簩戭愃瓶蚣艿臅r(shí)候,我們一定要注意的是焦辅,并不是Tomcat為我們創(chuàng)建好了Session之后博杖,我們首先獲取Session然后再上傳到Redis等進(jìn)行存儲(chǔ),而是直接有我們自己創(chuàng)建Session筷登,這一點(diǎn)是至關(guān)重要的剃根!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市前方,隨后出現(xiàn)的幾起案子狈醉,更是在濱河造成了極大的恐慌,老刑警劉巖惠险,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苗傅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡班巩,警方通過查閱死者的電腦和手機(jī)渣慕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抱慌,“玉大人逊桦,你說我怎么就攤上這事∫纸” “怎么了卫袒?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)单匣。 經(jīng)常有香客問我夕凝,道長(zhǎng)宝穗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任码秉,我火速辦了婚禮逮矛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘转砖。我一直安慰自己须鼎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布府蔗。 她就那樣靜靜地躺著晋控,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姓赤。 梳的紋絲不亂的頭發(fā)上赡译,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音不铆,去河邊找鬼蝌焚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛誓斥,可吹牛的內(nèi)容都是我干的只洒。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼劳坑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼毕谴!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起距芬,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤涝开,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蔑穴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惧浴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年存和,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衷旅。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捐腿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柿顶,到底是詐尸還是另有隱情茄袖,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布嘁锯,位于F島的核電站宪祥,受9級(jí)特大地震影響聂薪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝗羊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一藏澳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耀找,春花似錦翔悠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至狞悲,卻和暖如春撮抓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背效诅。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工胀滚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乱投。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓咽笼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親戚炫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剑刑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容