Spring session redis共享session数据(Nginx)

我们为什么要共享session数据呢?

1.Web中的Session和Cookie回顾

1.1session机制

由于HTTP协议是无状态的协议,一次浏览器和服务器的交互过程就是:
浏览器:你好吗?
服务器:很好!
这就是一次会话,对话完成后,这次会话就结束了,服务器端并不能记住这个人,下次再对话时,服务器端并不知道是上一次的这个人,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是Session。
request.getSession().setAttribute(“msg”,“存入共享的数据”)
request.getSession().getAttribute(“msg”)//获取共享数据

1.2Cookie

服务端如何识别特定的客户?
这个时候需要使用Cookie。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。
用户第一次访问的时候是没有sessionID的,所以此时tomcat会创建一个session对象以及sessionID。
实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session时,服务端会在HTTP协议中向客户端 Cookie 中记录一个Session ID,以后每次请求把这个会话ID发送到服务器,这样服务端就知道客户端是谁了。

1.3url重写

那么如果客户端的浏览器禁用了 Cookie 怎么办?
一般这种情况下,会使用一种叫做URL重写的技术来进行session会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sessionId=xxxxx 这样的参数,服务端据此来识别客户端是谁

1.4Session会话管理及带来的问题

在Web项目开发中,Session会话管理是一个很重要的部分,用于存储与记录用户的状态或相关的数据。
通常情况下session交由容器(tomcat)来负责存储和管理,但是如果项目部署在多台tomcat中,则session管理存在很大的问题

多台tomcat之间无法共享session,比如用户在tomcat A服务器上已经登录了,但当负载均衡(Nginx)跳转到tomcat B时,由于tomcat B服务器并没有用户的登录信息,session就失效了,用户就退出了登录
一旦tomcat容器关闭或重启也会导致session会话失效

因此如果项目部署在多台tomcat中,就需要解决session共享的问题

1.5Session会话共享方案

  1. 第一种是使用容器扩展插件来实现,比如基于Tomcat的tomcat-redis-session-manager插件,基于Jetty的jetty-session-redis插件、memcached-session-manager插件;这个方案的好处是对项目来说是透明的,无需改动代码,但是由于过于依赖容器,一旦容器升级或者更换意味着又得重新配置
    其实底层是,复制session到其它服务器,所以会有一定的延迟,也不能部署太多的服务器。
  2. 第二种是使用Nginx负载均衡的ip_hash策略实现用户每次访问都绑定到同一台具体的后台tomcat服务器实现session总是存在
    这种方案的局限性是ip不能变,如果手机从北京跳到河北,那么ip会发生变化;另外负载均衡的时候,如果某一台服务器发生故障,那么会重新定位,也会跳转到别的机器。
  3. 第三种是自己写一套Session会话管理的工具类,在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中,这个方案灵活性很好,但开发需要一些额外的时间。
  4. 第四种是使用框架的会话管理工具,也就是我们要介绍的Spring session,这个方案既不依赖tomcat容器,又不需要改动代码,由Spring session框架为我们提供,可以说是目前非常完美的session共享解决方案。

浏览器<—>tomact中cookie和session过程分析!!!

1.浏览器每次发送请求到tomcat ,都会把cookie数据传给tomact,而tomact接收到用户请求以后都会从cookie中寻找数据,寻找name为sessionid的对象,如果没有该对象,证明是第一次发送数据到tomact,此时tomact创建一个新的session对象以及对应的sessionId。
在这里插入图片描述
假设创建的新的session对象就是0*200,假设对应的是sessionId为123,此时启动tomact的同时,jvm也会随之启动,此时就会把创建的ID和session对象 记录在jvm中的session容器对象中。

