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

通常情況下,Tomcat、Jetty等Servlet容器,會默認將Session保存在內存中。如果是單個服務器實例的應用,將Session保存在服務器內存中是一個非常好的方案。但是這種方案有一個缺點,就是不利于擴展。

目前越來越多的應用采用分布式部署,用于實現高可用性和負載均衡等。那么問題來了,如果將同一個應用部署在多個服務器上通過負載均衡對外提供訪問,如何實現Session共享?

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

本文主要介紹另一種實現Session共享的方案,不依賴于Servlet容器,而是Web應用代碼層面的實現,直接在已有項目基礎上加入Spring Session框架來實現Session統一存儲在Redis中。如果你的Web應用是基于Spring框架開發的,只需要對現有項目進行少量配置,即可將一個單機版的Web應用改為一個分布式應用,由于不基于Servlet容器,所以可以隨意將項目移植到其他容器。

Maven依賴

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

<!-- 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的過濾器要放在第一位。

<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配置文件

<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等。使用過程中會出現異常:

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命令:

在錯誤提示鏈接的文檔中,可以看到Redis需要以下的配置:

redis-cli config set notify-keyspace-events Egx

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

部分Redis云服務提供商可以在對應的管理后臺配置:

如果不能在后臺配置,可以通過工單聯系售后工程師幫忙配置,例如阿里云:

完成之后,還需要在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.

配置:

<?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>

 

來自:http://xxgblog.com/2016/09/29/spring-session-redis/

 

 本文由用戶 luoyin889 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!