Spring Session分布式会话管理

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Spring Session分布式会话管理

1 HttpSession 回顾

1.1 什么是 HttpSession

是 JavaWeb 服务端提供的用来建立与客户端会话状态的对象。可以用来识别某个客户端

2 Session 共享

2.1 什么是 Session 共享

是指在一个浏览器对应多个 Web 服务时,服务端的 Session 数据需要共享。

2.2 Session 共享应用场景

  1. 单点登录
  2. Web 服务器集群等场景都需要用到

3 Session 共享常见的解决方案

3.1 Session 复制

通过对应用服务器的配置开启服务器的 Session 复制功能,在集群中的几台服务器之间同步 Session 对象,使得每台服务器上都保存所有的 Session 信息,这样任何一台宕机都不会导致 Session 的数据丢失,服务器使用 Session 时,直接从本地获取。这种方式的缺点也比较明显。因为 Session 需要时时同步,并且同步过程是有应用服务器来完成,由此对服务器的性能损耗也比较大。

3.2 Session 绑定

利用 hash 算法,比如 nginx 的 ip_hash,使得同一个 Ip 的请求分发到同一台服务器上。这种方式不符合对系统的高可用要求,因为一旦某台服务器宕机,那么该机器上的Session 也就不复存在了,用户请求切换到其他机器后么有 Session,无法完成业务处理。


3.3 利用 Cookie 记录 Session

Session 记录在客户端,每次请求服务器的时候,将 Session 放在请求中发送给服务器,服务器处理完请求后再将修改后的 Session 响应给客户端。这里的客户端就是 cookie。利用 cookie 记录 Session 的也有缺点,比如受 cookie 大小的限制,能记录的信息有限,安全性低,每次请求响应都需要传cookie,影响性能,如果用户关闭 cookie,访问就不正


常。


3.4 Session 服务器

Session 服务器可以解决上面的所有的问题,利用独立部署的 Session 服务器统一管理


Session,服务器每次读写 Session 时,都访问 Session 服务器。


对于 Session 服务器,我们可以使用 Redis 或者 MongoDB 等内存数据库来保存 Session


中的数据,以此替换掉服务中的 HttpSession。达到 Session 共享的效果。

4 什么是 Spring Session

Spring Session 是 Spring 的项目之一。Spring Session 提供了一套创建和管理 Servlet


HttpSession 的方案,默认采用外置的 Redis 来存储 Session 数据,以此来解决 Session 共享的

问题。

5 Spring Session 的使用

5.1 安装 Redis

第一步 需要在 linux 系统中安装 gcc

命令:yum install -y gcc-c++

第二步 需要将下载好的 redis 压缩包添加到 linux 服务器中

版本:redis-3.0.0.tar.gz

redis 的版本:副版本号奇数版本号是测试版,不建议在生产环境中使用。偶数版本时稳定版建议在生产环境中使用。3.0 版本更新比较大。集成了集群技术

第三步 解压压缩包

命令:tar -zxvf redis…

第四步 编译 redis

命令:进入 redis 的解压完毕的根目录下 执行命令:make

第五步 安装 redis

命令:进入 redis 的解压完毕的根目录下,执行命令: make install PREFIX=/usr/local/redis

第六步:启动 redis

1.前端启动

在 bin 目录下执行命令: ./redis-server (ctrl+c)退出 redis

2.后端启动

(1)先将 redis 解压目录下的 redis.conf 文件拷贝到 安装好的 redis 的 bin 目录下

命令:cp redis.conf /usr/local/redis/bin

(2)修改拷贝过来的 redis.conf 配置文件

命令:vim redis.conf

将 daemonize no 改为 yes

(3)启动 redis

在 bin 目录下执行命令:./redis-server redis.conf

(4)查看 redis 启动是否成功

输入命令:ps aux|grep redis

(5) 关闭 redis 的命令

./redis-cli shutdown

第七步:测试 redis

在 bin 目录下启动 redis 自带的客户端 ./redis-cli

常见 redis 命令:

ping—>pong

5.2 搭建案例环境

5.2.1 版本介绍

JDK:1.8

Spring Boot:2.1.6.RELEASE

Spring Session:Bean-SR3

5.2.2 创建项目

创建父工程 spring_session

在Idea中创建Maven工程即可

创建模块 session_service1

选中项目,右键单击创建模块,修改项目位置即可

创建模块 session_service2

5.3 修改 POM 文件添加坐标依赖

