聊聊几种常见的分布式Session解决方案


highlight: xcode

theme: vuepress

问题引入:什么是分布式Session?

分布式 Session 是指在多台服务器之间共享和管理用户的会话数据,使得用户的会话状态能够在不同的服务器上保持一致。这样,无论用户的请求被路由到哪台服务器,都能够访问到相同的会话信息,从而保证用户体验的一致性。

回顾一下单机服务的 HttpSession 的存储:

在传统的 JavaWeb 的 Tomcat + Servlet 的项目中,HttpSession 通常存储在 JVM 内存中。浏览器第一次访问服务之后,会得到一个名为 JSESSIONID 的 Cookie。在后续的请求中,浏览器都会携带此 Cookie。用一个图来简单说明一下:

在 HttpSession 中存储用户数据最原始的做法是怎么做的呢?初学者一般都是这么写(因为教科书上也是这么写的🐶):

java public void doGet(HttpServletRequest req, HttpServletResponse resp) { HttpSession session = req.getSession(); // 获取用户名 String username = req.getParameter("username"); session.setAttribute("username", username); // 后续操作... }

这种写法只适合单机部署的场景,在分布式场景下是不可行的,因为请求会到不同的机器上,每台机器上的数据都不一样。而且服务端对同一个客户端的请求不能共享 Session。

常见解决方案

对于分布式 Session,通常有以下几种解决方案:

  1. Session 粘滞:通过负载均衡器(如 Nginx、F5 等)将同一用户的请求始终路由到同一台服务器上。
  2. 数据库共享 Session:将 Session 存储在数据库中(如 MySQL、PostgreSQL 等),各个服务器访问同一个数据库。
  3. Token 方式:将 Session 数据编码为 Token(如 JWT),并由客户端保存(通常在 Cookie 或 HTTP 头中传输)。
  4. 分布式文件系统:将 Session 存储在分布式文件系统(如 NFS、GlusterFS 等)中,各个服务器访问共享的文件系统。
  5. 缓存共享 Session:使用分布式缓存系统(如 Redis、Memcached 等)存储 Session 数据,各个服务器共享同一个缓存。

Session 粘滞

以 nginx 的 IP Hash 策略为例,通过 IP Hash 策略可以将客户端的 IP 地址哈希到特定的后端服务器上,从而确保同一个客户端的请求总是被路由到同一台服务器上。

示例配置:

```conf http { upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; }

复制代码
server {
    location / {
        proxy_pass http://backend;
    }
}

} ```

寥寥几行,通过简单的配置即可实现。同一客户端的请求总是路由到同一台服务器,因此可以保证会话的一致性,避免 Session 丢失。

但是缺点也很明显,由于 IP 地址的分布不均匀,可能导致某些服务器负载过重,而其他服务器负载较轻。如果某台服务器宕机,与其绑定的所有客户端会话信息都会丢失,无法自动迁移到其他服务器。添加或移除服务器会改变 IP 到服务器的映射,导致会话丢失。对于跨地域的用户,IP Hash 可能导致某些地域的用户集中到特定的服务器,进一步加剧负载不均衡。

明显缺点大于优点,所以基本不推荐。

数据库共享Session

使用数据库存储 Session,虽然实现起来很简单,但是会导致每次请求都要查询数据库让数据库的负载变大。 在一些性能敏感的系统中,性能瓶颈明显,扩展性很差。所以也不推荐。

Token方式

将 Session 数据编码为 Token,并由客户端保存可以让服务端无状态化,扩展性好,不依赖集中存储。但是 Token 很可能被截取和伪造,安全性低,需要加密和签名。也不是很推荐。

分布式文件系统

使用分布式文件系统才存储 Session,实现起来相对简单,可以利用现有的文件系统。但是性能比较差,文件系统的同步和一致性问题需要考虑,所以也不是很推荐。

缓存共享Session

上面说了几种方案都不推荐,这个总得推荐了吧?哈哈哈,那肯定啊,不然这篇文章都没有写的必要了。

使用缓存系统存储 Session,读写速度快,而且支持高并发。需要注意的是缓存失效策略。以 Redis 为例存储分布式 Session。

创建一个 Spring Boot 工程,导入 Spring Session 的依赖:

```xml org.springframework.boot spring-boot-starter-web

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</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-data-redis</artifactId>
</dependency>

```

配置一下 application.yml 文件:

```yaml server: port: 8081

spring: redis: port: 6379 host: localhost ```

写一个简单的 Controller:

```java @RestController public class TestController {

复制代码
@GetMapping("/")
public String hello(HttpServletRequest req) {
    HttpSession session = req.getSession(true);
    System.out.println(session);
    Cookie[] cookies = req.getCookies();
    if (cookies == null) {
        return "Hello World";
    }
    for (Cookie cookie : cookies) {
        System.out.println(cookie.getName());
    }
    return "Hello World";
}

} ```

访问一下浏览器:

可以看到名称为 JSESSIONID 的 Cookie。

再看一下 Redis 中的数据:

可以看到 Session 数据已经自动存到 Redis 中了。你可能疑问为什么会自动存进去了,我并没有干什么操作啊?

因为 spring-session-data-redis 这个包在 Spring 接收到了请求的时候自动帮我们做了这个操作。有兴趣的话可以分析一下源码。

我们可以再开启一个实例观察一下 Cookie 是否发生了变化:

访问一下 http://localhost:8082 然后打开 F12 查看 Cookie:

可以看到 8081 和 8082 的 JSESSIONID 的 Cookie 是一样的,最终实现了 Session 的共享。

怎么样,你学会了吗?

相关推荐
数据智能老司机4 小时前
CockroachDB权威指南——CockroachDB SQL
数据库·分布式·架构
数据智能老司机5 小时前
CockroachDB权威指南——开始使用
数据库·分布式·架构
数据智能老司机5 小时前
CockroachDB权威指南——CockroachDB 架构
数据库·分布式·架构
IT成长日记5 小时前
【Kafka基础】Kafka工作原理解析
分布式·kafka
州周7 小时前
kafka副本同步时HW和LEO
分布式·kafka
爱的叹息9 小时前
主流数据库的存储引擎/存储机制的详细对比分析,涵盖关系型数据库、NoSQL数据库和分布式数据库
数据库·分布式·nosql
千层冷面10 小时前
RabbitMQ 发送者确认机制详解
分布式·rabbitmq·ruby
ChinaRainbowSea10 小时前
3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列
java·分布式·后端·rabbitmq·ruby·java-rabbitmq
敖正炀10 小时前
基于RocketMQ的可靠消息最终一致性分布式事务解决方案
分布式
一條狗12 小时前
随笔 20250402 分布式 ID 生成器 Snowflake 里面的坑
分布式