Envoy HTTP Cache 过滤器(Cache Filter)实战指南:配置、语义与存储后端
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
HTTP Cache 过滤器是 Envoy 原生实现 RFC 7234 缓存语义的 HTTP 过滤器,负责判定请求/响应是否可缓存、计算新鲜度(freshness lifetime)、并在内存或磁盘后端中存储与检索缓存对象。本文基于当前仓库的 cache_filter.rst 文档及其 API 定义与源码实现,完整讲解该过滤器的配置方法、请求/响应两侧的缓存判定规则、CacheConfig全部可用字段,以及SimpleHttpCache(内存)与FileSystemHttpCache(磁盘 LRU)两种内置存储后端的接入方式。读完本文,你将能够在一个真实 Envoy 静态配置中落地 HTTP 缓存,并理解缓存键、Vary 处理与过滤链语义背后的底层机制。
一、过滤器定位与基本约束
HTTP Cache 过滤器通过 HTTP 过滤器链(http_filters)接入,其 v3 配置类型为:
type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig完整字段定义见 cache.proto,存储后端配置见 SimpleHttpCacheConfig 与 FileSystemHttpCacheConfig。
使用该过滤器时需要明确以下几点约束:
- 不支持 virtual host 级别的配置:缓存配置只能挂在 HTTP 过滤器链上,不能针对某个虚拟主机单独开关。
- 过滤链语义特殊:当缓存启用后,可缓存(cacheable)的请求只会经过
upstream_http_filters链(即 Router.upstream_http_filters 中定义的过滤器),而不会经过普通过滤器链中位于 Cache 过滤器更上游(further upstream)的其他过滤器;不可缓存的请求则照常走完整的监听器过滤器链。 - 推荐放置位置:为了保证两条路径行为一致,官方文档明确建议——在监听器过滤器链中,Cache 过滤器更上游的位置只应放置 router 过滤器。换言之,典型排布是
... -> cache -> router,避免可缓存请求绕过中间过滤器而产生语义差异。
二、请求侧缓存语义
对于进入的 HTTP 请求,Cache 过滤器遵循以下判定逻辑(对应源码source/extensions/filters/http/cache/cacheability_utils.cc中的可缓存性判断):
- 尊重请求的
Cache-Control指令:例如请求携带Cache-Control: no-store时,该请求不会被缓存。唯一的例外是当CacheConfig.ignore_request_cache_control_header被置为true,此时过滤器将忽略这类指令。 - 不缓存 HEAD 请求:HTTP Cache 不会存储 HEAD 请求(HEAD 通常用于探测资源是否存在,其响应体为空且不应污染缓存条目)。
此外,从 cache.proto 的注释可以看出,默认情况下请求中的cache-control: no-cache或pragma: no-cache头会导致缓存即使命中也会回源(upstream)做校验(revalidation);设置ignore_request_cache_control_header = true可跳过这一行为。
三、响应侧缓存语义
对于上游返回的 HTTP 响应,Cache 过滤器只会存储满足以下全部条件的对象:
足以计算新鲜度生命周期的响应:过滤器的缓存判定以 RFC 7234 的新鲜度计算 为准——响应中必须携带足够的信息(如
Cache-Control: max-age、Expires或Last-Modified等)来推导 freshness lifetime,否则不缓存。尊重上游的
Cache-Control指令:例如状态码 200、携带Cache-Control: max-age=60且没有vary头的响应会被缓存;而携带no-store或no-cache等指令的响应则按指令语义处理。状态码在白名单内:只缓存以下状态码的响应:
200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, 451, 501可以看出白名单同时覆盖了成功类(200/203/204/206)、重定向类(300/301/308)以及错误类(404/405/410/414/451/501)响应,但排除了例如 500、502 等 5xx 服务端错误与 401/403 等需要按用户区分的响应。
Vary 头允许列表:
allowed_vary_headers(StringMatcher列表)在插入阶段充当白名单——如果响应的vary头中提及了任何未被allowed_vary_headers规则匹配的请求头名称,该响应将不被缓存;在查找阶段,它又决定了哪些请求头会被传给缓存存储后端用于命中判定。这在处理内容协商(如Accept-Encoding、Accept-Language)响应时尤其关键。
四、CacheConfig 配置字段详解
CacheConfig目前共 7 个字段位(#next-free-field: 7),其中部分已实现、部分在 proto 中标注为#not-implemented-hide:。逐项说明如下:
| 字段 | 类型 | 状态 | 说明 |
|---|---|---|---|
typed_config | google.protobuf.Any | ✅ 已实现 | 缓存存储后端的嵌套配置,通过扩展类别envoy.http.cache选定实现。除非disabled为 true,否则该字段为必填。 |
allowed_vary_headers | repeated StringMatcher | ✅ 已实现 | 定义允许的Vary头规则,同时控制插入白名单与查找时传给后端的请求头集合,详见上文第三节。 |
key_creator_params | KeyCreatorParams | ⚠️ 未实现 | 计划用于定制缓存键(是否排除 scheme/host、包含或排除哪些 query 参数),proto 中已预留结构但标注not-implemented-hide。 |
max_body_bytes | uint32 | ⚠️ 未实现 | 计划限制写入缓存的响应体大小,0 表示不限制(存储后端仍可能有自身限制)。 |
disabled | google.protobuf.BoolValue | ✅ 已实现 | 为 true 时过滤器退化为 no-op(空操作)。典型用途是与 ECDS(扩展配置发现服务)配合实现在线开关。 |
ignore_request_cache_control_header | bool | ✅ 已实现 | 默认 false;置 true 后忽略请求中的cache-control: no-cache与pragma: no-cache,避免每次命中都强制回源校验。 |
KeyCreatorParams子消息(当前仅用于预留)包含exclude_scheme、exclude_host、query_parameters_included与query_parameters_excluded四个字段,其中 query 参数匹配复用config.route.v3.QueryParameterMatcher,说明未来的缓存键定制将支持精确到 query 参数粒度的包含/排除。
五、架构与扩展点:HttpCache 接口
Envoy 的 HTTP 缓存体系被拆分为两个可独立扩展的层次:
- HTTP Cache 过滤器(扩展名
envoy.filters.http.cache,类别envoy.filters.http)——通过CacheConfig配置,负责实现 HTTP 缓存的全部语义判定(可缓存性、新鲜度、命中/未命中决策等)。 - 缓存存储后端(扩展类别
envoy.http.cache)——过滤器将对象的存储与检索委托给后端实现。后端通过CacheConfig.typed_config嵌套选定。
从源码结构看,存储抽象由 http_cache.h 中的HttpCache接口承担,各实现需要提供查找(lookup)与插入(insert)两条上下文路径;过滤器主体位于 cache_filter.cc,可缓存性判定与新鲜度逻辑集中在 cacheability_utils.cc,缓存键结构定义在 key.proto。
这种"语义内核 + 可插拔存储"的设计使得后端可以覆盖持久化、性能、分布式的任意组合点:从本地 RAM 缓存到全局分布式持久化缓存,既可以是完全自研的实现,也可以是本地或远端开源/商业缓存的包装器(wrapper/adaptor)。内置后端目前有两个:
SimpleHttpCacheConfig——内存实现,简单快速,适合单实例、小规模或测试场景;FileSystemHttpCacheConfig——磁盘持久化实现,默认采用 LRU(最近最少使用)逐出策略,适合跨重启保留缓存的大规模场景。
六、示例一:SimpleHttpCache(内存缓存)
以下配置来自仓库示例 http-cache-configuration.yaml,展示了在http_filters链中挂载内存缓存后端的完整写法:
static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 8000 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 codec_type: AUTO stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "*" routes: - match: prefix: "/service/1" route: cluster: service1 - match: prefix: "/service/2" route: cluster: service2 http_filters: - name: "envoy.filters.http.cache" typed_config: "@type": "type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig" typed_config: "@type": "type.googleapis.com/envoy.extensions.http.cache.simple_http_cache.v3.SimpleHttpCacheConfig" - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service1 type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service1 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: service1 port_value: 8000 - name: service2 type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service2 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: service2 port_value: 8000要点拆解:
- 过滤器名必须为
envoy.filters.http.cache,其typed_config类型为CacheConfig; CacheConfig.typed_config内再嵌套一层后端类型SimpleHttpCacheConfig。从 config.proto 可以看到该消息体目前是空的——内存后端无需任何额外参数;- 过滤器链中 Cache 位于router 之前,即上文第一节约束中推荐的排布;路由规则将
/service/1与/service/2分别指向两个 STRICT_DNS 集群。
七、示例二:FileSystemHttpCache(磁盘 LRU 缓存)
需要跨重启持久化缓存时,改用磁盘后端。以下配置来自仓库示例 http-cache-configuration-fs.yaml:
static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 8000 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 codec_type: AUTO stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: backend domains: - "*" routes: - match: prefix: "/service/1" route: cluster: service1 - match: prefix: "/service/2" route: cluster: service2 http_filters: - name: envoy.filters.http.cache typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig typed_config: "@type": type.googleapis.com/envoy.extensions.http.cache.file_system_http_cache.v3.FileSystemHttpCacheConfig manager_config: thread_pool: thread_count: 2 cache_path: /var/cache/envoy max_cache_size_bytes: 1073741824 - name: envoy.filters.http.router typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: service1 type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service1 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: service1 port_value: 8000 - name: service2 type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: service2 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: service2 port_value: 8000FileSystemHttpCacheConfig的字段在 file_system_http_cache.proto 中定义,完整参数语义如下:
| 字段 | 示例值 | 说明 |
|---|---|---|
manager_config | thread_pool.thread_count: 2 | 必填(validate 规则为required: true)。异步文件管理器配置,指定如何以异步方式使用文件系统。 |
cache_path | /var/cache/envoy | 缓存文件存储路径,同时充当缓存唯一标识:不同路由可共享同一cache_path的缓存,不同路径则各自独立。若多个CacheConfig使用相同cache_path,其余配置必须一致,且共享同一缓存实例。 |
max_cache_size_bytes | 1073741824(1 GiB) | 缓存总大小上限(按文件大小之和计,包含 header/trailer/元数据,不含文件系统开销与块填充)。达到上限触发逐出;不设置则仅受文件系统容量限制。 |
max_individual_cache_entry_size_bytes | 未设置 | 单个缓存条目大小上限,超限响应不缓存;不设置则无限制(当前标注未实现)。 |
max_cache_entry_count | 未设置 | 缓存条目数量上限,达到上限触发逐出;不设置则无限制。 |
cache_subdivisions | 未设置(默认 1) | 将缓存细分为多个子目录的数量。对于单目录大量 inode 会拖慢性能的文件系统,可设为sqrt(期望条目数)来提升性能;inode 友好的文件系统建议保持默认 1(当前标注未实现)。 |
evict_fraction | 未设置(默认 0) | 每次逐出时清理的比例。例如上限 10 MB、evict_fraction=0.2,超限后会逐出至 ≤ 8 MB;为 0 则只逐出至 ≤ 10 MB。逐出比例越大,逐出线程唤醒频率越低(省 CPU),但额外逐出的条目会带来更多缓存未命中(当前标注未实现)。 |
max_eviction_period | 未设置 | 两次逐出扫描的最大间隔。即使没有超限,只要距上次扫描超过该时长也会唤醒逐出线程做一次状态同步——这对多实例并行访问同一缓存很关键(例如两个实例各写入 10 MB 到 15 MB 上限的缓存,彼此不知情,需要同步扫描来发现超限)(当前标注未实现)。 |
min_eviction_period | 未设置 | 两次逐出扫描的最小间隔。可减少逐出抖动,代价是缓存可能在max_cache_size_bytes基础上多增长该时段内可写入的量。官方建议min_eviction_period与evict_fraction二选一(当前标注未实现)。 |
create_cache_path | 未设置(默认 false) | 为 true 且cache_path不存在时自动创建(含缺失的父目录),失败则拒绝配置;为 false 且路径不存在时直接拒绝配置(当前标注未实现)。 |
从源码目录 source/extensions/http/cache/file_system_http_cache/ 看,该后端由 file_system_http_cache.cc 实现主体,另含 cache_eviction_thread.cc(LRU 逐出线程)、cache_file_header.proto(磁盘文件头格式)与 stats.cc(统计指标),实现细节可进一步阅读该目录下的 DESIGN.md。内存后端则非常轻量,主体仅有 simple_http_cache.cc 与对应的头文件。
八、源码级补充:过滤器内部工作流
结合 source/extensions/filters/http/cache/ 目录中的源码组织,可以梳理出 Cache 过滤器内部的职责划分:
- cache_filter.cc / cache_filter.h:过滤器主逻辑,负责接入 HTTP 解码/编码链路,协调查找、命中回放与未命中回源;
- cacheability_utils.cc:集中实现请求/响应可缓存性判定,即上文第二、三节的规则所在;
- cache_headers_utils.cc:解析与计算
Cache-Control、Expires、Age等缓存相关头,支撑新鲜度计算; - http_cache.h:定义
HttpCache存储接口(查找/插入上下文),是接入新存储后端的扩展点; - cache_insert_queue.cc:负责插入操作的排队与合并,避免同一资源并发回源时重复写入;
- upstream_request.cc:管理未命中时向上游发起的请求及其与缓存插入的衔接;
- range_utils.cc:处理 Range 请求与缓存片段(206 响应)的切片逻辑;
- filter_state.h:在过滤器与后端之间传递缓存键、命中信息等状态。
这些文件共同印证了文档所述"HTTP Cache 过滤器实现了 HTTP 缓存语义的大部分复杂性"——语义判定全部集中在过滤器内,存储后端只需要实现纯粹的读写接口。
九、进一步学习路径
- 交互式沙箱:Envoy 提供了 Cache 过滤器逐步实操沙箱,可结合官方 Cache Sandbox 按步骤验证配置效果。
- V2 版本:仓库同时保留了缓存过滤器的 v2 变体,见 cache_v2 proto 及其存储后端 simple_http_cache v2 与 file_system_http_cache v2;新配置应优先使用 v3。
- API 现状提示:从 proto 标注可以看出,
KeyCreatorParams、max_body_bytes及FileSystemHttpCacheConfig的多个容量/逐出调优字段仍处于not-implemented-hide状态,生产使用时请以当前版本实际生效行为为准。
【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考