跳转到内容
Glossary 3 分钟阅读 1410 次浏览

会话保持(粘性会话)

理解代理中的会话保持(粘性会话)。了解这一关键技术如何让用户始终连接到同一台服务器,从而改善使用体验。

会话保持(粘性会话)

会话保持(session persistence),也称为粘性会话(sticky sessions),是代理使用的一种负载均衡技术,用于确保来自某个特定客户端的所有请求,在整个会话期间都被一致地路由到同一台后端服务器。

该机制解决的是有状态应用在负载均衡环境中面临的难题。许多 Web 应用会把会话相关数据(例如购物车内容、用户认证状态、个性化设置)保存在服务器上。如果没有会话保持,客户端的后续请求可能被负载均衡器分发到另一台后端服务器。若该服务器没有这个客户端的会话数据,会话状态就会丢失,从而导致报错、要求重新认证,或迫使客户端重新开始整个操作流程。粘性会话通过在初次建立连接后把客户端"粘"在某台服务器上来避免这一问题。

会话保持的工作原理

代理通过多种方法实现会话保持,核心思路都是先识别客户端,再把该客户端映射到某台特定的后端服务器。

客户端识别方法

IP Hash(源 IP 哈希)

该方法把客户端的 IP 地址作为决定后端服务器的键。代理对客户端 IP 地址做哈希运算,并用结果值从可用服务器池中挑选一台服务器。这样可以保证来自同一 IP 地址的所有请求都被送往同一台服务器。

  • 机制: hash(client_ip) % number_of_servers
  • 优点: 实现简单,不需要客户端 Cookie,也不需要修改应用。
  • 缺点:
    • 负载不均: 如果许多客户端共用同一个公网 IP(例如位于 NAT 网关或企业网络之后),那一台服务器就可能过载。
    • 服务器故障: 如果被分配的服务器发生故障,客户端会话就会丢失,后续请求将被路由到新的服务器(若哈希计算指向另一台服务器,或代理重新评估分配,粘性可能就此失效)。
    • 动态 IP: 使用动态 IP 地址的客户端,如果在会话中途 IP 发生变化,可能会丢失会话。

这是应用广泛且更灵活的方法。代理或应用会在客户端浏览器中写入一个 Cookie,其中包含代理用来把后续请求路由到正确后端服务器的信息。

代理本身在把 HTTP 响应发给客户端之前,向响应中注入一个 Cookie。该 Cookie 通常包含处理了首次请求的后端服务器的标识。在后续请求中,代理读取这个 Cookie,并把客户端引导到对应的服务器。

  • 机制: 代理拦截响应并添加 Set-Cookie: PROXY_SESSION_ID=server_id 头。在后续请求中,代理读取 Cookie: PROXY_SESSION_ID=server_id 并转发到 server_id
  • 优点: 无需修改应用,对应用完全透明。
  • 缺点:
    • 服务器故障: 如果被标识的服务器发生故障,客户端会话会丢失。代理必须把请求路由到新的服务器并更新 Cookie,否则客户端会遇到错误。
    • Cookie 管理: 要求客户端浏览器接受并回送 Cookie。

后端应用自行创建并管理会话 Cookie(例如 Java 应用的 JSESSIONID、PHP 的 PHPSESSID)。代理被配置为检查这个特定的应用 Cookie,并用它的值(或其中一部分)来确定目标后端服务器。代理可以对 Cookie 值做哈希,或从中提取内嵌的服务器标识。

  • 机制: 应用设置 Set-Cookie: APP_SESSION_ID=unique_session_id。代理读取 Cookie: APP_SESSION_ID=unique_session_id 并用该值映射到一台服务器。
  • 优点: 复用应用已有的会话管理,在识别唯一会话方面可能更可靠。
  • 缺点: 需要配置代理以理解应用特定的 Cookie 格式。服务器故障仍会导致会话丢失。

基于请求头的会话保持

这种方式不如 IP 哈希或基于 Cookie 的方法常见,它使用自定义 HTTP 头来携带会话或服务器标识信息。该请求头可以由客户端或中间代理插入。

  • 机制: 代理读取 X-Server-ID: server_123X-Client-Session: abcdef 并据此路由。
  • 优点: 在特定的 API 网关或微服务架构中可能很有用。
  • 缺点: 需要客户端或上游代理配合注入请求头。在没有客户端脚本的情况下,不适用于标准 Web 浏览器。

粘性会话方法对比

特性 IP Hash 代理生成的 Cookie 应用生成的 Cookie
客户端标识来源 客户端 IP 地址 代理注入的 Cookie 应用生成的 Cookie
是否需改应用 不需要 不需要 不需要(代理读取已有的应用 Cookie)
负载分布 可能不均(NAT/代理) 通常良好 通常良好
服务器故障 会话丢失,重新路由到新服务器 会话丢失,重新路由到新服务器 会话丢失,重新路由到新服务器
保持粒度 按 IP 地址 按浏览器会话(Cookie 过期) 按应用会话(Cookie 过期)
客户端要求 支持 Cookie 支持 Cookie
复杂度 中(代理负责 Cookie 管理) 中(需配置代理读取应用 Cookie)