修改父工程POM文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.bjsxt</groupId>
  <artifactId>spring_session</artifactId>
  <packaging>pom</packaging>
  <version>1.0-SNAPSHOT</version>
  <modules>
    <module>session_service1</module>
    <module>session_service2</module>
  </modules>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
  </parent>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-bom</artifactId>
        <version>Bean-SR3</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <!--web Starter-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--Redis Starter-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-redis</artifactId>
    </dependency>
    <!--Spring session data redis-->
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    <!--Lettuce 是一个基于 Netty 的 NIO 方式处理 Redis 的技术 -->
    <dependency>
      <groupId>io.lettuce</groupId>
      <artifactId>lettuce-core</artifactId>
    </dependency>
  </dependencies>
</project>

5.4 添加配置文件

5.4.1 session_service1

# 服务端口
server:
  port: 8080
spring:
  redis:
    host: 192.168.70.156 #Redis 配置
    port: 6379
    password:
    database: 0

5.4.2 session_service2

# 服务端口
server:
  port: 8081
spring:
  redis:
    host: 192.168.70.156 #Redis 配置
    port: 6379
    password:
    database: 0

5.5 创建启动类

5.5.1 session_service1

@SpringBootApplication
@EnableRedisHttpSession
public class Service1Application {
  public static void main(String[] args){
    SpringApplication.run(Service1Application.class,args);
  }
}

5.5.2 session_service2

@RestController
@RequestMapping("service1")
public class WebController {
  @RequestMapping("/setMsg")
  public String setMsg(HttpSession session,String msg){
    session.setAttribute("msg",msg);
    return "ok";
  }
}

5.6 编写测试代码测试效果

5.6.1 session_service1

5.6.2 创建Controller

@RestController
@RequestMapping("/service2")
public class WebController {
  @RequestMapping("/getMsg")
  public String getMsg(HttpSession session){
    String msg = (String) session.getAttribute("msg");
    return msg;
  }
}

5.6.3 session_service2

5.6.4 创建Controller

@RestController
@RequestMapping("/service2")
public class WebController {
  @RequestMapping("/getMsg")
  public String getMsg(HttpSession session){
    String msg = (String) session.getAttribute("msg");
    return msg;
  }
}

5.6.5测试

  • 启动上面创建的两个项目
  • 访问

6 共享自定义对象

6.1 创建Users实体类

两个项目中都添加

