Spring-Session 会话共享 -> 基于 Redis 集群,内附各大错误合集,包括配置,类寻找不到、连接错误等

⚠ 本文仅限于博客园阅读,不得转载

如需转载,请与博主联系,否则视为侵犯著作权

本文不会删除,会长期保留着,因此不必担心哪一天会找不到。

PS:此文技术点在两年内有效,如果采用更高级的框架,Jar 包,则此文仅作参考

 

以下为正文:

 

首先需要简单介绍一下 Session ,它是我们即将需要进行配置的基础对象,如果你已经认识了此对象,那么可滑动至示例阶段。

在各类应用程序,都会产生一个叫做 会话 的连接,而在Web项目中,它称之为 Session,此文以 Tomcat 9.0 为基础进行演示。

Session 的作用是为了记录用户的操作,状态以及相关数据;而通常的情况下,则是由容器(Tomcat或其它服务器)进行管理:如销毁,创建等操作。

但是这存在一个缺陷:如果网站过大,需要多个服务器来支持网站的运行:用于支撑起数十万之巨的用户数量;那么单靠一个 Tomcat 是无法完成这个任务的,这个时候,只能通过 集群 来解决这个问题(负载均衡),而多几台机器就意味着需要有多个Tomcat来支持,那么这些 Tomcat 的会话可以共享吗?

答案是不能。每个容器都管理着各自的 Session。

需要解决的问题:例如某个用户在服务器A进行登录,其域名为 www.xxx.com/index,此时由于某个需求,需要跳转至另外的项目:www.xxx.com/container 中,假设二者是由不同的机器上的容器运行的,它意味着两个项目无法兼容,对于 container 项目来说,用户是没有登录的,则会产生一个新的会话。

 

问题的解决很简单,那便是 Session 会话共享:

 

就目前而言,已经有了数套方案可以实现。

  • 使用容器插件,或者扩展来实现。
    •   Tomcat插件如 tomcat-redis-session-manager,当然还有其它(我没有进行实践,不过缺陷亦有:容器升级就需要重新配置)
  • 自己编写一套 Session 会话管理类,需要读取用户会话时,在此类获取,而此类可以将其存放在 Redis 以保持Session状态,但开发周期可能会延长。
  • (推荐)使用框架的会话管理,本文主要介绍的也是此类:即 Spring Session ,方案因为不依赖容器(Tomcat)亦不需要改动大量原有代码(低侵入性),在目前为止,都是各方面表现较好的 Session 会话共享解决方案。

 

至于 Spring Session 介绍,这篇博客就不占据过多篇幅来介绍:可以转到官网中查看。现在已经有了中文官网;不过官方的错误解决,以及依赖都没有太多的解释,导致网上博客有着各类错误解决方案....还都不尽相同,都不能够解决我的配置问题。

Spring Session 中文官网 -> 基于 1.3.4 版本(博主使用 1.3.1 版本)

例如:https://www.docs4dev.com/docs/zh/spring-session/1.3.4.RELEASE/reference/all.html#httpsession-redis-jc

写这篇文章之前,我是从零开始学习 Redis 集群会话共享技术,已经学习了四天,大致问题都已经解决了,而网上资料难以阅读,所以我决定发在博客上供大家参考。

 


 

 Spring Session 示例:

    大致分为四步,全部按照示例完成,就可以实现会话共享了 - > 这仅需改动一点原有代码。

1.导入 Jar 包,其中包括各项依赖性库,这些 Jar 包缺一不可

    如果你是基于 Maven 进行版本管理的话,那么请自行根据以下 Jar 包添加依赖。

    如果你仅是基于SSM,没有版本管理的话,请将以下 Jar 包载入你的 lib 目录下

  •   在 Tomcat 中,此目录通常是:WEB-INF/lib

    Jar 包依赖如下:

  1.   commons-pool-1.6.jar
  2.   commons-pool2-2.4.2.jar
  3.   jedis-2.9.0.jar
  4.   spring-data-commons-1.9.1.RELEASE.jar
  5.   spring-data-redis-1.8.8.RELEASE.jar
  6.   spring-session-1.3.1.RELEASE.jar
  7.   spring-session-data-redis-1.3.2.RELEASE.jar

    值得注意的是:版本号一定要对上,否则依赖上可能会出现错误。

 

2.  将 Jar 包环境准备就绪后,在 web.xml 文件中配置 SpringSessionRepositoryFilter 过滤器