在这里插入图片描述
然后tomact就会把携带sessionId的cookie返还到浏览器中,也就是写入到浏览器中,并存放在指定的cookie中
在这里插入图片描述
此时当sessionId存入cookie中时,代表着第一次请求结束。
当用户再次发送请求的时候,cookie数据传给tomact,此时cookie中存在sessionId的数据,此时tomact会获取这个数据 并到jvm中的session容器对象中根据sessionID来获取到这个对应的value值,如果value存在 那么我们为您表示此时session有效,如果不存在,则表示session已经失效过期,此时tomact会重新创建一个session以及sessionId 并记录在jvm中,然后将sessionId写入浏览器中的cookie中。
在这里插入图片描述

那么session数据为什么会丢失呢?

下图是 第一次访问 tomact里面没有sessionId所以先创建一个新的session对象 并把id和value值放在jvm中,现在并把cookie返还给浏览器(写入到浏览器)
此时 nginx的负载均衡机制 使得请求去访问了另一个tomact,这个tomact中没有sessionId,所以现在tomact又重新创建 一个session对象以及session ID存储在它的jvm中,此时再把新的cookie数据返还给浏览器
由于nginx负载均衡 相当于随机的调用tomact服务器处理请求,所以总是会发生sessionID的不断的覆盖以前的sessionID,这就会导致session里面的数据丢失的现象。
在这里插入图片描述
在这里插入图片描述

那么根本原因就是,每一个tomcat都有一个独立的jvm,所以我们此时要有一个共享的数据的地方 那我们就想到了redis来存放这些session对象值,因为cookie存放的是map 而redis最适合存放map数据。

所以我们现在用Spring session 工具来实现redis共享数据

Spring session 简介

Spring Session 是Spring家族中的一个子项目,它提供一组API和实现,用于管理用户的session信息
它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中。##

Spring session 特点

  1. 提供用户session管理的API和实现
  2. 提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session
  3. 支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题

案例 我们用idea创建一个webapp项目(同域名同项目名)

具体结构如下:
在这里插入图片描述
1.先引入需要的依赖

<!-- servlet依赖的jar包start -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
    </dependency>
    <!-- jsp依赖jar包start -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.1</version>
    </dependency>
    <!-- jsp依赖jar包end -->

    <!--jstl标签依赖的jar包start -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!-- Spring session redis 依赖start -->
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
      <version>1.3.1.RELEASE</version>
    </dependency>
    <!-- Spring session redis 依赖end -->
    <!-- spring web模块依赖 start -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>4.3.16.RELEASE</version>
    </dependency>

2.创建两个servlet,一个用来创建共享数据setServlet,一个用来获取共享数据getServlet。
setServlet

package com.zy.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/setSession")
public class SetServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {

        request.getSession().setAttribute("set","my Setsession");
        response.getWriter().write("setSession ok");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {
        doPost(request,response);
    }
}

setServlet

package com.zy.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/getSession")
public class getServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

     String data  = (String) request.getSession().getAttribute("set");
       response.getWriter().print(data);
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}

3.配置tomcat服务器
setSession
在这里插入图片描述
getSession
在这里插入图片描述
配置两个tomcat 端口需要不同 不然无法一块启动。

4.配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
<!--    就是当Web服务器接收到http请求后,当请求进入对应的Filter进行过滤,
将原本需要由web服务器创建会话的过程转交给Spring-Session进行创建,
本来创建的会话保存在Web服务器内存中,通过Spring-Session创建的会话信息可以保存第三方的服务中,
如:redis,mysql等。Web服务器之间通过连接第三方服务来共享数据,实现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>
    </filter-mapping>

<!--    注册spring监听器-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