public class Users implements Serializable {
  private String username;
  private String userpwd;
  public String getUsername() {
    return username;
  }
  public String getUserpwd() {
    return userpwd;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public void setUserpwd(String userpwd) {
    this.userpwd = userpwd;
  }
}

6.2 修改session_service1的controller

@RestController
@RequestMapping("service1")
public class WebController {
  @RequestMapping("/setMsg")
  public String setMsg(HttpSession session,String msg){
    session.setAttribute("msg",msg);
    return "ok";
  }
  /**
  * 获取 Users 信息,保存到 HttpSession 中
  */
  @RequestMapping("/addUsers")
  public String addUsers(HttpSession session, Users users){
    session.setAttribute("u",users);
    return "ok";
  }
}

6.3 修改session_service2的controller

@RestController
@RequestMapping("/service2")
public class WebController {
  @RequestMapping("/getMsg")
  public String getMsg(HttpSession session){
    String msg = (String) session.getAttribute("msg");
    return msg;
  }
  /**
  * 获取 HttpSession 中的 Users 对象
  */
  @RequestMapping("/getUsers")
  public Users getUsers(HttpSession session){
    Users users = (Users) session.getAttribute("u");
    return users;
  }
}

6.4测试

7 SpringSession的Redis存储结构

spring:session:expirations:(Set结构)做用户ttl过期时间记录


这个k中的值是一个时间戳,根据这个Session过期时刻滚动至下一分钟而计算得出。


这个k的过期时间为Session的最大过期时间+5分钟(session的最大过期时间为30mins,加上5mins,那么就是2100s,上面的ttl值每次刷新之后就是2100)。


spring:session:sessions:(Hash结构)


maxInactiveInterval:过期时间间隔(session的失效时间)


creationTime:创建时间


lastAccessedTime:最后访问时间


sessionAttr:Attributes中的数据


存储Session的详细信息,包括Session的过期时间间隔、最后的访问时间、attributes


的值。这个k的过期时间为Session的最大过期时间+5分钟。


spring:session:sessions:expires:(String结构)过期时间记录


这个k-v不存储任何有用数据,只是表示Session过期而设置。


这个k在Redis中的过期时间即为Session的过期时间间隔。

8 设置Session的失效时间

8.1 在注解中设置失效时间

@SpringBootApplication
@EnableRedisHttpSession(maxInactiveIntervalInSeconds=10)
public class Service1Application {
  public static void main(String[] args){
    SpringApplication.run(Service1Application.class,args);
  }
}

注意:那个ttl还是要加上5mins的时间,另外就是一个服务的ttl与另一个没有相互影响

9 @EnableRedisHttpSession注解讲解

9.1 maxInactiveIntervalInSeconds

设置 Session 的失效时间,单位为秒。默认(1800 秒)30 分钟。


9.2 redisNamespace

为键定义唯一的命名空间。该值用于通过更改前缀与默认 spring:session 隔离会话


这个进行测试的话,就是将service1服务设置redisNamespace为spring:session之外的,然后通过session存储Users到redis中,此时service2还是使用的是默认的命名空间,这样service1通过session存到redis中的数据,service2就取不到了。


如果我们想要将一组服务可以相互共享session中的数据,只要将他们的命名空间统一即可


9.3 redisFlushMode

Redis 会话的刷新模式。默认值为“保存”


9.4 cleanupCron

过期会话清理作业的 cron 表达式。默认值(“0 * * * * *”)每分钟运行一次。

10 更换 Spring Session 的序列化器

Spring Session 中默认的序列化器为 jdk 序列化器,该序列化器效率低下,内存占用大。


我们可以根据自己的需要更换其他序列化器,如 GenericJackson2JsonRedisSerializer 序列化


器。


10.1 在配置类中创建自定义序列化器

两个项目中都要添加

@Configuration
public class SpringSessionConfig {
  /**
  * 更换默认的序列化器
  *
  */
  @Bean("springSessionDefaultRedisSerializer")
  public RedisSerializer defaultRedisSerializer(){
    return getSerizlizer();
  }
  /**
  * 定义序列化器
  */
  private RedisSerializer getSerizlizer(){
    return new GenericJackson2JsonRedisSerializer();
  }
}

10.2 测试

相关实践学习
基于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
目录
相关文章
|
4月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
133 1
|
2月前
|
存储 缓存 NoSQL
分布式架构下 Session 共享的方案
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求、系统架构和性能要求等因素,选择合适的 Session 共享方案。同时,还需要不断地进行优化和调整,以确保系统的稳定性和可靠性。
|
3月前
|
消息中间件 Java 对象存储
数据一致性挑战:Spring Cloud与Netflix OSS下的分布式事务管理
数据一致性挑战:Spring Cloud与Netflix OSS下的分布式事务管理
56 2
|
3月前
|
存储 NoSQL Java
Spring Session框架
Spring Session 是一个用于在分布式环境中管理会话的框架,旨在解决传统基于 Servlet 容器的会话管理在集群和云环境中的局限性。它通过将用户会话数据存储在外部介质(如数据库或 Redis)中,实现了会话数据的跨服务器共享,提高了应用的可扩展性和性能。Spring Session 提供了无缝集成 Spring 框架的 API,支持会话过期策略、并发控制等功能,使开发者能够轻松实现高可用的会话管理。
Spring Session框架
|
3月前
|
存储 NoSQL Java
使用springSession完成分布式session
本文介绍了如何使用 `spring-session` 实现分布式 Session 管理,并提供了将 Session 存储在 Redis 中的具体配置示例。通过配置相关依赖及 Spring 的配置文件,可以轻松实现 Session 的分布式存储。示例中详细展示了所需的 Maven 依赖、Spring 配置及过滤器配置,并给出了启动项目后在 Redis 中查看 Session 数据的方法。
|
5月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
Spring Cloud Alibaba 发布了 Scheduling 任务调度模块 [#3732]提供了一套开源、轻量级、高可用的定时任务解决方案,帮助您快速开发微服务体系下的分布式定时任务。
15004 31
|
4月前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
|
4月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
4月前
|
Dubbo Java 调度
揭秘!Spring Cloud Alibaba的超级力量——如何轻松驾驭分布式定时任务调度?
【8月更文挑战第20天】在现代微服务架构中,Spring Cloud Alibaba通过集成分布式定时任务调度功能解决了一致性和可靠性挑战。它利用TimerX实现任务的分布式编排与调度,并通过`@SchedulerLock`确保任务不被重复执行。示例代码展示了如何配置定时任务及其分布式锁,以实现每5秒仅由一个节点执行任务,适合构建高可用的微服务系统。
72 0
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?