<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>
</filter-mapping>

 

    【重要】请将此过滤器放置在其它过滤器之前。

 

 

3. 在 Spring 配置文件中配置一个 RedisHttpSessionConfiguration 类(此处默认项目已经可以正常访问并使用)

 

<!-- spring注解、bean的处理器 -->
<context:annotation-config/> 
 
<!-- Spring session 的配置类 -->
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
    <!-- 指定session策略 -->
    <!--<property name="httpSessionStrategy" ref="httpSessionStrategy"/>-->
</bean>
 
<bean id="httpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy">
    <!-- 使用cookie策略 -->
    <property name="cookieSerializer" ref="cookieSerializer"/>
</bean>
 
<bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
    <!-- 指定cookie的域名及路径:此处配置值得注意,可能将引发异常(domainName)-->
    <property name="cookiePath" value="/"/>
    <property name="domainName" value="localhost"/>
</bean>

 

 

4.写入 Redis 连接配置 -> Spring redis 配置

    PS:如果可以的话,建议采用属性配置文件形式来配置连接数据 -> redis.properties

    但我急于测试,因此没有写属性配置文件,不过我在下方已顺便贴出配置文件

 

<!--读取redis.properties属性配置文件-->
    <context:property-placeholder location="classpath:redis.properties"/>
    
    <!-- 使用Spring配置JedisPoolConfig对象 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大空闲数 -->
            <property name="maxIdle" value="300" />
        <!-- 最大连接数 -->
        <property name="maxTotal" value="100" />
        <!-- 最大等待时间 -->
        <property name="maxWaitMillis" value="20000" />
    </bean>
    
    <!-- 配置jedis连接工厂,用于连接redis -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1"/>
        <property name="port" value="6379"/>
        <property name="password" value="cloud2650"/>
        <property name="usePool" value="true"/>
        <property name="timeout" value="10000"/>
    </bean>

 

如果你没有采用硬编码的形式来导入配置,而是属性配置文件,那么请在 value 中写入:

value="${属性配置名称}"

属性配置文件如下:

 

redis.hostName=127.0.0.1
redis.port=6379
redis.password=123265
redis.usePool=true
redis.timeout=10000

 

 

PS:【重要】网上有更全的配置,请根据实际情况而定。

 


 

完成了四大步骤,接下来就可以进入测试阶段了。

    以下是两种域名情况:

    www.xxx.com/项目一

    www.xxx.com/项目二

    值得注意的是:Spring Session 不支持根域名的会话共享,如:

    项目一.xxx.com

    项目二.xxx.com

 

    好了,在实际开发中,你可能会遇到诸多问题,我猜你的项目肯定报错。

    接下来我总结了九大错误,它们全都出现在我的项目里。。

    它们对我厚爱有加,不过我觉得各自不同,可能有些项目没有报错呢。

 


 

错误类型,共如下(目前遇到):

  • NoClassDefFoundError : 类找不到错误
  • Jar 包 -> 通常是 commons-pool 包互相冲突
    • 此错误会引发 NoClassDefFoundError
  • redis 配置错误
  • jedis 配置错误
  • 读取超时:Read timed out
  • enableRedisKeyspaceNotificationsInitializer 集群通知出错
  • Cookie 错误
  • 实例化 POJO(实体类)对象出错
  • Http status 500 reids denied 拒绝服务

 

    我们一个个来解决,请根据实际错误滑动以浏览。    

 

类找不到错误通常是 jar 包的缺失而导致的,请转至示例中查看是否缺失某个 Jar 包

    大致导入示例中的 Jar 包便会进入下一个问题: Jar 包冲突,这是因为 commons-pool两个版本不兼容的缘故。

    我的Spring版本是 4.3.21,tomcat容器是 9.0.07
    此时,应该改动以下两种配置:
 (如是手动打包则跳过此步骤,仅适合IDE)

 

 

 

 

 【重要】选中第二条:将应用发布到容器目录内,而不是IDE生成的副本容器

 

    之后修改 Tomcat 配置文件,此时你应该明确知道程序是在哪个容器运行的,可能是副本,不过你选中第二个,则转到其路径下,并寻找 ->

  apache-tomcat-9.0.17\conf\context.xml

    新增一行配置代码如下:

<valve className="com.radiadesign.catalina.session.RedisSessionHandlerValve"/>
<manager className="com.radiadesign.catalina.session.RedisSessionManager" database="0" host="127.0.0.1" maxInactiveInterval="60" port="6379"/>

    host 地址和其余子配置视实际情况而定

    注意:时刻多看我的环境以及配置,指不定哪天官方更新,Tomcat 配置失效依旧会出现错误(以及Jar冲突出现的各种ClassNotFoundException):
