Spring Session + Redis實現分佈式Session共享

 發表於 2016-09-29

文章目錄
  1. 1. Maven依賴
  2. 2. 配置Filter
  3. 3. Spring配置文件
  4. 4. 解決Redis雲服務Unable to configure Redis to keyspace notifications異常

一般狀況下,Tomcat、Jetty等Servlet容器,會默認將Session保存在內存中。若是是單個服務器實例的應用,將Session保存在服務器內存中是一個很是好的方案。可是這種方案有一個缺點,就是不利於擴展。html

目前愈來愈多的應用採用分佈式部署,用於實現高可用性和負載均衡等。那麼問題來了,若是將同一個應用部署在多個服務器上經過負載均衡對外提供訪問,如何實現Session共享?html5

實際上實現Session共享的方案不少,其中一種經常使用的就是使用Tomcat、Jetty等服務器提供的Session共享功能,將Session的內容統一存儲在一個數據庫(如MySQL)或緩存(如Redis)中。我在之前的一篇博客中有介紹如何配置Jetty的Session存儲在MySQL或MongoDB中。java

本文主要介紹另外一種實現Session共享的方案,不依賴於Servlet容器,而是Web應用代碼層面的實現,直接在已有項目基礎上加入Spring Session框架來實現Session統一存儲在Redis中。若是你的Web應用是基於Spring框架開發的,只須要對現有項目進行少許配置,便可將一個單機版的Web應用改成一個分佈式應用,因爲不基於Servlet容器,因此能夠隨意將項目移植到其餘容器。web

Maven依賴

在項目中加入Spring Session的相關依賴包,包括Spring Data Redis、Jedis、Apache Commons Pool:redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- Jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.3.RELEASE</version>
</dependency>
<!-- Spring Session -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>
<!-- Apache Commons Pool -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>

 

配置Filter

在web.xml中加入如下過濾器,注意若是web.xml中有其餘過濾器,通常狀況下Spring Session的過濾器要放在第一位。spring

1
2
3
4
5
6
7
8
9
10
<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>

 

Spring配置文件

1
2
3
4
5
6
7
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost" />
<property name="password" value="your-password" />
<property name="port" value="6379" />
<property name="database" value="10" />
</bean>

只須要以上簡單的配置,至此爲止即已經完成Web應用Session統一存儲在Redis中,能夠說是及其簡單。數據庫

解決Redis雲服務Unable to configure Redis to keyspace notifications異常

若是是自建服務器搭建Redis服務,以上已經完成了Spring Session配置,這一節就不用看了。不過不少公司爲了穩定性、減小運維成本,會選擇使用Redis雲服務,例如阿里雲數據庫Redis版、騰訊雲存儲Redis等。使用過程當中會出現異常:apache

1
2
3
4
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

 

實際上這種異常發生的緣由是,不少Redis雲服務提供商考慮到安全因素,會禁用掉Redis的config命令:
禁用config命令禁用config命令
在錯誤提示連接的文檔中,能夠看到Redis須要如下的配置:
api

1
redis-cli config set notify-keyspace-events Egx

 

文檔地址:
http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent緩存

首先要想辦法給雲服務Redis加上這個配置。

部分Redis雲服務提供商能夠在對應的管理後臺配置:
配置notify-keyspace-events配置notify-keyspace-events
若是不能在後臺配置,能夠經過工單聯繫售後工程師幫忙配置,例如阿里雲:
阿里雲工單阿里雲工單

完成以後,還須要在Spring配置文件中加上一個配置,讓Spring Session再也不執行config命令:

However, in a secured Redis enviornment the config command is disabled. This means that Spring Session cannot configure Redis Keyspace events for you. To disable the automatic configuration add ConfigureRedisAction.NO_OP as a bean.

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">

<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="localhost" />
<property name="password" value="your-password" />
<property name="port" value="6379" />
<property name="database" value="10" />
</bean>

<!-- 讓Spring Session再也不執行config命令 -->
<util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>

</beans>
相關文章
相關標籤/搜索