大话 Spring Session 共享

javaweb中我们项目稍微正规点,都会用到单点登录这个技术。实现它的方法各家有各界的看法。这几天由于公司项目需求正在研究。下面整理一下最近整理的心得。

简介

  • 在分布式项目中另我们头疼的是多项目之间的数据共享(即Session共享),经常会出现数据丢失的情况。为了解决这种Bug。前人已经把我们实现了两种解决的办法。今天我在这里一下这两种方式。侧重偏向第二种方法

配tomcat实现session共享

资源下载

点我下载


配置Tomcat

  • 下载好上面的jar包,把他放在tomcat的lib文件下。

  • 然后修改Tomcat里的conf/context.xml文件
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
host="192.168.1.130"
port="7006"
database="db0"
maxInactiveInterval="60" />

  • maxInactiveInterval 就是设置缓存的过期时间。但是在实际测试中我发现这个属性并没有发生作用。(你有啥见解欢迎点评)

到这里我们的第一个Tomcat配置完成。下面就是重复上面的步骤多配制几个Tomcat

配置nginx

  • nginx功能丰富,可以作为HTTP服务器,也可以作为反向代理的服务器,邮件服务器。支持FastCGI、SSL、Virtual Host、URL Rewrite、Gzip等功能。并且支持很多第三方的模块扩展。
  • nginx的下载官网有现成的,直接下载符合自己电脑的版本傻瓜式安装就行了。(安装或者解压不要出现中文)
  • 找到conf/nginx.conf文件在里面修改设置
    • listen 802: 我们监听的端口
    • server_name 192.168.1.130:监听的地址
    • proxy_pass http://mynginxserver:根据情况跳转最后的连接
    • 在mynginxserver配置多个Tomcat:
upstream mynginxserver {
            server 192.168.1.78:8080 weight=1;
            server 192.168.1.130:8080 weight=1;
        }
  • 完成配置