会话保持为什么重要

  • 支持有状态应用: 对于把会话数据保存在服务器本地的应用(例如电商购物车、登录会话、多步骤表单)来说必不可少。
  • 用户体验: 避免会话丢失、重复登录或进度丢失,使体验更流畅。
  • 简化应用设计: 减少对复杂分布式会话管理方案(例如 Redis 或 Memcached 等外部会话存储)的需求,让应用保持更简单。

缺点与注意事项

  • 负载不均: 主要缺点。如果某台服务器承接了过多"粘住"的客户端,它可能过载,而其他服务器却处于闲置状态,这会抵消负载均衡带来的好处。
  • 服务器故障的影响: 如果一台持有活跃粘性会话的服务器发生故障,当前"粘"在该服务器上的所有客户端都会丢失会话数据,导致这些客户端体验下降或服务中断。
  • 扩展性挑战: 会让横向扩展更复杂。增删后端服务器可能打断已有的粘性会话,扩缩容时需要谨慎管理。
  • 资源消耗: 维护粘性映射关系(例如保存在查找表中)会占用代理的资源。
  • Cookie 管理开销: 对基于 Cookie 的方法而言,每个请求设置和读取 Cookie 都会带来少量开销。

配置示例

Nginx(开源版)

Nginx 可以通过 ip_hash 指令实现粘性会话,也可以借助商业模块或 nginx-sticky-module-ng 这类第三方模块,使用更高级的基于 Cookie 的方法。

IP Hash
http {
    upstream backend {
        ip_hash;
        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

在这个示例中,ip_hash; 确保来自同一客户端 IP 地址的请求,被一致地路由到 backend upstream 组中的同一台服务器。

如果使用 Nginx Plus 或兼容的第三方模块:

http {
    upstream backend {
        # 使用名为 "route" 的、由代理生成的 Cookie
        # 其值是 Nginx 分配给该服务器的路由 ID。
        sticky route $cookie_route;
        server backend1.example.com route=A;
        server backend2.example.com route=B;
        server backend3.example.com route=C;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

这里,sticky route $cookie_route; 让 Nginx 使用名为 route 的 Cookie 来实现会话保持。Nginx 会为该 Cookie 设置一个与最初选中的后端服务器相对应的值(ABC)。

此外,也可以用 sticky learn 来学习应用生成的 Cookie:

http {
    upstream backend {
        # 学习应用的 JSESSIONID Cookie
        sticky learn
               create=$upstream_cookie_JSESSIONID
               lookup=$cookie_JSESSIONID
               zone=client_sessions:1m; # 用于存放会话数据的共享内存区
        server backend1.example.com;
        server backend2.example.com;
        server backend3.example.com;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }
}

该配置会尝试学习后端应用设置的 JSESSIONID Cookie。如果该 Cookie 存在,Nginx 就用它的值把后续请求路由到最初设置它的那台服务器。

HAProxy

HAProxy 提供了强大的粘性会话能力,同时支持基于 IP 和基于 Cookie 的方法。

IP Hash(源 IP 哈希)
frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance source  # 使用客户端的 IP 地址
    server backend1 192.168.1.10:80 check
    server backend2 192.168.1.11:80 check
    server backend3 192.168.1.12:80 check

balance source 指令让 HAProxy 使用客户端的源 IP 地址进行负载均衡,从而实际提供粘性会话。

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    cookie SERVERID insert indirect nocache  # 插入名为 SERVERID 的 Cookie
    server backend1 192.168.1.10:80 cookie S1 check
    server backend2 192.168.1.11:80 cookie S2 check
    server backend3 192.168.1.12:80 cookie S3 check

这里,cookie SERVERID insert indirect nocache 告诉 HAProxy 在返回给客户端的响应中插入名为 SERVERID 的 Cookie。每台服务器行上的 cookie S1cookie S2 等指定了 HAProxy 为该服务器使用的值。随后 HAProxy 就用这个 Cookie 路由后续请求。indirect 表示只有在客户端还没有该 Cookie 时才设置它,nocache 则阻止缓存代理缓存带有 Set-Cookie 头的响应。

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    balance roundrobin
    appsession JSESSIONID len 52 timeout 3h # 使用 JSESSIONID Cookie
    server backend1 192.168.1.10:80 check
    server backend2 192.168.1.11:80 check
    server backend3 192.168.1.12:80 check

appsession JSESSIONID len 52 timeout 3h 指令配置 HAProxy 去查找名为 JSESSIONID 的 Cookie(在 Java 应用中很常见)。len 52 指定要考虑的会话 ID 长度,timeout 3h 设定当请求中不含该 Cookie 时,HAProxy 记住会话与服务器映射关系的时长。该方法要求应用设置 JSESSIONID Cookie。

已更新: 03.03.2026
返回分类

试用我们的代理

遍布 100+ 国家的 20,000+ 代理

support_agent
GProxy Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.