基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

Nginx 负载均衡如何实现会话保持(Session Persistence)?

在 Nginx 负载均衡中实现会话保持(Session Persistence,也称会话粘滞 Sticky Sessions),主要是为了确保来自同一个客户端的请求始终被转发到后端的同一台服务器上。这对于将 Session 数据存储在本地内存中的传统 Web 应用至关重要。

Nginx 提供了多种实现会话保持的方法,分为开源版原生支持第三方模块以及 Nginx Plus(商业版)专属功能

以下是具体的实现方式和配置指南:


方法一:基于源 IP 地址的哈希(ip_hash)—— 最常用(开源版)

这是 Nginx 开源版最简单、最常用的会话保持方法。它根据客户端的 IP 地址(IPv4 的前三个网段或整个 IPv6 地址)计算哈希值,然后将请求分配给特定的后端服务器。

配置示例:

plaintext
upstream backend_servers {
    ip_hash; # 开启 IP 哈希
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080 down; # 如果某台服务器下线,必须标记为 down,以保持哈希的稳定性
}

server {
    listen 80;
    location / {
        proxy_pass http://backend_servers;
    }
}
  • 优点: 配置极其简单,无需修改应用程序代码,Nginx 开源版原生支持。
  • 缺点:
    • NAT/代理问题: 如果大量用户通过同一个企业出口 IP(NAT)或代理服务器访问,会导致负载严重不均衡(所有请求全压在一台机器上)。
    • IP 变动: 如果用户在移动网络中切换基站,IP 发生变化,会话就会丢失。

方法二:基于自定义变量的哈希(hash)—— 更灵活(开源版)

如果 ip_hash 不适用,你可以根据请求中的特定变量(如 URL、特定的 Cookie 值、请求头等)来进行哈希路由。建议配合 consistent(一致性哈希)使用,这样在增减后端服务器时,能最大程度减少会话的重新分配。

场景:根据用户请求自带的 Session ID (如 JSESSIONID) 进行哈希。

配置示例:

plaintext
upstream backend_servers {
    # 根据 Cookie 中的 JSESSIONID 进行一致性哈希
    hash $cookie_JSESSIONID consistent; 
    
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    listen 80;
    location / {
        proxy_pass http://backend_servers;
    }
}
  • 优点: 解决了 NAT/代理带来的 IP 集中问题。
  • 缺点: 用户的第一次请求(还没有生成 Cookie 时)可能会被随机分配,需要应用程序配合处理首次响应。

方法三:Cookie 植入(Sticky Cookie)—— Nginx Plus 或 第三方模块

Nginx 会在用户的第一次请求响应中主动植入一个包含路由信息的 Cookie。用户后续请求带上这个 Cookie 时,Nginx 解析 Cookie 并将其转发到对应的服务器。

1. 使用 Nginx Plus(商业版)

Nginx Plus 原生支持 sticky 指令。

plaintext
upstream backend_servers {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
    
    # 商业版专属指令:设置名为 route_cookie 的 cookie,过期时间 1 小时
    sticky cookie route_cookie expires=1h domain=.example.com path=/;
}

2. 使用第三方模块(开源版替代方案)

如果你使用的是免费的 Nginx 开源版,可以通过编译第三方模块 nginx-sticky-module-ng 来实现类似功能。

配置示例(需先编译安装该模块):

plaintext
upstream backend_servers {
    sticky; # 开启基于 cookie 的粘滞
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}
  • 优点: 负载分配非常均衡,完全不受客户端 IP 变化或 NAT 的影响。
  • 缺点: 商业版需要付费;开源版需要自己重新编译 Nginx 添加模块,增加了运维复杂度。客户端必须支持并开启 Cookie。

方法四:基于请求路由(Sticky Route)

这种方法通常配合前端逻辑或特定的 URL 规则使用。比如在 URL 参数中明确指定要去哪台服务器。

配置示例(Nginx 控制通过 map 映射):

plaintext
# 假设前端通过在 Cookie 中附加后端服务器的标示符 route=server1
map $cookie_route $backend {
    default backend_servers; # 默认负载均衡池
    server1 192.168.1.10:8080;
    server2 192.168.1.11:8080;
}

upstream backend_servers {
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

server {
    listen 80;
    location / {
        # 直接代理到 map 映射出来的具体服务器
        proxy_pass http://$backend; 
    }
}

💡 架构建议:分布式 Session(无状态化)

虽然 Nginx 提供了上述多种会话保持机制,但在现代互联网架构(特别是微服务和云原生架构)中,强烈不建议过度依赖负载均衡器的会话保持功能

为什么?

  1. 如果后端某台服务器宕机,分配到该服务器上的所有用户 Session 将全部丢失,导致用户被踢下线。
  2. 不利于弹性伸缩(动态增加/减少服务器会打乱哈希规则)。

最佳实践方案:将后端服务器做到“无状态(Stateless)”

  • Session 集中存储: 使用 RedisMemcached 或数据库来统一存储 Session 数据。
  • Token 机制: 使用 JWT (JSON Web Token),将状态保存在客户端,服务端只需解密验证。
  • 效果: 无论 Nginx 将请求转发给后端的哪台服务器,该服务器都可以从 Redis 中读取到用户的 Session 数据。此时 Nginx 只需要使用默认的轮询(Round Robin)最少连接(least_conn)算法,即可实现完美的负载均衡和高可用。

总结

  • 要求配置最简单,不在乎 NAT 问题的:用 ip_hash
  • 有 NAT 问题,但不想重新编译 Nginx 的:用基于 Cookie 的 hash $cookie_jsessionid consistent
  • 愿意折腾编译 Nginx 开源版的:用第三方模块 nginx-sticky-module
  • 有钱且追求官方支持的:用 Nginx Plus (sticky cookie)
  • 终极推荐架构:抛弃 Nginx 会话保持,改用 Redis 集中存储 SessionJWT
00:00
00:00