5.配置applicationContext.xml文件和springsession.xml(两者需要关联下 然后在applicationContext.xml导入springsession.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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">
<import resource="classpath:springsession.xml"/>

</beans>

springsession.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:comtext="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<comtext:annotation-config/>
<!-- Spring session 的配置类 只配置RedisHttpSessionConfiguration的bean
就可以实现同域名同项目名的session共享(并没有用到单点登录)
适用于我们nginx集群模式下的项目部署
 -->
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<!--这里也可以配置session的生命周期 
<property name="maxInactiveIntervalInseconds" value="1800"/> 以秒为单位30分钟=1800-->

<!-- 配置jedis连接工厂,用于连接redis -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="192.168.64.128"/>
    <property name="port" value="6379"/>
</bean>
</beans>

在这里插入图片描述
6.然后我们在Linux系统里面启动自己的redis服务!!!!!!(记得一定要关掉防火墙)

7.先启动setServlet,再启动getServlet
8.访问setServlet的项目 路径 localhost:8080/项目名/setServlet 出现
在这里插入图片描述
再访问
路径 localhost:8080/项目名/getServlet 出现
在这里插入图片描述
成功的获取在setServlet中存储的数据。下面就是最关键的 在redis存储的session共享数据,看能不能在端口8081成功访问呢?
在这里插入图片描述
成功访问!!!!!!
到此利用Spring session工具 实现 在多个tomcat中 利用redis共享session数据。解决了nginx负载均衡随机访问多个tomcat服务器 带来的数据丢失问题。

此时我们打开redis的客户端 查看存储在里面的共享数据

在这里插入图片描述
红色箭头就是我们存储在redis里面的数据。

我们在最后再次说下实现原理

就是当Web服务器接收到http请求后,当请求进入对应的Filter(web.xml里面的过滤器)进行过滤,将原本需要由web服务器创建会话的过程转交给Spring-Session进行创建,本来创建的会话保存在Web服务器内存中,通过Spring-Session创建的会话信息可以保存第三方的服务中,如:redis,mysql等。Web服务器之间通过连接第三方服务来共享数据,实现Session共享!

在这里插入图片描述

案例 我们用idea创建一个webapp项目(同域名不同项目名)

还是刚才的代码和项目,我们现在把setServlet和getServlet的项目名分别改为p2p和shop
在这里插入图片描述
在这里插入图片描述
我们现在尝试着去拿到共享数据,看结果怎么样?
启动setServlet项目和getServlet项目。
访问:
在这里插入图片描述
此时查看redis里面的session有没有更新
在这里插入图片描述
发现session数据已经上传成功。
现在我们在p2p项目获取数据,肯定可以获取的到
在这里插入图片描述获取成功
现在最关键来了,我们在shop项目看能不能获取到数据呢?
在这里插入图片描述
发生错误,这是什么原因呢?原因在于
在这里插入图片描述
这里根本没有session数据。
为什么没有session数据呢?
因为我们把项目名从一致改成了p2p和shop两个不同的路径项目。而cookie中是不允许跨路径进行存储数据的。
在这里插入图片描述
所以我们怎么解决这个问题呢?我们通过在springseiion的配置文件中,加一个序列化就可以了。

springsession.xml
具体如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:comtext="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<comtext:annotation-config/>
<!-- Spring session 的配置类 -->
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="cookieSerializer" ref="cookieSerializer"></property>
</bean>
<!--    配置一个cookie序列化规则对象,用于改变cookie的存放规则-->
    <bean id="cookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<!--     指定springsession的sessionID存放域名的根路径下,用于实现同域名不同项目名的session共享-->
        <property name="cookiePath" value="/"/>
    </bean>

<!-- 配置jedis连接工厂,用于连接redis -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="192.168.64.128"/>
    <property name="port" value="6379"/>
</bean>
</beans>

然后测试运行:
在这里插入图片描述

运行成功,这样我们就实现了在同域名下不同项目名的session共享。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

案例 我们用idea创建一个webapp项目(同根域名不同二级子域名)

在这里插入图片描述
在这里插入图片描述
不同的cookie存放到了不同的域名下,所以我们获取不了共享的信息。

此时,我们依然在springsession配置文件中 添加这样的代码。就可以实现。代码截图如下:
在这里插入图片描述
测试结果:
在这里插入图片描述
成功!!!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值