为什么需要Redis共享Session?
在传统的单体应用中,用户的所有请求通常都会被负载均衡器(如Nginx)转发到同一台服务器(JVM实例),Session数据可以直接存储在该服务器的内存中,没有问题。

但在分布式系统或微服务架构中,情况发生了变化:
- 负载均衡:用户的请求可能被转发到集群中的任何一台服务器。
- 无状态服务:为了水平扩展,我们通常希望服务是无状态的,即不依赖本地存储的数据。
- 会话不一致:如果用户第一次请求到达了服务器A,Session信息存在A的内存中;下一次请求被转发到了服务器B,B上没有这个用户的Session,用户就需要重新登录,体验极差。
Redis Session共享正是为了解决这个问题,它将Session数据从各个服务器的本地内存中剥离出来,统一存储在一个外部的、所有服务节点都能访问的Redis服务器上,这样,无论用户的请求被分发到哪个服务节点,应用都能从Redis中获取到相同的Session数据。
实现步骤(以Spring Boot + Spring Session + Redis为例)
这是目前最主流、最简单、最优雅的实现方式,Spring Session项目专门用于简化Session管理,它提供了与Redis等存储后端的集成。
第1步:环境准备
- 安装并启动Redis服务器。
- 你可以从 Redis官网 下载并安装。
- 或者使用Docker一键启动:
docker run -d -p 6379:6379 redis
- 创建一个Spring Boot Web项目。
- 确保项目包含
spring-boot-starter-web依赖。
- 确保项目包含
第2步:添加Maven依赖
在你的 pom.xml 文件中,添加以下关键依赖:

<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Session Data Redis -->
<!-- 这个依赖是核心,它会自动配置Spring Session和Redis的集成 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- Spring Boot Data Redis Starter -->
<!-- 用于连接Redis服务器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Lombok (可选,简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
第3步:配置Redis连接
在 application.properties 或 application.yml 文件中配置Redis服务器的地址和端口。
application.properties 示例:
# Redis服务器地址 spring.redis.host=localhost # Redis服务器端口 spring.redis.port=6379 # (可选) 设置密码,如果Redis设置了密码 # spring.redis.password=yourpassword # (可选) 设置数据库索引,默认是0 # spring.redis.database=0
application.yml 示例:
spring:
redis:
host: localhost
port: 6379
# password: yourpassword
# database: 0
第4步:启用Spring Session Redis(最关键的一步)
创建一个配置类,并使用 @EnableRedisHttpSession 注解,这个注解会告诉Spring Boot,你希望使用Redis来存储HTTP Session。

import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@Configuration
@EnableRedisHttpSession // 核心注解,启用基于Redis的HttpSession
public class SessionConfig {
// 可以在这里配置Session的超时时间等
// @EnableRedisHttpSession(maxInactiveInSeconds = 1800) // 设置30分钟超时
}
就是这么简单! 加上这个注解后,Spring Session就会接管所有与Session相关的操作。
第5步:创建一个简单的Controller来测试
创建一个Controller,用来设置Session和获取Session。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@RestController
@RequestMapping("/session")
public class SessionController {
@GetMapping("/set")
public String setSession(HttpSession session) {
// 向session中存入一个属性
session.setAttribute("user", "zhangsan");
session.setAttribute("loginTime", System.currentTimeMillis());
return "Session has been set. User: zhangsan";
}
@GetMapping("/get")
public String getSession(HttpSession session) {
// 从session中获取属性
String user = (String) session.getAttribute("user");
Long loginTime = (Long) session.getAttribute("loginTime");
if (user != null) {
return "Get Session Success! User: " + user + ", Login Time: " + loginTime;
} else {
return "Session is empty or expired.";
}
}
@GetMapping("/invalidate")
public String invalidateSession(HttpSession session) {
// 使session失效
session.invalidate();
return "Session has been invalidated.";
}
}
第6步:测试
- 启动你的Spring Boot应用。
- 使用Postman或浏览器访问
http://localhost:8080/session/set,这个请求会在Redis中创建一个Session。 - 再次访问
http://localhost:8080/session/get,你会成功获取到之前设置的Session信息。 - 为了验证分布式效果,你可以启动两个完全相同的应用实例(修改端口为8081后启动),并配置一个负载均衡器(如Nginx)将请求轮流分发到8080和8081端口,你会发现,无论访问哪个端口,Session数据都是共享的。
底层原理浅析
当你使用了 @EnableRedisHttpSession 后,Spring Session做了以下几件事:
- 替换
HttpSession实现:Spring Session创建了一个自定义的SessionRepositoryFilter,这个Filter会拦截所有HTTP请求,并用Spring Session自己实现的SessionRepository替换掉Tomcat(或其他Servlet容器)默认的HttpSession实现。 - Session操作:当你调用
session.setAttribute()或session.getAttribute()时,实际上操作的是Spring Session的ExpiringSession对象。 - 序列化与存储:Spring Session会将这个
ExpiringSession对象进行序列化(默认使用的是 JDK序列化,但推荐使用 JSON 或 JDK序列化,后者性能更好),然后将序列化后的字节数据存入Redis。- Redis Key:通常是一个以
spring:session:sessions:为前缀,加上Session ID的字符串。spring:session:sessions:3fa0c5b0-8d3a-4f9c-b8b0-1f2e3d4c5b6a。 - Redis Value:就是序列化后的Session数据。
- Redis Hash:为了支持过期等功能,Spring Session还会在Redis中使用一个Hash结构来存储Session的元数据,例如过期时间 (
spring:session:expirations:3fa0c5b0...)。
- Redis Key:通常是一个以
- Session ID传递:Session ID仍然通过Cookie在浏览器和服务器之间传递,Spring Session会确保这个Cookie在所有服务实例之间是有效的。
高级配置与最佳实践
Session序列化方式
默认的JDK序列化方式存在安全性和跨语言兼容性问题,推荐使用 JSON 序列化。
配置JSON序列化:
创建一个配置类来配置Redis的Template。
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 使用StringRedisSerializer 