十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Envoy gRPC Field Extraction 过滤器新增 metadata_key:动态元数据键覆盖与归一化实战

Envoy gRPC Field Extraction 过滤器新增 metadata_key:动态元数据键覆盖与归一化实战 Envoy gRPC Field Extraction 过滤器新增 metadata_key动态元数据键覆盖与归一化实战【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本篇文章聚焦 Envoy 的envoy.filters.http.grpc_field_extractionHTTP 过滤器新增的metadata_key配置能力它允许开发者覆盖提取字段值写入动态元数据dynamic metadata时使用的键名从而将不同 gRPC 方法中语义相同的字段统一写入同一键下。读完本文你将掌握该字段的配置方法、默认行为、键冲突校验规则以及它在源码中的实现路径与测试验证方式可直接用于限流、鉴权等下游过滤器消费场景。特性来源与变更定位该能力记录在 grpc_field_extraction__added-metadata-key-override.rst属于当前未发布版本的 new_features 变更条目其核心内容为新增metadata_key字段用于覆盖被提取字段值写入的动态元数据键。若未设置则使用请求字段路径request field path即此前的默认行为。对应的协议定义位于 config.proto 中的RequestFieldValueDisposition消息metadata_key为字段号 2// The key that the extracted value is written to within the dynamic metadata. // If empty, the field path (the key of request_field_extractions) is used. // // This is useful for normalizing the metadata written by different gRPC methods whose // request messages name the same logical field differently. // // Within a single gRPC method, two field extractions must not resolve to the same // metadata key, otherwise the configuration is rejected. string metadata_key 2;前置背景gRPC Field Extraction 过滤器做了什么在深入metadata_key之前先回顾该过滤器的基础行为完整说明见 config.proto 顶部注释过滤器支持从第一个 gRPC 请求消息中提取字段无论该方法是 unary 还是 streaming并将结果写入静态的动态元数据命名空间envoy.filters.http.grpc_field_extraction。仅适用于以 Protobuf 作为负载的 gRPC。对双向流与客户端流式客户端发送的首条消息不应依赖服务器端 initial metadata。处理流程为请求到达后若命中配置的 gRPC 方法则阻塞数据流直到解码出第一个完整 gRPC 消息查找目标字段将提取结果写入动态元数据后恢复请求传播若方法未配置或请求非 gRPC则直接透传。格式错误的请求会被拒绝。提取结果的输出格式约定提取的字段名/值以field - values的形式包裹其中字段值一律存储为ListValue即使是非 repeated 的单数字段。空字段值会写入一个空Value。消费该元数据的下游过滤器如 ratelimit必须使用带index段的MetadataKey来访问值。例如访问提取出的单数字符串字段tenant_idmetadata_key: key: envoy.filters.http.grpc_field_extraction path: - key: tenant_id - index: 0 # 必需解开包裹单值的 ListValue省略index段会导致消费者拿到ListValue本身而非其元素通常表现为空值或错误的元数据描述符值。metadata_key 的默认行为与覆盖逻辑在未引入该字段之前提取结果写入动态元数据的键就是request_field_extractions中的字段路径field path。例如请求消息为message MethodRequest { string foo 1; Nested nested 2; uint32 baz 3; ... } message Nested { repeated string bar 1; }配置JSON 形式{ descriptor_set: {}, extractions_by_method: { pkg.svc.Method: { request_field_extractions: { foo: {}, nested.bar: {}, baz: {} } } } }运行时收到如下请求负载{ foo: val_foo, nested: { bar: [val_bar1, val_bar2]} }过滤器写入envoy.filters.http.grpc_field_extraction的动态元数据为{ foo: [val_foo], nested.bar: [val_bar1, val_bar2], baz: [] }注意baz未出现在请求中因此写入空数组所有值包括单数的foo都被包成ListValue。引入metadata_key后可对上述键名做归一化。例如将nested.bar的元数据键覆盖为bar{ descriptor_set: {}, extractions_by_method: { pkg.svc.Method: { request_field_extractions: { nested.bar: { metadata_key: bar } } } } }则写入的动态元数据变为{ bar: [val_bar1, val_bar2] }这正是该特性文档强调的“Normalizing the metadata key”默认元数据键是字段路径metadata_key覆盖它使不同 gRPC 方法可以把同一逻辑值写入公共键下方便下游统一消费。源码实现键的解析、冲突检测与写入键的解析与去重extractor_impl.ccmetadata_key的解析发生在 extractor_impl.cc 的ExtractorImpl::init中for (const auto it : field_extractions.request_field_extractions()) { auto extractor_or_error extractor_factory.Create(request_type_url, it.first); RETURN_IF_NOT_OK(extractor_or_error.status()); const std::string metadata_key it.second.metadata_key().empty() ? it.first : it.second.metadata_key(); PerFieldExtractor entry{it.first, std::move(extractor_or_error.value())}; if (!per_field_extractors_.emplace(metadata_key, std::move(entry)).second) { return absl::InvalidArgumentError( absl::StrCat(multiple field extractions are configured for the dynamic metadata key , metadata_key, )); } }关键逻辑有三点回退语义metadata_key为空字符串时直接回退到字段路径it.first即request_field_extractions的 key与旧行为完全一致因此该特性向后兼容。冲突检测通过std::map::emplace的返回值判断metadata_key是否重复。若同一 gRPC 方法内两条字段提取解析出相同的元数据键配置加载即失败返回InvalidArgument错误错误信息形如multiple field extractions are configured for the dynamic metadata key \name。提取器与键的绑定PerFieldExtractor同时保存原始字段路径path与按metadata_key归类的提取器后续processRequest以元数据键为键遍历执行提取。动态元数据的写入filter.cc提取结果写入动态元数据的实现位于 filter.cc 的handleExtractionResultProtobuf::Struct dest_metadata; for (const auto req_field : result) { RELEASE_ASSERT(!req_field.metadata_key.empty(), req_field.metadata_key shouldnt be empty); if (req_field.value.kind_case() ::google::protobuf::Value::KindCase::KIND_NOT_SET) { // Initialize an empty ListValue for any unset field. (*dest_metadata.mutable_fields())[req_field.metadata_key].mutable_list_value(); } else { (*dest_metadata.mutable_fields())[req_field.metadata_key] req_field.value; } } if (dest_metadata.fields_size() 0) { ENVOY_STREAM_LOG(debug, injected dynamic metadata {} with {}, *decoder_callbacks_, kFilterName, dest_metadata.DebugString()); decoder_callbacks_-streamInfo().setDynamicMetadata(kFilterName, dest_metadata); }从中可以确认每个提取结果以req_field.metadata_key作为键写入Protobuf::Struct对应google.protobuf.Struct。若字段值为KIND_NOT_SET即请求中缺失该字段仍会写入一个空的ListValue占位这与输出格式约定中“空字段写空 Value”一致。最终通过streamInfo().setDynamicMetadata(kFilterName, ...)注入动态元数据命名空间为过滤器名即envoy.filters.http.grpc_field_extraction。过滤器整体处理链在 filter.cc 中decodeHeaders第 80-118 行先通过Grpc::Common::isGrpcRequestHeaders判断是否为 gRPC 请求content-type 为application/grpc非 gRPC 请求直接透传随后把形如/pkg.svc.Method的:path转换为pkg.svc.Method并查找对应的提取器命中则StopIteration阻塞数据。decodeData/handleDecodeData第 120-209 行负责缓冲首个完整消息、执行提取、写元数据最后用convertBackToBuffer把未修改的消息数据原样归还保证上游收到的请求体不被改动。配置冲突校验的测试佐证重复元数据键的配置拒绝行为在 filter_config_test.cc 中有专门的用例覆盖。CustomMetadataKey用例第 226-248 行验证了合法配置parent字段通过metadata_key: normalized_parent覆盖键名而key.name保持默认字段路径键配置加载成功且findExtractor(apikeys.ApiKeys.CreateApiKey)不为空extractions_by_method: { key: apikeys.ApiKeys.CreateApiKey value: { request_field_extractions: { key: parent value: { metadata_key: normalized_parent } } request_field_extractions: { key: key.name value: { } } } }DuplicateMetadataKeys用例第 251-279 行验证了冲突场景parent与key.name两条提取都被覆盖为metadata_key: namemakeConfig()预期失败且错误信息包含multiple field extractions are configured for the dynamic metadata key \name与 extractor_impl.cc 的实现一一对应。端到端验证集成测试中的归一化键integration_test.cc 给出了完整的过滤器配置形态filter()方法第 41-61 行其中两个 gRPC 方法的key.name字段都被覆盖为公共键api_key_namename: grpc_field_extraction typed_config: type: type.googleapis.com/envoy.extensions.filters.http.grpc_field_extraction.v3.GrpcFieldExtractionConfig descriptor_set: filename: %s extractions_by_method: apikeys.ApiKeys.CreateApiKey: request_field_extractions: parent: key.name: metadata_key: api_key_name apikeys.ApiKeys.CreateApiKeyInStream: request_field_extractions: parent: key.name: metadata_key: api_key_nameUnary用例第 73-96 行构造了parent: project-id、key { name: api-key-name }的请求断言上游请求体原样透传upstream_request_-body()与序列化后的请求数据完全一致并校验 access log 中出现了parent:[project-id]与api_key_name:[api-key-name]两条元数据——api_key_name正是metadata_key覆盖后的结果。Streaming用例第 98 行起在流式场景下同样验证了该能力。此外 filter_test.cc 也以normalized_parent等键名对提取结果做了单元级断言。实践要点与注意事项向后兼容metadata_key未设置时行为与旧版本完全相同使用字段路径作键存量配置无需任何改动即可平滑升级。归一化价值当多个 gRPC 方法请求消息对同一逻辑字段命名不同例如key.name与name时通过metadata_key统一键名下游限流、鉴权等过滤器只需针对单一元数据键编写规则。冲突即拒绝同一 gRPC 方法内两条字段提取不得解析到相同元数据键配置加载阶段即报错InvalidArgument避免运行时静默覆盖。读取方记住 ListValue无论字段是否 repeated写入动态元数据的值都被包裹为ListValue消费方如 ratelimit 过滤器需使用带index段的MetadataKey才能取到真实值。命名空间固定当前动态元数据命名空间固定为envoy.filters.http.grpc_field_extractionRequestFieldValueDisposition中的dynamic_metadataoneof 字段 1在协议中标注为“Unimplemented”实际仍使用默认命名空间。小结metadata_key以极小的配置代价解决了 gRPC 字段提取结果在动态元数据中的键命名归一化问题。从协议定义、源码解析到集成测试整个链路清晰可验证解析阶段完成默认回退与冲突检测extractor_impl.cc运行阶段按覆盖键写入ListValue形式的值filter.cc测试阶段分别覆盖了合法覆盖、重复键拒绝与端到端元数据断言。对需要在多个 gRPC 方法间统一消费同一语义字段的场景这是一个即插即用且经过校验的标准化方案。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表