Envoy 本地回复(Local Reply)定制:从响应改写、格式化到源码级原理
发布时间:2026/9/13 15:40:13 作者:尧图编辑部 阅读量:1,286
定制:从响应改写、格式化到源码级原理)
Envoy 本地回复Local Reply定制从响应改写、格式化到源码级原理【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本地回复Local Reply是指由 Envoy 自身直接生成并返回给下游客户端的响应例如路由未匹配、上游连接失败、超时或请求被拒绝时的错误响应。本文以 Envoy 仓库中的官方文档 local_reply.rst 为主体结合 HTTP 连接管理器HCM 的源码与 proto 定义系统讲解如何通过LocalReplyConfig定制本地回复的内容、状态码、响应头与响应体格式帮助读者在网关/代理场景下构建统一、规范、可运维的错误响应体系。读完本文你将掌握mappers匹配规则、四条改写规则的执行顺序、text_format/json_format两种响应体格式以及%LOCAL_REPLY_BODY%、%RESPONSE_CODE%等命令操作符command operator的取值时机差异。本地回复与 LocalReplyConfig 概述Envoy 的 HTTP 连接管理器在向下游返回由 Envoy 自己生成的响应而非来自上游的响应时会经过一个可配置的本地回复改写环节。官方文档明确指出HCM 支持对这类本地回复进行两类修改内容修改content modification改写响应状态码、添加/覆盖/追加响应头、替换响应体格式修改format modification定制响应体的内容类型与编排格式。这两类修改统一由 http_connection_manager.proto 中的LocalReplyConfig消息承载。其核心结构proto 第 1154–1222 行如下LocalReplyConfig.mappersrepeatedResponseMapper一组按顺序检查的映射器用于过滤并改写本地响应LocalReplyConfig.body_formatconfig.core.v3.SubstitutionFormatString全局的响应体格式化配置ResponseMapper.filterconfig.accesslog.v3.AccessLogFilter必填决定该 mapper 是否生效的过滤器ResponseMapper.status_codeUInt32Value改写后的状态码校验范围为[200, 600)ResponseMapper.bodyDataSource新的本地回复响应体文本会进入%LOCAL_REPLY_BODY%命令操作符ResponseMapper.headers_to_addrepeatedHeaderValueOption最多 1000 项为本地回复添加的响应头ResponseMapper.body_format_overrideSubstitutionFormatString仅当该 mapper 命中时生效的响应体格式覆盖项。从实现看这一机制由 source/common/local_reply/local_reply.cc 中的LocalReplyImpl类承载HCM 配置解析阶段通过LocalReply::Factory::create(config.local_reply_config(), context)见 config.cc完成构建。LocalReply::rewrite()接口见 local_reply.h接收请求头、响应头、流信息以及待改写的code、body、content_type引用是整个改写流程的入口。Local reply 内容修改mappers 匹配与四条改写规则内容修改的核心是mappers列表。每个 mapper 必须携带一个过滤器filterEnvoy 会按配置顺序逐个检查直到第一个匹配的 mapper 生效。一旦某个 mapper 匹配成功其全部改写规则都会按以下固定顺序应用这是官方文档明确给出的顺序也是理解%RESPONSE_CODE%取值差异的关键body—— 设置静态响应体文本headers_to_add—— 求值并添加响应头此时%RESPONSE_CODE%等替换变量解析为原始响应码status_code—— 改写响应码同时更新响应头Status与 stream infobody_format_override或回退到全局body_format—— 格式化响应体此时%RESPONSE_CODE%解析为改写后的响应码。由于上述顺序%RESPONSE_CODE%在headers_to_add原始码与body_format_override改写码中可能得到不同的值。如果你需要在响应体中引用原始响应码官方文档给出的推荐做法是先用headers_to_add把原始码捕获进一个响应头再在 body 格式中用%RESP(header-name)%引用它。该顺序在源码 local_reply.cc 的ResponseMapper::matchAndRewrite()中逐条实现先求值过滤器命中后依次执行 body 赋值、header_parser_-evaluateHeaders()求值响应头、status_code_改写同时response_headers.setStatus()与stream_info.setResponseCode()最后把命中 mapper 的body_formatter_提升为最终格式化器。示例一状态码与响应体改写官方文档给出的第一个示例将状态码 400 的本地回复改写为 401并把响应体替换为 not allowed同时追加一个foo: bar响应头mappers: - filter: status_code_filter: comparison: op: EQ value: default_value: 400 runtime_key: key_b headers_to_add: - header: key: foo value: bar append_action: OVERWRITE_IF_EXISTS_OR_ADD status_code: 401 body: inline_string: not allowed几点实战说明filter.status_code_filter是 accesslog 过滤器体系中的状态码过滤器op: EQ表示相等匹配value.default_value给出静态默认值 400runtime_key允许通过运行时runtime动态覆盖比较值append_action: OVERWRITE_IF_EXISTS_OR_ADD表示若响应头foo已存在则覆盖、否则新增这是最常见的幂等写法body.inline_string表示直接以内联字符串作为响应体DataSource也支持filename等来源但配置期解析Config::DataSource::read(..., true, ...)要求静态可读由于执行顺序headers_to_add阶段%RESPONSE_CODE%解析为 400而 body 格式化阶段为 401。该行为在 test/common/local_reply/local_reply_test.cc 中有大量对应用例可用于验证过滤器匹配、状态码改写与响应体替换的实际效果。Local reply 格式修改body_format 与 body_format_override格式修改涉及两个body_format字段官方文档特别强调了二者的生效优先级LocalReplyConfig.body_format全局配置当没有任何 mapper 命中或命中的 mapper 未指定自己的body_format_override时使用ResponseMapper.body_format_overridemapper 级配置仅当该 mapper 命中时才使用且优先于全局配置。未指定任何body_format时默认内容类型为text/plain。两种格式类型都通过config.core.v3.SubstitutionFormatString表达定义见 substitution_format_string.proto其oneof format支持text_format使用命令操作符拼接纯文本字符串proto 中标注已废弃推荐迁移到text_format_source通过DataSource.inline_string表达但文档示例仍以text_format演示语义一致json_format使用google.protobuf.Struct表达 JSON 结构值为字符串、数字或布尔值部分命令操作符如FILTER_STATE、DYNAMIC_METADATA还可生成嵌套 JSON。内容类型content_typeSubstitutionFormatString.content_type可进一步定制响应头Content-Type。默认行为在源码 local_reply.cc 中实现显式指定content_type时以其为准未指定时json_format默认application/json其余text 格式默认text/plain。示例二mapper 级 override 与全局 body_format 并存官方文档给出的第二个示例同时展示了两种字段mappers: - filter: status_code_filter: comparison: op: EQ value: default_value: 400 runtime_key: key_b status_code: 401 body_format_override: text_format: h1%LOCAL_REPLY_BODY% %REQ(:path)%/h1 content_type: text/html; charsetUTF-8 - filter: status_code_filter: comparison: op: EQ value: default_value: 500 runtime_key: key_b status_code: 501 body_format: text_format: %LOCAL_REPLY_BODY% %RESPONSE_CODE%行为拆解第一个 mapper 匹配status_code 400时状态码改写为 401响应体按text_format拼接%LOCAL_REPLY_BODY%原始本地回复体与请求头:path输出形如h1upstream connect error /foo/h1Content-Type为text/html; charsetUTF-8第二个 mapper 匹配status_code 500时状态码改写为 501但未指定body_format_override因此回退到全局body_format输出形如upstream connect error 501注意此处%RESPONSE_CODE%为改写后的 501当两个 mapper 都未命中例如 503 场景时状态码与 body 均保持原始值仅按全局body_format重新编排输出形如upstream connect error 503。该命中 mapper 优先、全局兜底的语义在 local_reply.cc 的LocalReplyImpl::rewrite()中实现遍历mappers_一旦matchAndRewrite()返回 true 即break若最终final_formatter为空则回退到全局body_formatter_。使用命令操作符定制响应体SubstitutionFormatString的格式串支持与 access log 相同的命令操作符体系。官方 proto 注释http_connection_manager.proto给出了两组可直接套用的示例。text_format纯文本示例text_format: %LOCAL_REPLY_BODY%:%RESPONSE_CODE%:path%REQ(:path)%\n对于本地回复体为 upstream connect error、响应码 503、请求路径/foo的请求输出upstream connect error:503:path/foojson_format结构化示例json_format: status: %RESPONSE_CODE% message: %LOCAL_REPLY_BODY% path: %REQ(:path)%同样的请求将输出{ status: 503, message: upstream connect error, path: /foo }常用操作符速览操作符含义%LOCAL_REPLY_BODY%本地回复的响应体文本可被 mapper 的body规则改写%RESPONSE_CODE%响应码取值时机取决于所处改写阶段见上文顺序%REQ(:path)%请求头:path的值%RESP(header-name)%已设置响应头的值可用于搬运原始响应码值得注意的实现细节在 local_reply.cc 中rewrite()一开始就会先把当前code同步到response_headers的Status与stream_info.setResponseCode()——注释解释了原因StatusCodeFilter从 stream info 读取响应码而%RESP(:status)%从响应头的Status读取。这也保证了status_code_filter能基于正确的响应码做匹配。本地回复改写的完整执行流程综合官方文档与源码一次本地回复的改写生命周期可归纳为HCM 需要生成本地回复如路由失败、上游错误等得到原始code、body与content_type调用LocalReply::rewrite()先将原始code写入响应头Status与 stream info保证后续过滤器与操作符取值正确按配置顺序遍历mappers_第一个匹配的 mapper 依次应用body→headers_to_add→status_code并指定其body_format_override为最终格式化器若没有任何 mapper 命中或命中 mapper 未提供 override则使用全局body_format两者都未配置时使用默认纯文本格式化器最终格式化器执行format()若存在 formatter 则以格式化结果替换body并设置content_type之后响应经 HCM 返回下游。工厂方法Factory::create()与createDefault()local_reply.h分别用于从 proto 配置构建带 FactoryContext可解析 runtime_key、读取 DataSource 等与无上下文时的默认空配置构建后者被 admin 等不涉及完整 HCM 配置的模块复用。常见应用场景与配置建议基于上述机制本地回复定制最典型的落地场景包括统一错误响应格式把分散的 Envoy 内置错误文案统一为 JSON 结构json_format配合%LOCAL_REPLY_BODY%、%RESPONSE_CODE%、%REQ(:path)%等便于客户端与监控系统解析语义化状态码映射如示例中将 400 → 401、500 → 501或把内部错误码映射为对客户端更友好的状态码补充诊断信息通过headers_to_add注入x-envoy-*调试头或错误追踪标识按条件差异化响应多个mappers依序匹配可实现状态码 路径等多维条件的组合定制runtime_key则支持不改配置即可热切换比较阈值。配置时建议注意filter为必填字段proto 校验validate.rules强制要求status_code合法范围为 200–599headers_to_add单项上限 1000body使用DataSource在配置解析期读取静态内联字符串inline_string是最稳妥的写法多个 mapper 之间的顺序就是优先级顺序需把更具体的规则放在前面。如需进一步验证行为或深入阅读可参考官方文档docs/root/configuration/http/http_conn_man/local_reply.rstProto 定义api/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto核心实现source/common/local_reply/local_reply.cc、source/common/local_reply/local_reply.h单元测试test/common/local_reply/local_reply_test.cc格式串定义api/envoy/config/core/v3/substitution_format_string.proto【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考