Envoy Dynamic Modules 连接属性访问器对齐 CEL:端口、非 IP 地址与缺失语义详解
Envoy Dynamic Modules 连接属性访问器对齐 CEL端口、非 IP 地址与缺失语义详解【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本指南围绕 Envoy 当前版本中 Dynamic Modules动态模块的一项行为变更展开连接相关的属性访问器attribute accessors与 CEL 表达式语言的对齐工作。核心内容包括source.address、destination.address现在会像 CEL 一样携带端口、保留非 IP 地址如 Unix Domain Socket 路径的原始字符串、缺失的connection.id与地址不再返回占位值而是明确不可用。读完本文你将掌握这些属性的精确语义、底层实现位置以及如何在 C/Go/Rust 三种 SDK 中正确读取它们。行为变更概述本次变更记录于仓库的 changelogs/current/behavior_changes/dynamic_modules__connection-attribute-accessors.rst属于behavior_changes行为变更类别意味着已发布版本中的既有行为可能被修改升级后模块行为可能变化。变更内容可拆解为三部分IP 源地址与目的地址属性与 CEL 对齐包含端口source.address与destination.address由原来仅返回 IP 地址本身改为返回IP:port形式的完整地址串。保留非 IP 地址字符串当连接对端或本端不是 IP 地址例如 Unix Domain Socket、Envoy internal 地址等时不再丢弃或错误处理而是原样返回地址的字符串表示。缺失的连接 ID 与地址变得不可用当底层数据缺失如无下游连接、地址未设置时connection.id、source.address、destination.address等属性返回未设置/不可用状态而不是返回一个虚假或默认值。这一对齐的目的在于消除 Dynamic Modules 与 Envoy 原生 CEL 属性系统之间对同一概念的语义分歧让开发者无论用哪种方式表达属性都能得到一致的结果。属性语义详解与 CEL 对齐后的取值规则source.address 与 destination.address包含端口在 CEL 属性系统中source.address与destination.address的取值形式为IP:port例如1.1.1.1:1234。对齐后Dynamic Modules 的envoy_dynamic_module_type_attribute_id_SourceAddress与_DestinationAddress也遵循同一规则。以 HTTP 过滤器为例在单元测试 test/extensions/dynamic_modules/http/abi_impl_test.cc 中测试环境将远端地址设置为1.1.1.1:1234、本地地址设置为127.0.0.2:4321断言结果如下// envoy_dynamic_module_type_attribute_id_SourceAddress EXPECT_TRUE(envoy_dynamic_module_callback_http_filter_get_attribute_string( filter, envoy_dynamic_module_type_attribute_id_SourceAddress, result_buffer)); EXPECT_EQ(std::string(result_buffer.ptr, result_buffer.length), 1.1.1.1:1234); // envoy_dynamic_module_type_attribute_id_DestinationAddress EXPECT_TRUE(envoy_dynamic_module_callback_http_filter_get_attribute_string( filter, envoy_dynamic_module_type_attribute_id_DestinationAddress, result_buffer)); EXPECT_EQ(std::string(result_buffer.ptr, result_buffer.length), 127.0.0.2:4321);底层实现位于 source/extensions/dynamic_modules/abi_context_accessors.cc通过stream_info.downstreamAddressProvider().remoteAddress()/localAddress()获取地址实例后调用asStringView()返回完整字符串——由于 Envoy 地址对象的字符串表示本身就是IP:port这一改动本质上是直接采用地址对象的完整字符串表示而非剥离端口case envoy_dynamic_module_type_attribute_id_SourceAddress: { const auto addr_provider stream_info.downstreamAddressProvider(); const auto address addr_provider.remoteAddress(); if (address) { const auto addr_str address-asStringView(); *result {const_castchar*(addr_str.data()), addr_str.size()}; ok true; } break; }source.port / destination.port仅 IP 地址时有值与地址字符串配套的SourcePort与DestinationPort走整数访问器getAttributeInt仅在地址类型为 IP 时才返回端口值。实现同样在 abi_context_accessors.cccase envoy_dynamic_module_type_attribute_id_SourcePort: { const auto addr stream_info.downstreamAddressProvider().remoteAddress(); if (addr addr-type() Network::Address::Type::Ip) { *result addr-ip()-port(); ok true; } break; }注意这里的addr-type() Network::Address::Type::Ip显式类型检查对于非 IP 地址如 Unix Domain SocketSourcePort/DestinationPort将返回不可用false这一点与下文非 IP 地址一节相呼应。保留非 IP 地址字符串当连接地址不是 IP 时典型场景是 Unix Domain Socket即地址类型Pipe地址字符串属性依然返回该地址的原始字符串而端口属性则不可用。单元测试 GetAttributesMissingAndNonIpAddresses 完整覆盖了这一场景auto pipe_or_error Network::Address::PipeInstance::create(dynamic-module-attribute-test); Network::Address::InstanceConstSharedPtr pipe_address(std::move(pipe_or_error).value()); stream_info.downstream_connection_info_provider_-setRemoteAddress(pipe_address); stream_info.downstream_connection_info_provider_-setLocalAddress(pipe_address); // 非 IP 地址仍返回原始字符串 for (const auto id : {envoy_dynamic_module_type_attribute_id_SourceAddress, envoy_dynamic_module_type_attribute_id_DestinationAddress}) { EXPECT_TRUE(envoy_dynamic_module_callback_http_filter_get_attribute_string(filter, id, string_result)); EXPECT_EQ(dynamic-module-attribute-test, absl::string_view(string_result.ptr, string_result.length)); } // 但端口属性不可用 for (const auto id : {envoy_dynamic_module_type_attribute_id_SourcePort, envoy_dynamic_module_type_attribute_id_DestinationPort}) { EXPECT_FALSE( envoy_dynamic_module_callback_http_filter_get_attribute_int(filter, id, int_result)); }这一语义与 CEL 属性系统中的行为保持一致例如 CEL 的source.address在非 IP 场景下同样返回地址的字符串表示Unix socket 路径而source.port无值。缺失的 connection.id 与地址明确不可用对齐前的实现可能会在底层数据缺失时返回默认值或空值。对齐后任何缺失情况都通过返回值false/std::nullopt表达属性不可用模块侧必须显式处理取不到值的分支地址未设置remoteAddress()/localAddress()为空指针→SourceAddress/DestinationAddress返回false无下游连接 →ConnectionId不可用无上游连接信息upstreamInfo()无值→UpstreamAddress等上游属性不可用。上述测试中的第一部分演示了地址为空指针时的行为stream_info.downstream_connection_info_provider_-setRemoteAddress(nullptr); stream_info.downstream_connection_info_provider_-setLocalAddress(nullptr); for (const auto id : {envoy_dynamic_module_type_attribute_id_SourceAddress, envoy_dynamic_module_type_attribute_id_DestinationAddress}) { EXPECT_FALSE(envoy_dynamic_module_callback_http_filter_get_attribute_string(filter, id, string_result)); } for (const auto id : {envoy_dynamic_module_type_attribute_id_SourcePort, envoy_dynamic_module_type_attribute_id_DestinationPort, envoy_dynamic_module_type_attribute_id_ConnectionId}) { EXPECT_FALSE( envoy_dynamic_module_callback_http_filter_get_attribute_int(filter, id, int_result)); }对应的ConnectionId实现位于 abi_context_accessors.cc通过stream_info.downstreamAddressProvider().connectionID()的可选值判断case envoy_dynamic_module_type_attribute_id_ConnectionId: { const auto connection_id stream_info.downstreamAddressProvider().connectionID(); if (connection_id.has_value()) { *result connection_id.value(); ok true; } break; }属性 ID 的完整清单与映射所有可用的属性 ID 以枚举形式定义在纯 C 头文件 source/extensions/dynamic_modules/abi/abi.h 中每个枚举值都标注了其对应的 CEL 属性名如request.path、response.code、source.address、connection.id等。与本次变更直接相关的属性汇总如下ABI 枚举值CEL 属性取值类型取值规则attribute_id_SourceAddresssource.addressstring远端地址完整字符串IP 时为IP:port非 IP 时为原始字符串地址缺失时不可用attribute_id_SourcePortsource.portint仅当地址类型为 IP 时有值否则不可用attribute_id_DestinationAddressdestination.addressstring本地地址完整字符串规则同 SourceAddressattribute_id_DestinationPortdestination.portint仅当地址类型为 IP 时有值否则不可用attribute_id_ConnectionIdconnection.idint下游连接 ID无连接时不可用attribute_id_UpstreamAddressupstream.addressstring上游 host 地址完整字符串无上游信息时不可用attribute_id_UpstreamPortupstream.portint仅当上游地址为 IP 时有值否则不可用attribute_id_UpstreamLocalAddressupstream.local_addressstring上游本端地址无上游信息时不可用其中SourceAddress/DestinationAddress/ConnectionId/SourcePort/DestinationPort/UpstreamAddress/UpstreamPort/UpstreamLocalAddress的具体取值逻辑分别对应 abi_context_accessors.cc 中getAttributeString与getAttributeInt的各个case分支。底层实现ContextAccessor 与 ABI 边界单一实现入口所有属性访问逻辑集中封装在ContextAccessor类中头文件为 source/extensions/dynamic_modules/abi_context_accessors.h实现为 source/extensions/dynamic_modules/abi_context_accessors.cc。根据头文件注释访问日志access logger与格式化formatter扩展均包装这些辅助函数因此通用属性、头、元数据、本地回复体逻辑只存在一份避免多套实现漂移Shared context accessors used by Dynamic Module extensions to expose Envoy request and response state across the C ABI boundary. The access logger and formatter extensions both wrap these helpers in their own callbacks, so the generic attribute, header, metadata, and local reply body logic lives in a single place.属性访问按类型拆分为三个静态方法getAttributeString字符串类属性地址、协议、证书、SNI 等getAttributeInt整数类属性端口、响应码、标志位、连接 ID 等getAttributeBool布尔类属性connection.mtls、health_check。三者都遵循同一约定返回bool表示属性是否可用数据通过出参填充未命中分支时写入调试日志并返回false。零拷贝与所有权约定ABI 头文件 abi.h 定义了两类缓冲区envoy_dynamic_module_type_envoy_bufferEnvoy 拥有内存与envoy_dynamic_module_type_module_buffer模块拥有内存。从getAttributeString的实现可以看到所有结果都通过{const_castchar*(data.data()), data.size()}直接指向 Envoy 内部字符串的内存不做拷贝。因此模块侧读取的缓冲区只在触发回调的上下文内有效不应跨回调缓存使用。此外abi_context_accessors.h 明确约定所有返回的缓冲区都指向 Envoy 拥有的内存其生命周期与发起调用的回调一致辅助函数从不分配或拷贝。这意味着本次对齐并未引入额外的内存分配开销端口合并进地址字符串的成本为零。三种 SDK 中的消费方式Dynamic Modules 的 ABI 由纯 C 头文件定义因此可被多种语言 SDK 复用。仓库中已提供 C、Go、Rust 三类 SDK分别位于 sdk/cpp、sdk/go、sdk/rust 及测试数据目录 test_data。以source.address为例三种语言的读取方式如下。C 示例C 测试模块 test/extensions/dynamic_modules/test_data/cpp/http.ccif (auto val handle_.getAttributeString(AttributeID::SourceAddress); !val) { // 属性不可用地址缺失或不是字符串 // 处理无属性分支 }Go 示例Go 测试模块 test/extensions/dynamic_modules/test_data/go/http/http.goif _, ok : p.handle.GetAttributeString(shared.AttributeIDSourceAddress); !ok { // 属性不可用 }Rust 示例Rust 网络过滤器集成测试 test/extensions/dynamic_modules/test_data/rust/network_integration_test.rs 在on_new_connection钩子中同时读取source.address与connection.id并断言地址必须存在、连接 ID 必须大于 0网络过滤器场景下新连接必然有有效 IDfn on_new_connection(mut self, envoy_filter: mut ENF) - abi::envoy_dynamic_module_type_on_network_filter_data_status { assert!(envoy_filter .get_attribute_string(abi::envoy_dynamic_module_type_attribute_id::SourceAddress) .is_some()); let connection_id envoy_filter .get_attribute_int(abi::envoy_dynamic_module_type_attribute_id::ConnectionId) .unwrap_or(0); assert!(connection_id 0); abi::envoy_dynamic_module_type_on_network_filter_data_status::Continue }注意三个示例都遵循先判可用性、再使用返回值的模式——这正是本次变更后缺失语义的核心体现属性取值必须显式处理不可用分支。升级影响与迁移建议由于该变更为行为变更behavior_changes升级到包含此变更的 Envoy 版本时请关注以下几点地址字符串格式变化如果模块代码曾把source.address当作纯 IP 使用例如自行拼接端口、或作为 map 的 key升级后得到的是IP:port需要相应调整解析逻辑若只需 IP建议改用SourcePort/DestinationPort与地址拆分或直接读取source.port。非 IP 地址行为使用 Unix Domain Socket 或 Envoy internal 地址的部署中source.address/destination.address返回路径字符串而非空值端口属性不可用——日志与指标中不要再假定地址一定是 IP。缺失语义必须处理ConnectionId在无下游连接例如某些上游侧回调或本地回复场景时返回不可用模块中unwrap_or(0)或默认值用法请确认是否符合预期避免将0误当作真实连接 ID。与 CEL 一致性收益若同一配置中同时使用 CEL 表达式与 Dynamic Modules 模块处理同一属性本次对齐后两者结果一致消除了此前两套系统的语义漂移。验证途径仓库为本次行为提供了多维度的验证手段单元测试test/extensions/dynamic_modules/http/abi_impl_test.cc 中的GetAttributes、GetAttributesMissingAndNonIpAddresses、GetAttributesAbsentTypedHeaders用例覆盖端口拼接、非 IP 保留、缺失不可用三类场景集成测试test/extensions/dynamic_modules/test_data/rust/http_integration_test.rs 与 network_integration_test.rs 在真实 Envoy 实例上验证属性读取ABI 枚举完整性source/extensions/dynamic_modules/abi/abi.h 中每个属性 ID 与 CEL 属性名的对应注释可作为升级时的对照表。若需在本仓库中快速定位本次变更的全貌可依次阅读行为变更条目 dynamic_modules__connection-attribute-accessors.rst、核心实现 abi_context_accessors.cc 与对应测试 abi_impl_test.cc形成变更声明 → 实现 → 验证的完整证据链。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考