news 2026/9/13 2:24:37

Envoy Stateful Session 过滤器全解析:基于可扩展会话状态实现强会话粘性

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Envoy Stateful Session 过滤器全解析:基于可扩展会话状态实现强会话粘性

Envoy Stateful Session 过滤器全解析:基于可扩展会话状态实现强会话粘性

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

导读

Envoy 的 Stateful Session(有状态会话)HTTP 过滤器允许基于可扩展的会话状态(Session State)覆盖最终选择的上游主机,并将最终选中的上游主机写回会话状态,从而在不依赖哈希负载均衡的前提下实现"强"会话粘性(strong stickiness)。本文以官方文档 stateful_session_filter.rst 为主线,结合仓库中的 proto 定义、示例配置与 C++ 源码实现,系统讲解该过滤器的工作原理、Cookie/Header 两种会话状态扩展的配置方法、严格模式(strict)的行为差异以及统计指标体系,帮助你在一线接入网关或网格代理中正确启用并运维这一能力。

什么是 Stateful Session:从弱粘性到强粘性

会话粘性(Session Stickiness)的核心诉求是:属于同一会话(Session)的请求,应被一致地路由到同一个上游主机。Envoy 中传统的实现方式是哈希负载均衡(Hash-based Load Balancing,如envoy.lb_policy.maglevenvoy.lb_policy.ring_hash)。但哈希粘性被认为是"弱"粘性——因为粘性建立在主机集合(Host Set)的哈希映射上,一旦上游主机集合发生变化(扩缩容、摘除等),原有会话就可能被重新映射到不同的主机。

Stateful Session 过滤器实现的是强粘性:会话状态中显式记录"该会话应去往哪个上游主机",后续请求直接按记录的主机路由。原文档指出,它主要面向两类场景:

  • 需要更稳定粘性的场景:例如某主机已被标记为 degraded(降级),但仍希望现有会话继续路由到该主机(此时哈希负载均衡会因为健康检查或主机集合变化而改变映射)。
  • 使用非哈希负载均衡器却仍需要粘性的场景:例如使用 Random、Round Robin 等负载均衡策略时,新会话按负载均衡结果选择上游主机,而既有会话固定路由到会话记录的主机。

从源码看,该过滤器通过decoder_callbacks_->setUpstreamOverrideHost(...)将会话解析出的上游地址注入负载均衡上下文,实现对负载均衡结果的覆盖(override),且覆盖优先级高于负载均衡本身的选择(见 stateful_session.cc)。

安全与可靠性提醒

原文档特别给出note警告:Stateful Session 可能导致上游之间负载不均衡,并可能允许外部参与者将请求定向到特定的上游主机。因此在启用该功能前,运维人员必须仔细评估其安全性与可靠性影响——它本质上是把路由决策的"一部分控制权"交给了会话载体(Cookie/Header)中的内容。

配置方式

该过滤器通过 HTTP Connection Manager 下的 HTTP 过滤器链装配,必须使用类型 URL:

type.googleapis.com/envoy.extensions.filters.http.stateful_session.v3.StatefulSession

对应的完整 proto 定义为 stateful_session.proto,核心配置字段如下:

字段类型默认值说明
session_stateconfig.core.v3.TypedExtensionConfig必填(语义上最核心)指定会话状态实现,用于存取分配给会话的上游主机地址
strictboolfalse是否严格路由到请求的目标主机。true时若目标不存在则按status_on_strict_destination_not_found返回;若目标存在但不健康则始终返回503false时回退到普通负载均衡
stat_prefixstring可选统计前缀;为空则不输出任何统计
status_on_strict_destination_not_founduint32503严格模式下目标主机不在可用端点集合中时返回的 HTTP 状态码;strictfalse时该字段被忽略;设为0或不设置时取默认503

此外还提供StatefulSessionPerRoute用于路由级覆盖(见 stateful_session.proto),它通过oneof override支持两种形式:

  • disabled:在特定 vhost 或 route 上显式禁用该过滤器(多个 per-filter-config 同时存在时取最具体的配置);
  • stateful_session:为路由提供一份独立的StatefulSession配置(可通过 RDS 下发)。

工作原理:会话状态扩展点

该过滤器最关键的配置项是session_state这个可扩展会话状态(Extensible Session State)。处理请求时,过滤器会基于请求检索对应的会话及其上游主机,检索结果将影响最终的负载均衡结果;若未找到既有会话,则创建会话用于存储选中的上游主机。需要强调的是,这里的"会话"是抽象概念,具体的存储细节完全取决于会话状态实现(例如存在 Cookie 里,还是存在响应 Header 里)。