java.lang.ClassNotFoundException: com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve

    因为技术总是不断迭代,今年(2019)的Spring5版本我无法升级,总会出现错误,所以我的网站还没有使用最新的框架。

 

首要的两条错误解决后,即:类找不到和 Jar 包冲突错误,如果仍然出错,请继续往下阅读,我觉得应该是 redis 配置文件问题

 

    此时的错误跟以下关键字有关:
  enableRedisKeyspaceNotificationsInitializer


    打开redis的conf配置文件中配置(redis.windows.conf):

    按下快捷键来找到这一行配置:

notify-keyspace-events ""

    将其修改为:

 

notify-keyspace-events Egx

 

【重要】此错误是集群通知错误,打开会占用一点CPU和内存容量。

所以,官方将其默认关闭了。

 

另外的配置错误:客户端携带了密码进行访问,导致无法连接。

    错误代码为:

  ERR Client sent AUTH, but no password is set

    此时的错误,原因有很多种,我都总结下来了,如下:

    【重要】请依次检查:

  • 是否启动了 redis
  • 是否配置文件中有空格或特殊符号
  • 是否 redis 的配置端口有误
  • 密码是否错误
  • 连接池无连接对象可供使用
  • 如是外网,配置 redis 的机器上是否有防火墙限制?

    唔。。以上觉得没啥问题的话,请转到 redis 配置文件来检查是否启用了密码登录

    打开 redis.windows.conf (其余系统请自行查看配置文件名称)文件:

  使用 文本编辑工具 打开后,使用快捷键 Ctrl+F 找到此关键字:requirepass

    将那一行的第一个符号:# ,删减掉,然后写入你在Spring配置中的密码。

 

 

 

 

    如果错误代码为:Could not get a resource from the pool

    此错误代码应该根据实际情况解决,例如服务器暂时挂起(长时间没有连接)等
    其它各自看情况解决吧,如果是无连接对象使用,那么视情况增加连接对象:
    当然,如无其他问题,也可能是服务器自行挂起,此时重启一下 redis 服务即可

 

若需增加连接对象请滑动至Spring Session示例 -> 第四步骤中修改对应的代码

 

读取超时错误,错误代码为:

  java.net.SocketTimeoutException: Read timed out

    此错误比较容易解决:重启网站应用以及 redis 服务器

    错误发生原因:有一定几率为资源阻塞,增加线程池线程数量亦可(增加连接对象)

 

Cookie 错误:关键字带有 Cookie 二字,此时的网站应该可以正常访问,但数据异常。

    错误的发生源自 Spring Session 示例第三步配置错误,请进行修改:

<property name="domainName" value="localhost"/>

 

   【重要】value 中不能带有端口,例如 localhost:8080

    PS:Session 会话都共享了,端口不必加上了。

 

POJO 对象实例化错误,错误关键字:

DefaultSerializer requires a Serializable payload but received an object of type [model.Admin]

 

    错误的发生源自我没有好好看官方文档;

    事实上,Spring 会将对象序列化后存入 Session ,然后将其导入至 redis 中,因此 Java 对象必须实现 Serializable 接口

    大致代码如下:

public class User implements Serializable

    

Http status 500 reids denied 拒绝服务

    通常来说,单机上难以遇见,不过我依旧碰到了。。解决方案网络上有很多种,我在此贴上网络方案中常见的两种:

  1. 【推荐】修改 bind 地址 (redis配置文件中)

  2. 关闭 protected mode

    依旧打开 redis 配置文件:

 

 

     绑定多个可信任IP即可,如:bind 192.168.1.210 xxx.xxx.xxx.xxx

    中间使用空格隔开即可,当然亦可将其注释掉以信任所有IP访问。

    第二种方案:

  更改 protected-mode yes 为 protected-mode no

    最后,重启一下 Redis 服务器以使其生效。

 

关于 Spring Session 会话共享就到这里了,我会长期更新(若遇到错误问题)

来自:https://www.cnblogs.com/chongsaid/

 

 

写于 2019/10/23,于惠州石湾。

博客:https://www.cnblogs.com/chongsaid/

转载权限:需取得博主意见,否则视为侵犯著作权

posted @ 2019-10-23 17:33  辰之道  阅读(992)  评论(0编辑  收藏  举报