SpringBoot 分布式session共享方案(并且可实现session在多个项目中共享)

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 单机环境下我们Session是存储在应用服务的内存中,但是在分布式环境 下,这种存储在应用服务器内存的方案显然不能实现session共享。本次我们将介绍spring-session实现分布式环境下Session共享方案,Session信息存储在redis中。

前言

单机环境下我们Session是存储在应用服务的内存中,但是在分布式环境 下,这种存储在应用服务器内存的方案显然不能实现session共享。本次我们将介绍spring-session实现分布式环境下Session共享方案,Session信息存储在redis中。

版本

spring-session 2.1.4.RELEASE

实现步骤

1. 引入依赖

<!--redis的依赖-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
  </dependency>

spring-session-data-redis 依赖的作用是引入将Session写入redis的工具类。


2. 添加Session配置类

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {}

maxInactiveIntervalInSeconds 属性用来设置Session失效的时间,单位是秒,-1表示永不失效。如果配置1小时失效则是60 * 60 * 1。


3. 配置redis

spring.redis.host=192.168.226.111
spring.redis.database=0
spring.redis.port=6379

测试步骤

将同一个应用分别部署到两台服务上,然后通过nginx 实现负载均衡。

如下,我将应用分别部署在192.168.226.111和192.168.226.112两台服务器上,然后配置nginx 做了转发,配置如下:

upstream bootDemo {
  server 192.168.226.111:8080;
  server 192.168.226.112:8080;
} 
server {
    listen       80;
    server_name  shoptest.jss.com.cn;
  location /spring-boot-session/ {
            proxy_pass http://bootDemo/spring-boot-session/;
        }
}

调用登录接口,接口请求落到了192.168.226.112服务器上,由于是第一次请求,所以Tomcat会给当前会话生成一个Session。该Session中中保存了sessionId,creationTime,lastAccessedTime等信息。其中sessionId是4cebb851-b9b2-488e-9569-9ace9298bb67我们手动设置到Session中的用户信息是{“password”:“123123”,“userName”:“ceshi”}。

然后,我们我们通过 keys "*session*"指令可以找到 当前存入的用户如下图所示:

f5e78b89e8e58215c64dbc521528a7a4_20200120093923829.png

相关key的说明如下:

//存储 Session 数据,数据类型hash,可以使用type查看
        Key:spring:session:sessions:4cebb851-b9b2-488e-9569-9ace9298bb67
        //Redis TTL触发Session 过期。(Redis 本身功能),数据类型:String
        Key:spring:session:sessions:expires:4cebb851-b9b2-488e-9569-9ace9298bb67
        //执行 TTL key ,可以查看剩余生存时间
        //定时Job程序触发Session 过期。(spring-session 功能),数据类型:Set
        Key:spring:session:expirations:133337740000

说明:前面的spring:session是Spring Session保存Session信息的前缀名称。

然后,我们请求获取用户信息接口,接口请求到了192.168.226.112服务器,可以获取到登录用户信息。

b3b7eb739cf78f27c8bd6ab973891bbc_20200115211319486.png

----------------------------------------------------------------完美的分割线-----------------------------------------------------------------------------------


新场景

今天项目中碰到一个需求,A项目和B项目在同一个域名下,在A项目上登录之后,在B项目上就不需要在登录了,这要实现同域名下不同项目间的session共享。

然而,使用这个配置,

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {}

并不能实现,原因是sessionid是放在cookie中缓存的,而这个cookie值的作用域(path)是指定项目名的,如下图所示:

4edc7f389fda7153b0457d2ae1135823_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

所以同域名下不同项目名的项目是不能共享同一个session的,要想共享的话,只有修改Path,将其改到根目录下/。那么该怎么修改呢?首先,我们来看看spring-session是如何管理cookie的。

管理cookie的入口是SessionRepositoryFilter过滤器,而SessionRepositoryFilter过滤器中定义了httpSessionIdResolver属性,这个属性是CookieHttpSessionIdResolver类的实例,而在CookieHttpSessionIdResolver类中又定义了cookieSerializer属性,这个属性是DefaultCookieSerializer类的实例。那么我们最终就可以知道,cookie是通过DefaultCookieSerializer类来管理的。那么我们只需要重新设置DefaultCookieSerializer值就可以了。

那么怎么设置呢?通过看DefaultCookieSerializer类中设置CookiePath的方法getCookiePath,我们知道当cookiePath为空是默认取项目名,当cookiePath不为空时就取cookiePath。

private String getCookiePath(HttpServletRequest request) {
  if (this.cookiePath == null) {
    return request.getContextPath() + "/";
  }
  return this.cookiePath;
  }

所以,我们只需要在SessionConfig类中重新设置DefaultCookieSerializer的cookiePath属性即可。设置如下:

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = -1)
public class SessionConfig {
    @Bean
    public DefaultCookieSerializer defaultCookieSerializer() {
        DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer();
        defaultCookieSerializer.setCookiePath("/");
        return defaultCookieSerializer;
    }
}

设置后的效果如下图所示:

ed6158ad88319e6e4e26734523230d36_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

f3d7cd614406c59cdb3cadd7c70695c0_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

同样的B项目也是同样的设置。


源代码地址:

https://github.com/XWxiaowei/spring-boot-session-demo

参考文章

https://blog.csdn.net/sayyy/article/details/104199215?tdsourcetag=s_pctim_aiomsg


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
28天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
这篇文章是关于如何在SpringBoot应用中整合Redis并处理分布式场景下的缓存问题,包括缓存穿透、缓存雪崩和缓存击穿。文章详细讨论了在分布式情况下如何添加分布式锁来解决缓存击穿问题,提供了加锁和解锁的实现过程,并展示了使用JMeter进行压力测试来验证锁机制有效性的方法。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解分布式情况下如何添加分布式锁 【续篇】
|
5天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
36 1
|
28天前
|
缓存 NoSQL Java
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
这篇文章介绍了如何在SpringBoot项目中整合Redis,并探讨了缓存穿透、缓存雪崩和缓存击穿的问题以及解决方法。文章还提供了解决缓存击穿问题的加锁示例代码,包括存在问题和问题解决后的版本,并指出了本地锁在分布式情况下的局限性,引出了分布式锁的概念。
SpringBoot整合Redis、以及缓存穿透、缓存雪崩、缓存击穿的理解、如何添加锁解决缓存击穿问题?分布式情况下如何添加分布式锁
|
1月前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
|
27天前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
1月前
|
Oracle Java 关系型数据库
分布式锁设计问题之由于GC导致的Client同时操作共享资源的问题如何解决
分布式锁设计问题之由于GC导致的Client同时操作共享资源的问题如何解决
|
1月前
|
存储 运维 安全
多云网络部署存在挑战,F5分布式云应用简化方案解读
多云网络部署存在挑战,F5分布式云应用简化方案解读
26 0
|
2月前
|
消息中间件 编译器 C++
分布式篇问题之TCC方案中的Try阶段主要问题如何解决
分布式篇问题之TCC方案中的Try阶段主要问题如何解决
|
2月前
分布式篇问题之集群(Cluster)模式主控节点的高可用性问题如何解决
分布式篇问题之集群(Cluster)模式主控节点的高可用性问题如何解决
|
19天前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
53 2
基于Redis的高可用分布式锁——RedLock