结合 stateful_session.cc 的源码,可梳理出完整的请求/响应处理链路:

  1. 请求阶段(decodeHeaders:解析出最具体的 per-route 配置(若路由级禁用则直接Continue);通过session_state中指定的工厂创建会话状态实例;若会话中解析出上游地址(upstreamAddress()有值),则调用setUpstreamOverrideHost注入覆盖主机、strict 开关与严格模式失败状态码。
  2. 响应阶段(encodeHeaders:若请求阶段没有会话状态,且过滤器处于激活状态(未被 per-route 禁用)、请求已到达上游,则累加no_session统计;若有会话状态且拿到了最终上游主机,则调用会话状态的onUpdate(host_address, headers)——若最终主机与会话记录不一致(说明覆盖失败发生了回退),则根据 strict 模式标记failed_openfailed_closed;若一致则标记routed

会话状态是通过扩展工厂(SessionStateFactory)按session_state.name动态查找并实例化的,具体机制在 stateful_session.cc。若配置中完全未指定session_state,则会使用EmptySessionStateFactory(返回空会话),此时过滤器不产生任何粘性行为(见 stateful_session.cc)。

官方示例:Cookie 型会话状态

目前官方支持三类会话状态扩展,其中文档详细讲解的是Cookie 型envoy.http.stateful_session.cookie)与Header 型envoy.http.stateful_session.header)。以下为文档内嵌的 Cookie 型完整示例(原样取自 stateful-cookie-session.yaml):

static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http access_log: - name: envoy.access_loggers.stdout typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: cluster: service1 http_filters: - name: envoy.filters.http.stateful_session typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.stateful_session.v3.StatefulSession session_state: name: envoy.http.stateful_session.cookie typed_config: "@type": type.googleapis.com/envoy.extensions.http.stateful_session.cookie.v3.CookieBasedSessionState cookie: name: global-session-cookie path: /path ttl: 120s - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service1 load_assignment: cluster_name: service1 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 8080

该配置的行为:Cookie 型会话状态从名为global-session-cookie的 Cookie 中解析当前会话应覆盖的上游主机;若该主机确实存在于上游集群中,请求将被路由到该主机。若请求没有有效 Cookie,则负载均衡器正常挑选一个新上游主机,并在响应阶段把选中的上游主机地址写入名为global-session-cookie的 Cookie(通过Set-Cookie响应头)。

Cookie 值格式与生命周期细节

从 cookie.proto 可以看到,Cookie 型扩展把负载均衡器选中的上游地址编码进Set-Cookie响应头;收到新请求时按 Cookie 名解析上游地址,若地址对应有效上游主机则优先选择。其编码形式为 Base64(文档示例中sticky-host="MS4yLjMuNDo4MA=="1.2.3.4:80的 Base64 表示)。需要说明的是,根据 cookie.cc 的当前实现,写入 Cookie 的内容实际是将上游地址(连同可选的过期时间expires)序列化后的消息再做 Base64 编码,并且仅在最终选择的主机与会话记录不一致(或原会话不存在)时才更新 Cookie。

stateful_session_filter.rst 中演示的cookie字段支持:

  • name:Cookie 名(示例为global-session-cookie);源码 cookie.cc 会校验其非空,否则抛异常;
  • path:Cookie 的路径属性(示例为/path),同时它还被用作请求路径匹配器:空路径或/匹配所有请求;以/结尾的路径前缀匹配;否则要求请求路径与 Cookie 路径相同,或紧随其后是/?#之一(见 cookie.cc);
  • ttl:Cookie 有效期(示例为120s),源码中ttl为 0 时不写入expires(会话永不过期,见 cookie.cc);此外还支持attributes附加 Cookie 属性。

官方示例:Header 型会话状态

Header 型扩展的配置同样内嵌于文档(原样取自 stateful-header-session.yaml):

static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 10000 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http access_log: - name: envoy.access_loggers.stdout typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog route_config: name: local_route virtual_hosts: - name: local_service domains: ["*"] routes: - match: prefix: "/" route: cluster: service1 http_filters: - name: envoy.filters.http.stateful_session typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.stateful_session.v3.StatefulSession session_state: name: envoy.http.stateful_session.header typed_config: "@type": type.googleapis.com/envoy.extensions.http.stateful_session.header.v3.HeaderBasedSessionState name: session-header - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service1 load_assignment: cluster_name: service1 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 8080

Header 型配置仅需一个字段name(示例为session-header),用于从下游请求中读取会话值、并在响应中生成同名响应头。其 proto 定义见 header.proto:请求中若携带形如session-header: "MS4yLjMuNDo4MA=="(即1.2.3.4:80的 Base64)的头部,Envoy 会优先选择1.2.3.4:80作为上游;处理上游响应时,若最终选择的主机与会话记录不一致,则把新选中主机地址 Base64 编码后写回session-header响应头(见 header.cc)。源码同样会校验name非空(header.cc)。

使用 Header 型实现时需要注意两点(原文档note原文):

  • Header 型实现假定客户端会使用最后一次提供的会话头值,并在后续每个请求中携带它
  • 若需要基于路径匹配来启用/禁用粘性,应使用StatefulSessionPerRoute进行路由级配置。

扩展生态:Envelope 型会话状态

