ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Envoy Json-To-Metadata 过滤器实战:将 JSON Body 动态提取为 Metadata 驱动负载均衡与限流

Envoy Json-To-Metadata 过滤器实战:将 JSON Body 动态提取为 Metadata 驱动负载均衡与限流 Envoy Json-To-Metadata 过滤器实战将 JSON Body 动态提取为 Metadata 驱动负载均衡与限流【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoyJson-To-Metadata 是 Envoy 提供的一个 HTTP 过滤器HTTP filter它把请求或响应的 JSON 请求体解析后按规则提取指定 JSON 属性的值并写入 stream 的动态元数据dynamic metadata。这些元数据随后可以被负载均衡如 subset load balancing、访问日志、限流rate limit等模块消费从而让 Envoy 在不解业务代码的前提下根据请求体内容做出转发决策。本文以 Envoy 仓库中 json_to_metadata_filter.rst 为骨架结合 v3 API 定义 与 filter.cc 实现源码完整讲解该过滤器的配置模型、规则匹配语义、请求/响应处理流程、Per-route 配置与统计指标并给出可直接运行的完整 YAML 示例。过滤器是什么从 JSON Body 到动态元数据该过滤器应使用类型 URLtype.googleapis.com/envoy.extensions.filters.http.json_to_metadata.v3.JsonToMetadata进行配置对应的 protobuf 消息定义位于 json_to_metadata.proto扩展名为envoy.filters.http.json_to_metadata见 extensions_metadata.yaml。过滤器的工作机制可以概括为三步规则匹配过滤器配置一组规则rules每条规则包含若干选择器selectors目前仅支持 key 选择器并针对 JSON 属性存在on_present、缺失on_missing、解析失败on_error三种情况分别定义元数据写入行为。写入元数据当规则被触发时把 JSON 中提取的值或配置中指定的兜底值与指定的 key 一起写入动态元数据属性缺失或出错时则使用规则中指定的值。下游消费写入的动态元数据可用于负载均衡决策如 subset 负载均衡的匹配键、从访问日志中读取、供限流动作匹配等。原文档给出的一个典型使用场景是动态匹配请求的指定 JSON 属性与限流规则——先从请求 JSON 中提取某个 key 的值并附加为动态元数据随后限流过滤器基于该元数据匹配对应的限流 action。一个值得注意的行为是JSON 转元数据过滤器在拿到完整 payload或遇到错误之前会停止向后续过滤器迭代stops iterating即它需要缓冲完整 body 才能解析出 JSON 属性这一点在处理流程章节会结合源码展开。配置模型MatchRules / Rule / Selector / KeyValuePair 逐层解析过滤器顶层配置只有两个字段request_rules和response_rules二者至少提供一个proto 注释明确要求 At least one of request_rules and response_rules must be provided.。request_rules用于匹配请求 JSON bodyresponse_rules用于匹配响应 JSON body。两者都是MatchRules消息。MatchRules规则容器与 Content-Type 控制message MatchRules { // 要应用的规则列表至少 1 条 repeated Rule rules 1 [(validate.rules).repeated {min_items: 1}]; // 允许执行 json to metadata 转换的 content-type默认 {application/json} repeated string allow_content_types 2 [(validate.rules).repeated {items {string {min_len: 1}}}]; // 是否允许空/缺失的 content-type默认 false bool allow_empty_content_type 3; // 通过正则匹配 content-type可与 allow_content_types 并行使用 type.matcher.v3.RegexMatcher allow_content_types_regex 4; }allow_content_types默认值为{application/json}。在 filter.cc 的generateAllowContentTypes中可以看到若该字段为空会回退到Http::Headers::get().ContentTypeValues.Json即application/json。allow_empty_content_type默认false。当请求/响应头中 content-type 为空或缺失时只有此项为true才继续处理否则走 error 分支。allow_content_types_regex通过RegexMatcher允许一组满足正则的 content-type与allow_content_types是或的关系见 filter.cc 中requestContentTypeAllowed/responseContentTypeAllowed的实现。Rule选择器与三种触发分支message Rule { // 选择器链例如匹配 {foo: {bar: 1}, bar: 2} 中的 1 // selectors: // - key: foo // - key: bar repeated Selector selectors 1 [(validate.rules).repeated {min_items: 1}]; // 属性存在时应用的元数据 KeyValuePair on_present 2; // 属性缺失时应用的元数据value 必须设置 KeyValuePair on_missing 3; // body 过大、解析失败或 content-type 不匹配时应用的元数据value 必须设置 KeyValuePair on_error 4; }selectors是可嵌套的 key 路径数组中的第一个 key 是根节点属性后续 key 是逐级向下的子属性。例如selectors: [{key: foo}, {key: bar}]匹配{foo: {bar: 1}}中的1。在 filter.cc 的Rule构造函数中只提取每个 selector 的key字段Support key selectors only说明当前版本仅支持 key 类型选择器。校验规则在Rule::createfilter.cc中强制执行on_present与on_missing至少设置一个否则报错 neitheron_presentnoron_missingset设置了on_missing就必须同时设置 value否则报错 cannot specify on_missing rule with empty value设置了on_error就必须同时设置 value否则报错 cannot specify on_error rule with empty value。KeyValuePair元数据的命名空间、key、值与类型message KeyValuePair { // 元数据命名空间为空时使用过滤器自身的命名空间HttpFilterNames::JsonToMetadata string metadata_namespace 1; // 命名空间内的 key至少 1 个字符 string key 2 [(validate.rules).string {min_len: 1}]; oneof value_type { // 与 key 配对的值。 // on_present 场景若 value 非空则使用该值代替 JSON key 的值 // 若两者都为空则直接使用 JSON key 的值。 // on_missing / on_error 场景必须提供非空 value。 // 该字段忽略 ValueType即不做类型转换。 google.protobuf.Value value 3; } // 值的类型默认 protobuf.Value ValueType type 4 [(validate.rules).enum {defined_only: true}]; // 是否保留已存在的元数据值false 表示覆盖默认 false bool preserve_existing_metadata_value 5; }ValueType枚举json_to_metadata.proto取值说明PROTOBUF_VALUE默认值为序列化后的protobuf.Value布尔、整数、浮点、字符串原样保留STRING强制按字符串处理布尔/数字会被转换为字符串NUMBER强制按数字处理字符串会被尝试解析为 double源码中对应三个转换器filter.ccJsonValueToStringConverter、JsonValueToDoubleConverter字符串数字转换失败会返回错误并回退到 on_missing 分支、JsonValueToProtobufValueConverter。此外字符串值的长度受MAX_PAYLOAD_VALUE_LEN 8 * 1024字节限制filter.h超长会被拒绝。preserve_existing_metadata_value的实现位于 filter.cc 的addMetadata若为true且当前 stream 的该命名空间下已存在同名 key则保留旧值、跳过写入。完整示例按 version 属性分流到不同版本端点原文档引用 json-to-metadata-filter.yaml 第 25-45 行展示了核心过滤器配置。下面给出该文件完整内容含 listener、cluster 与 subset 负载均衡演示根据请求 JSON 中version属性的存在与否把流量路由到不同版本端点static_resources: listeners: - address: socket_address: address: 0.0.0.0 port_value: 80 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: app domains: - * routes: - match: prefix: / route: cluster: versioned-cluster http_filters: - name: envoy.filters.http.json_to_metadata typed_config: type: type.googleapis.com/envoy.extensions.filters.http.json_to_metadata.v3.JsonToMetadata request_rules: rules: - selectors: - key: version on_present: metadata_namespace: envoy.lb key: version on_missing: metadata_namespace: envoy.lb key: default value: true preserve_existing_metadata_value: true on_error: metadata_namespace: envoy.lb key: default value: true preserve_existing_metadata_value: true - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: versioned-cluster type: STRICT_DNS lb_policy: ROUND_ROBIN lb_subset_config: fallback_policy: ANY_ENDPOINT subset_selectors: - keys: - default - keys: - version load_assignment: cluster_name: versioned-cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 8080 metadata: filter_metadata: envoy.lb: default: true - lb_endpoints: - endpoint: address: socket_address: address: 127.0.0.1 port_value: 8081 metadata: filter_metadata: envoy.lb: version: 1.0该配置的工作原理请求 body 为{version: 1.0}等含version属性的 JSON 时触发on_present分支把version的值提取出来以 keyversion写入命名空间envoy.lb的动态元数据请求 body 不含version属性或 body 缺失、为空时触发on_missing分支把固定值true以 keydefault写入envoy.lbbody 非法、content-type 不匹配或解析失败时触发on_error分支同样写入default: true并配合preserve_existing_metadata_value: true避免覆盖已有元数据集群versioned-cluster配置了lb_subset_configsubset 选择器分别为default与version127.0.0.1:8080归属defaulttrue子集127.0.0.1:8081归属version1.0子集。于是带版本的请求被动态路由到 8081不带版本的请求落到 8080fallback_policy: ANY_ENDPOINT保证子集匹配失败时仍有兜底端点可用。注意metadata_namespace必须与端点filter_metadata中的命名空间这里是envoy.lb一致subset 负载均衡才能正确读取动态元数据。若metadata_namespace留空过滤器会使用自身默认命名空间HttpFilterNames::get().JsonToMetadata见 filter.cc。处理流程源码解析缓冲、解析与元数据落盘原文档提到过滤器会停止迭代直到拿到完整 payload。下面从 filter.cc 梳理完整的请求路径响应路径对称入口为encodeHeaders/encodeData/encodeTrailersdecodeHeadersfilter.cc#L430-L449若无请求规则doRequest()为 false直接Continue检查 content-type不匹配时对所有规则执行on_error分支handleAllOnError累加mismatched_content_type计数后Continue若end_stream为 true无 body 或 body 随 headers 到达对所有规则执行on_missinghandleAllOnMissing累加no_body计数否则返回StopIteration暂停下游过滤器等待 body 到达。decodeDatafilter.cc#L472-L496非 end_stream 的数据返回StopIterationAndBuffer持续缓冲end_stream 时把数据加入解码缓冲后调用processRequestBody。decodeTrailersfilter.cc#L524-L533若此前尚未处理完例如 body 为空只有 trailers在此补一次processRequestBody。processBodyfilter.cc#L354-L416是核心解析逻辑body 为空 → 所有规则走on_missing累加no_bodyJSON 解析失败 → 所有规则走on_error累加invalid_json_body解析成功但不是 JSON 对象纯字符串或纯数字文档明确说明纯字符串或数字 body 被视为成功的 JSON body 并增加 success 计数——此时因没有 key-value 可供匹配对所有规则执行on_missing并累加success见 filter.cc#L374-L385 的注释是 JSON 对象 → 逐条规则沿 selectors 逐级下钻node-getObject(keys[i])中间任何一级取不到对象则对该规则执行on_missing最后一级交给handleOnPresent提取值按ValueType转换提取失败同样回退on_missing全部完成后累加success。finalizeDynamicMetadatafilter.cc#L245-L259把规则产生的StructMap通过streamInfo().setDynamicMetadata写入动态元数据对请求路径还会调用decoder_callbacks_-downstreamCallbacks()-clearRouteCache()清空路由缓存请求处理时should_clear_route_cache为 true见 filter.cc#L418-L428使后续路由/负载均衡决策能够基于新写入的元数据重新计算——这正是动态路由得以生效的关键。FilterConfig::create中filter.cc#L117-L161还有一个 Per-route 专属校验per-route 配置必须至少指定request_rules或response_rules之一否则报错 Per route configs must at least specify one of request_rules or response_rules.。Per-route 配置覆盖全局规则或按路由局部启用该过滤器支持 Per-route 配置。原文档引用的 json-to-metadata-filter-route-config.yaml 演示了两种典型用法覆盖全局配置在某个路由的typed_per_filter_config中给出新的JsonToMetadata配置替换全局 http_filters 中的规则局部启用若全局配置为空没有规则仅在某条路由上启用过滤器。配置加载时getConfig()会调用Http::Utility::resolveMostSpecificPerFilterConfigFilterConfig解析当前路由上最具体的 per-filter 配置找不到才回退到全局配置见 filter.cc#L546-L560对应工厂方法createRouteSpecificFilterConfigTypedconfig.cc。下面给出该文件第 14-45 行的核心片段全局配置 路由级覆盖route_config: name: local_route virtual_hosts: - name: local_service domains: - * routes: - match: prefix: /version-to-metadata route: cluster: service typed_per_filter_config: envoy.filters.http.json_to_metadata: type: type.googleapis.com/envoy.extensions.filters.http.json_to_metadata.v3.JsonToMetadata request_rules: rules: - selectors: - key: version on_present: metadata_namespace: envoy.lb key: version - match: prefix: / route: cluster: some_service http_filters: - name: envoy.filters.http.json_to_metadata typed_config: type: type.googleapis.com/envoy.extensions.filters.http.json_to_metadata.v3.JsonToMetadata request_rules: rules: - selectors: - key: version on_present: metadata_namespace: envoy.lb key: version on_missing: metadata_namespace: envoy.lb key: default value: true - name: envoy.filters.http.router typed_config: type: type.googleapis.com/envoy.extensions.filters.http.router.v3.Router这里的全局规则在version缺失时写入default: true而/version-to-metadata路由的 per-route 规则只关心on_present——两条路径的元数据写入行为相互独立展示了 per-route 覆盖的灵活度。typed_per_filter_config的 key 必须使用过滤器名envoy.filters.http.json_to_metadatavalue 的type仍为...v3.JsonToMetadata。统计指标json_to_metadata 命名空间下的 8 个计数器该过滤器输出的统计位于命名空间http.stat_prefix.json_to_metadata.*其中stat_prefix来自所属 HTTP connection manager 的stat_prefix字段。原文档的统计表如下请求侧与响应侧各 4 个 Counter名称类型描述rq_successCounter成功解析 JSON body 的请求总数。注意纯字符串或纯数字的 body 被视为成功的 JSON body也会累加此计数rq_mismatched_content_typeCountercontent-type 与配置不匹配的请求总数rq_no_bodyCounter没有 content body 的请求总数rq_invalid_json_bodyCounterJSON body 非法的请求总数resp_successCounter成功解析 JSON body 的响应总数。同样纯字符串或数字 body 视为成功resp_mismatched_content_typeCountercontent-type 与配置不匹配的响应总数resp_no_bodyCounter没有 content body 的响应总数resp_invalid_json_bodyCounterJSON body 非法的响应总数这些统计的宏定义位于 filter.h#L27-L31ALL_JSON_TO_METADATA_FILTER_STATS计数器的实例化与命名在 filter.cc#L130-L133请求侧使用前缀json_to_metadata.rq、响应侧使用json_to_metadata.resp外层再叠加 HCM 的stat_prefix最终形如http.ingress_http.json_to_metadata.rq_success。测试与验证仓库提供了针对该过滤器的完整测试集test/extensions/filters/http/json_to_metadata/config_test.cc验证request_rules/response_rules配置的工厂创建流程以及 per-route 配置的合法性校验例如 per-route 缺少 request/response rules 时的报错。filter_test.cc覆盖on_present/on_missing/on_error三分支、嵌套 selector 匹配、content-type 控制、preserve_existing_metadata_value保留行为、统计计数等可据此理解各种边界输入空 body、纯字符串 body、非法 JSON的预期行为。integration_test.cc端到端验证过滤器在真实 HTTP 链路中的行为。编写自己的 Envoy 配置后可参照上述单元/集成测试中的 YAML 片段校验规则书写是否正确例如on_missing/on_error必须携带非空valueselectors至少一条且每个key非空request_rules与response_rules至少配置一个这些约束同时由 proto 的validate规则与 filter.cc 的Rule::create强制执行。小结Json-To-Metadata 过滤器把JSON body 内容与 Envoy 的元数据体系打通通过on_present/on_missing/on_error三分支配合可嵌套的 selector能够稳健地应对属性存在、缺失、body 为空、content-type 不匹配、JSON 非法等多种输入形态配合 subset 负载均衡、限流、访问日志等下游能力可在不修改业务代码的前提下实现基于请求内容的动态路由与治理策略。若要进一步深入可继续阅读 json_to_metadata.proto 中的字段注释、filter.cc 的完整实现以及 filter_test.cc 中覆盖的边界场景用例。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表