分布式环境中如何解决Session的问题
常见的会话解决方案
- 粘性会话 : Sticky Session (任意节点故障引发请求重定向到其他节点的无法获取到原有 Session)
- 会话复制 : Session Replication (复制成本递增)
- 集中会话 : Centralized Session
Spring Session
- 简化集群中的用户会话管理
- 无需绑定容器特定解决方案
支持的存储
- Redis
- MongoDB
- JDBC
- Hazelcast
实现原理
通过定制的 HttpServletRequest 返回定制的 HttpSession。
- SessionRepositoryRequestWrapper
- SessionRepositoryFilter
- DelegatingFilterProxy
基于 Redis 的 HttpSession
基本配置
- @EnableRedisHttpSession
- 提供 RedisConnectionFactory
- 实现 AbstractHttpSessionApplicationInitializer
SpringBoot 对 Spring Session 的支持
引入依赖
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.web</groupId>
<artifactId>session-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>session-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.redis.host=localhost
SessionDemoApplication
package geektime.spring.web.session;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class SessionDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SessionDemoApplication.class, args);
}
@RequestMapping("/hello")
public String printSession(HttpSession session, String name) {
String storedName = (String) session.getAttribute("name");
if (storedName == null) {
session.setAttribute("name", name);
storedName = name;
}
return "hello " + storedName;
}
}
测试
localhost:8080/hello?name=spring
hello spring
//重启进程(模拟单机会话丢失),印证会话保存在redis中
//客户端每次访问会携带Cookie中的 Session_ID
localhost:8080/hello?name=xx
hello spring