除文档主讲的 Cookie/Header 两种外,仓库还提供了第三种官方扩展——Envelope 型envoy.http.stateful_session.envelope,见 envelope.proto)。它适用于"会话上下文由上游服务器初始化"的场景:上游在会话首个响应中生成会话上下文(如会话 ID 头或 Cookie),客户端在后续请求中原样携带。

其处理逻辑为:处理上游响应时,若响应中包含会话上下文(无论新旧),Envoy 会把该上下文与当前上游主机"拼接"成新的会话上下文;处理下游请求时,若请求包含会话上下文,则从中剥离出上游主机。Header 模式下编码格式形如:

session-header: "MS4yLjMuNDo4MAo=;UV:eHh4eHh4Cg==" # base64(1.2.3.4:80);UV:base64(xxxxxx)

其中UV(upstream value)段用于保存上游原始头值,Envoy 据此把上游地址与会话值解耦,回程时用UV段还原原始请求头。

统计指标(Statistics)

该过滤器在http.<stat_prefix>.stateful_session.命名空间下输出统计,其中第一个stat_prefix来自所属 HTTP Connection Manager 的stat_prefix配置。注意两条关键规则(原文档note):

  • 过滤器自身未配置stat_prefix时,不输出任何统计
  • 若配置了过滤器级stat_prefix,会在stateful_session之后追加一段以区分多个实例,例如http.<stat_prefix>.stateful_session.my_prefix.routed(源码中统计前缀拼接逻辑见 stateful_session.cc);
  • per-route 配置覆盖不支持统计——即使在 per-route 的StatefulSession中设置了stat_prefix也不会输出统计(对应实现见 stateful_session.cc,per-route 配置传入空前缀)。

支持的统计指标定义于 stateful_session.h,汇总如下:

名称类型说明
routedCounter尝试了会话覆盖且成功应用、最终选中的上游与会话请求目标一致的请求总数
failed_openCounter尝试覆盖但目标不可用、随后按默认负载均衡继续处理(strictfalse)的请求总数
failed_closedCounter尝试覆盖但目标不可用、以503关闭请求(stricttrue)的请求总数
no_sessionCounter过滤器激活但请求到达上游时没有会话状态的请求总数,包括无会话 Cookie/Header 或会话提取失败的情况;不包含过滤器在 per-route 被显式禁用的请求

小结与实践建议

Stateful Session 过滤器把"粘性"从负载均衡算法中彻底解耦出来:哈希负载均衡的弱粘性依赖主机集合的稳定性,而本过滤器通过session_state扩展在请求中显式携带目标上游主机,实现了覆盖优先级高于负载均衡的强粘性。实践时的关键决策点包括:

  • 会话载体选择:Cookie 型适合浏览器类客户端(自动携带、TTL 可控、支持路径匹配);Header 型适合 API/微服务间调用(需客户端配合回传头值);Envelope 型适合上游已自带会话上下文的场景。
  • strict 模式取舍:追求粘性可靠性可开启strict(目标不可达时 fail-closed 返回503),追求可用性则保持false(fallback 到负载均衡,但会积累failed_open统计)。
  • 监控与安全:务必为过滤器配置stat_prefix以获得routed/failed_open/failed_closed/no_session四类指标;同时牢记文档警告——该特性可能造成上游负载不均、并允许外部控制请求目标,需结合网络隔离与信任边界审慎启用。

如需深入验证上述行为,可直接查阅本文引用的 proto 定义、示例配置与源码文件,并参考仓库中的实现与测试代码进行二次确认。

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 2:22:46

kohya_ss LoRA 训练完整指南:从零安装到练出第一个角色模型

kohya_ss LoRA 训练完整指南&#xff1a;从零安装到练出第一个角色模型 【免费下载链接】kohya_ss 项目地址: https://gitcode.com/GitHub_Trending/ko/kohya_ss kohya_ss 解决一件事&#xff1a;不碰命令行&#xff0c;也能给 Stable Diffusion 训练自己的 LoRA 和自定…

作者头像 李华
网站建设 2026/9/13 2:16:22

深入理解HOOK机制:从消息钩子到API Hook的底层原理与实战

HOOK这个词&#xff0c;搞Windows开发的人天天挂嘴边&#xff0c;做Web开发的人也经常听到&#xff0c;可你真要让人一句话说清楚它是什么、能干什么&#xff0c;能一口气讲明白的人真不多。我直接上两段能跑的代码&#xff0c;带你把HOOK的底层逻辑、实现方式和坑点一次性捋顺…

作者头像 李华
网站建设 2026/9/13 2:15:42

职场短剧AI配音选型指南:小云雀与OiiOii核心差异解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 2:14:50

Ubuntu下JDK安装全指南:三种方式与环境变量配置详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 2:13:07

FreeCAD 新手指南:从一张草图做出真正的 3D 参数化零件

FreeCAD 新手指南&#xff1a;从一张草图做出真正的 3D 参数化零件 【免费下载链接】FreeCAD Official source code of FreeCAD, a free and opensource multiplatform 3D parametric modeler. 项目地址: https://gitcode.com/GitHub_Trending/fr/FreeCAD 如果你一直想上…

作者头像 李华