upstream mynginxserver {
        server 192.168.1.78:8080 weight=1;
        server 192.168.1.130:8080 weight=1;
    }

    server {
        listen       802;
        server_name  192.168.1.130;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://mynginxserver;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache‘s document root
        # concurs with nginx‘s one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }
  • 这个时候当我们两个Tomcat都在运行状态我们浏览器中输入
192.168.1.130:802
  • nginx就会随机选择一个Tomcat来运行了。同时这两个Tomcat的session是共享的。就是说A Tomcat的session B 可以用。就算A 宕机了B也照样按到A的session。

配spring session实现session共享

  • 上面的一种方式我也是在网上案列操作的。在自己的项目中并不适合。因为我的项目中配置的是redis集群(非sentinel).所以在Tomcat里面配置是不行的。

    解释:第一种方式配置的redis默认是单节点的redis。也就是我们的session会产生sessionID作为redis中key存在我们配置redis节点的库上面。但是如果我们是redis集群的话,集群会将根据key值算出slot值,在根据slot值决定存取在哪一台redis服务商。也就是说在存session前我们根本不知道会存在哪一台redis上。所以这种方法不适合于redis集群。

具体看看我画的思维导图吧:

第二章明显出错了,事实上并没有存储在实现约定的redis上


spring session 配置

  • 说了这么多了,我们下面开始讲解spring中集成的session共享配置。首先如果你是maven项目那么直接引入jar
<dependency>                                                    <groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
  • 若果你不是maven项目,那么就麻烦你自己去下载这些相关的jar包。
spring-data-redis.jar
redis.clients.jar
spring-session.jar

spring配置文件配置

  • 有了上面的准备资料后,我们可以在是spring的配置文件中配置spring session了
  • 很简单我们需要引入spring session中的bean
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> 
  • 在spring的配置文件中我们只需要配置这一个bean就可以了。下面离将session共享到redis集群上只差一步了。就是在web.xml文件中配置拦截器
<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>
  • 到这里配置完成了,我们可以重启我们的项目运行一下存取session的部分。然后再去redis集群找看看有没有session。在redis中默认的session的key是spring:session:sessions:c0d1fadd-b04a-4244-9525-0f13ea7173bf。其中后面一串id就是我们Tomcat和浏览器会话时差生的sessionid。

问题

  • 在上面我们配置spring session 已经可以实现了session共享了。但是细心的朋友可以发现。session并没有真正意义上的共享。上面我们可以看到session在redis集群中的key包含了Tomcat和浏览器的sessionid。也就是说redis上这个session只能是我的这个Tomcat在该浏览器上才可以访问到。别的Tomcat就算和同一个浏览器交互也是拿不到redis上这个session的。这种效果是你们想要的吗。答案并不是。上面实现的效果我用一幅图描述一下。

  • 也就是说上面我的配置只是将Tomcat的session转移到了redis上。多服务的session仍然是各区各的。这是一个大坑。坑了好久。然后我想了另外一种办法现在可以解决这个局限性。

解决办法

  • 解决上面的问题。这里我提供三种思路。三种思路我都实现了一下。各有利弊。最后决定采用第三种比较靠谱。

1– 重写spring session源码,重写里面讲session存储在redis那部分代码,主要就是为了将redis 中session的key写成固定值。以后我们在去session中取这个固定的key就可以实现session共享了。

2–只重写spring session中我们的session仓库中去的策略,就是在A项目登录后将产生的sessionID传递给B项目。B项目在根据这个sessionid去redis中获取。

上面两中方法需要自定义很多类,整理了自定义的类和配置文件的配置。点我下载

3– 在向session中存值的地方,将sessionid作为value,请求头中的Host和Agent组合最为key存储在redis上。然后我们在spring session去session的方法里在根据Host和Agent组成的key去获取sessionid,然后就可以获取对应的session了。

存取策略重构

  • 这里主要讲解一下第三种的实现步骤。首先我新建一个类用来读取request求情头中的host和agent拼接的key值。
public static Map<String, Object> getInfoFromUrl(HttpServletRequest request)
    {
        Map<String, Object> resultMap=new HashMap<String, Object>();
        String Agent = request.getHeader("User-Agent");
        String Host = request.getHeader("Host");
        resultMap.put("agent", Agent);
        resultMap.put("host", Host);
        return resultMap;
    } 

    public static String getKeyFromUrl(HttpServletRequest request)
    {
        String result="";
        Map<String, Object> map = getInfoFromUrl(request);
        Set<String> keySet = map.keySet();
        for (String key : keySet)
        {
            result+=map.get(key).toString();
        }
        return result;
    }
  • 然后在我们调用session存值的地方,也就是登录的地方。通过我们项目中的redis操作类将其存储起来。

  • 然后我们修改spring session中的cookie仓库中的去request的sessionid的策略

  • 最后我们在spring的配置中修改是spring session的策略为我么你的cook策略
<bean id="redisCacheTemplate" class="com.bshinfo.web.base.cache.RedisCacheTemplate"/>
    <bean id="zxh" class="com.bshinfo.web.base.cache.CookieHttpSessionStrategyTest">
        <property name="redisCacheTemplate" ref="redisCacheTemplate" />
    </bean>
    <bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
        <property name="httpSessionStrategy" ref="zxh"/>
        <property name="maxInactiveIntervalInSeconds" value="60"/>
    </bean> 
  • 最后就实现了我们的效果。效果就是A项目在X浏览器上登录后。B项目在X浏览器获取session的用户可以正常获取。否则获取失败。
时间: 2024-10-28 20:31:41

大话 Spring Session 共享的相关文章

spring boot + redis 实现session共享

这次带来的是spring boot + redis 实现session共享的教程. 在spring boot的文档中,告诉我们添加@EnableRedisHttpSession来开启spring session支持,配置如下: @Configuration @EnableRedisHttpSession public class RedisSessionConfig { } 而@EnableRedisHttpSession这个注解是由spring-session-data-redis提供的,所以

Spring Session + Redis实现分布式Session共享

通常情况下,Tomcat.Jetty等Servlet容器,会默认将Session保存在内存中.如果是单个服务器实例的应用,将Session保存在服务器内存中是一个非常好的方案.但是这种方案有一个缺点,就是不利于扩展. 目前越来越多的应用采用分布式部署,用于实现高可用性和负载均衡等.那么问题来了,如果将同一个应用部署在多个服务器上通过负载均衡对外提供访问,如何实现Session共享? 实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat.Jetty等服务器提供的Session

spring+redis+nginx 实现分布式session共享

1,spring 必须是4.3以上版本的 2,maven配置 添加两个重要的依赖 <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.2.2.RELEASE</version> <type>pom</type>

利用spring session解决共享Session问题

1.共享Session问题 HttpSession是通过Servlet容器创建和管理的,像Tomcat/Jetty都是保存在内存中的.而如果我们把web服务器搭建成分布式的集群,然后利用LVS或Nginx做负载均衡,那么来自同一用户的Http请求将有可能被分发到两个不同的web站点中去.那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢? 最简单的想法就是把session数据保存到内存以外的一个统一的地方,例如Memcached/Redis等数据库中.那么问题又来了,如何

使用Spring Session和Redis解决分布式Session跨域共享问题

前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用的均衡算法有IP_Hash.轮训.根据权重.随机等.不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题. 文末分享了我一部分私人收藏 有兴趣的可以收藏看一下的 都是架构师进阶的内容 实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat.Jetty等服务器提供的Session共享功能,将Sessi

springboot+spring session+redis+nginx实现session共享和负载均衡

环境 centos7. jdk1.8.nginx.redis.springboot 1.5.8.RELEASE session共享 添加spring session和redis依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> &

Spring Boot 多站点利用 Redis 实现 Session 共享

如何在不同站点(web服务进程)之间共享会话 Session 呢,原理很简单,就是把这个 Session 独立存储在一个地方,所有的站点都从这个地方读取 Session. 通常我们使用 Redis 来解决这个问题 Spring Boot 2.1.8 Redis 5.0.3 本项目源码 github 下载 本章解决前面文章 Spring Boot 利用 nginx 实现生产环境的伪热更新 产生的session共享问题. 1 Redis 准备 本示例使用 Redis 5.0.3 操作系统为 Mac

Redis实战和核心原理详解(5)使用Spring Session和Redis解决分布式Session跨域共享问题

Redis实战和核心原理详解(6)使用Spring Session和Redis解决分布式Session跨域共享问题 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用的均衡算法有IP_Hash.轮训.根据权重.随机等.不管对于哪一种负载均衡算法,由于Nginx对不同的请求分发到某一个Tomcat,Tomcat在运行的时候分别是不同的容器里,因此会出现session不同步或者丢失的问题. 实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat.Jetty等服务器提

Spring MVC集成Spring Data Reids和Spring Session实现Session共享

说明:Spring MVC中集成Spring Data Redis和Spring Session时版本是一个坑点,比如最新版本的Spring Data Redis已经不包含Jedis了,需要自行引入.且最新版本的2.0.1会与Spring MVC 4.1.4有冲突,估计写法错了.所以要明确引入的Spring MVC版本和Spring Data Redis和Spring Session版本. 小提示:如果想要官方明确的版本可以参考Spring Boot的版本,比如我使用了1.4.7的Spring