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

资讯详情

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

Envoy Dynamic Modules UDP Listener Filter:用动态模块共享库自定义 UDP 数据包处理逻辑

Envoy Dynamic Modules UDP Listener Filter:用动态模块共享库自定义 UDP 数据包处理逻辑 Envoy Dynamic Modules UDP Listener Filter用动态模块共享库自定义 UDP 数据包处理逻辑【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy本文基于 Envoy 官方文档 UDP Dynamic Modules 配置页 展开讲解如何配置 Dynamic Modules UDP listener filter把自定义的 UDP 处理逻辑检查、修改、丢弃数据报或直接向 DNS 等协议发响应以dlopen动态模块的形式加载进 Envoy 进程并结合仓库中的 proto API 定义与 C 源码剖析其加载、初始化与数据报转发的完整调用链。1. 什么是 Dynamic Modules UDP listener filterDynamic Modules UDP listener filter扩展名envoy.filters.udp_listener.dynamic_modules允许用户用动态模块dynamic module即实现纯 C ABI 的共享库来编写 UDP listener filter。官方文档列出的典型应用场景包括检查InspectingUDP 数据报在数据报进入监听器 filter 链前查看其内容修改ModifyingUDP 数据报直接改写数据报载荷丢弃DroppingUDP 数据报拦截不希望上送的数据报从 filter 直接发送响应例如针对 DNS 查询直接在 filter 内应答而不需要经过完整的网络 filter 链。动态模块的核心机制是模块实现一个纯 C 头文件定义的 ABI见 abi.hABI 规定了模块必须导出的函数以及模块可回调的 Envoy 侧函数。官方提供了 C、Go、Rust 三种语言的 SDK 来屏蔽 ABI 细节架构说明见 dynamic_modules.rst理论上任何能产出共享库的语言都可以实现动态模块。2. 配置方式与完整示例该 filter 使用DynamicModuleUdpListenerFilter消息配置定义在 dynamic_modules.proto。官方文档给出的完整 YAML 示例如下可直接复制到 listener 的listener_filters中listener_filters: - name: envoy.filters.udp_listener.dynamic_modules typed_config: type: type.googleapis.com/envoy.extensions.filters.udp.dynamic_modules.v3.DynamicModuleUdpListenerFilter dynamic_module_config: name: my_module entry_point: envoy_dynamic_module_on_program_init filter_name: my_udp_filter filter_config: type: type.googleapis.com/google.protobuf.StringValue value: my_config下面逐字段解析这份配置对应的 API 定义。2.1 DynamicModuleUdpListenerFilter 字段说明proto 中该消息共 3 个字段字段类型说明dynamic_module_configDynamicModuleConfig共享库级别的配置模块名、加载选项等决定加载哪个模块filter_namestring本次 filter 配置的名字用于在同一模块内区分不同的 filter 实现filter_configgoogle.protobuf.Any传给模块初始化函数的配置内容与filter_name一起决定模块内部行为关于filter_nameproto 注释dynamic_modules.proto#L29-L46解释得很清楚一个动态模块内部可以包含完全多个不同的 filter 实现。当 Envoy 收到配置时会把filter_name连同filter_config一起传给模块的 UDP listener filter config init 函数模块据此在加载期决定使用哪个 in-module 实现。例如模块里有一个 echo 实现和一个限流实现filter_name负责二选一filter_config再微调具体行为echo 的行为参数、限流的阈值等。filter_config的序列化规则值得注意来自 proto 注释dynamic_modules.proto#L48-L62google.protobuf.Struct会序列化为 JSON后传给模块google.protobuf.BytesValue与google.protobuf.StringValue会去掉包装类型直接透传原始内容。proto 注释中给出了两种写法# 传递字符串 filter_config: type: type.googleapis.com/google.protobuf.StringValue value: hello # 传递原始字节 filter_config: type: type.googleapis.com/google.protobuf.BytesValue value: aGVsbG8 # echo -n hello | base642.2 DynamicModuleConfig模块加载级配置dynamic_module_config是各动态模块扩展点共用的 DynamicModuleConfig核心字段如下字段默认值说明name-模块名用于按${ENVOY_DYNAMIC_MODULES_SEARCH_PATH}/lib${name}.so搜索共享库文件若同时设置了module字段module优先do_not_closefalse为 true 时阻止模块被dlclose卸载适用于有全局状态、不应被卸载的模块load_globallyfalse默认以RTLD_LOCAL加载以避免多模块符号冲突设为 true 改用RTLD_GLOBAL供模块间共享符号使用官方明确警告可能引发符号冲突metrics_namespacedynamicmodulescustom模块自定义指标的命名空间前缀Prometheus 输出中会出现在标准envoy_前缀之后module-AsyncDataSource指定要加载的模块二进制本地文件路径或远程 HTTP 源注意据架构文档说明远程模块源目前仅 HTTP filter 支持UDP listener filter 场景请使用本地共享库nack_on_cache_missfalse远程模块缓存未命中时采用 NACK 模式还是 warming 模式仅当module.remote设置时生效2.3 模块搜索路径模块名到磁盘文件的解析规则见 架构文档 Module discovery 一节搜索路径由环境变量ENVOY_DYNAMIC_MODULES_SEARCH_PATH配置实际搜索路径为${ENVOY_DYNAMIC_MODULES_SEARCH_PATH}/lib${name}.so环境变量未设置时回退到当前工作目录指定路径搜索失败后再按dlopen(3)的标准行为搜索LD_LIBRARY_PATH、/usr/lib等系统库路径。举例配置中引用my_module且ENVOY_DYNAMIC_MODULES_SEARCH_PATH/path/to/modules时Envoy 依次查找/path/to/modules/libmy_module.so、$LD_LIBRARY_PATH/libmy_module.so、/usr/lib/libmy_module.so等。3. 源码级剖析从配置加载到数据报转发3.1 配置期绑定 5 个 ABI 入口点UDP 动态模块 filter 的配置类实现在 filter_config.cc。其构造函数做了三件事filter_config.cc#L11-L71读取配置保存filter_name并用MessageUtil::knownAnyToBytes把filter_configAny解包为字节串与 proto 注释中StringValue/BytesValue 去包装、Struct 转 JSON的规则一一对应解析 ABI 符号依次从模块中查找以下 5 个必须导出的函数任何一个缺失都会抛出Dynamic module does not support UDP listener filters异常即模块加载失败会导致配置被拒绝envoy_dynamic_module_on_udp_listener_filter_config_newenvoy_dynamic_module_on_udp_listener_filter_config_destroyenvoy_dynamic_module_on_udp_listener_filter_newenvoy_dynamic_module_on_udp_listener_filter_on_dataenvoy_dynamic_module_on_udp_listener_filter_destroy调用模块初始化on_filter_config_new_接收{filter_name, filter_config}两参数返回in_module_config_作为模块内部的配置句柄析构时调用on_filter_config_destroy_完成对称清理filter_config.cc#L73-L77。构造函数还创建了一个统计 scope命名为{metrics_namespace}.{filter_name}.并随后冻结统计名池stat_creation_frozen_ true这与metrics_namespace字段的语义一致模块自定义指标会挂在这个 scope 下。3.2 数据期onData 的转发与 FilterStatus 映射数据报路径在 filter.cc 中非常简短直接filter.cc#L23-L35Network::FilterStatus DynamicModuleUdpListenerFilter::onData(Network::UdpRecvData data) { if (in_module_filter_ nullptr) { return Network::FilterStatus::Continue; } current_data_ data; auto status config_-on_filter_on_data_(thisAsVoidPtr(), in_module_filter_); current_data_ nullptr; if (status envoy_dynamic_module_type_on_udp_listener_filter_status_StopIteration) { return Network::FilterStatus::StopIteration; } return Network::FilterStatus::Continue; }关键行为每个 worker 线程在收到 UDP 数据报时都会调用模块的..._on_data回调模块侧通过UdpReadFilterCallbacks回调接口即可访问/改写数据报、丢弃它或直接发送响应——这正是文档中列出的 4 类场景的落地方式模块返回StopIteration时Envoy 返回Network::FilterStatus::StopIteration数据报不再沿 listener filter 链继续传播对应丢弃场景返回其他状态则等价于ContinueonReceiveError固定返回Continuefilter.cc#L37-L39错误不由模块拦截。实例化方面filter 对象构造时调用on_filter_new_(in_module_config_, ...)得到每个 listener 实例的in_module_filter_析构时调用on_filter_destroy_filter.cc#L10-L21形成config 级句柄 filter 级句柄的两级生命周期管理。3.3 模块复用一次加载多 filter 共享proto 注释dynamic_modules.proto#L22-L24明确指出一个模块可被多个 UDP listener filter 加载引用模块只会加载一次并在多个 filter 之间共享。模块按其文件路径与 inode 唯一标识同路径同内容的共享库会被复用。这意味着多个 UDP listener 挂载不同filter_name时不会造成重复dlopen开销。4. 可观测性与故障排查4.1 内置统计所有动态模块扩展点共用dynamic_modules.统计命名空间各计数器按config_name标签区分实例据架构文档说明UDP listener filter 这类没有 per-instance 名字扩展点使用default作为标签值。相关计数器包括统计名类型含义module_load_errorCounter模块加载失败总数模块源缺失/无效、dlopen失败、按名查找失败或 ABI 符号无法解析config_init_errorCounter模块加载成功但配置初始化失败模块拒绝或无法解析所给配置remote_fetch_errorCounter远程模块源获取/加载失败仅 HTTP filter 支持远程源per_route_config_errorCounterper-route 配置加载失败仅 HTTP filter 产生对 UDP listener filter重点关注前两个module_load_error非零说明lib{name}.so没找到或 ABI 不匹配config_init_error非零说明filter_config被模块拒绝。4.2 错误日志模块无法加载时Envoy 会在dynamic_moduleslogger 上输出Unable to load dynamic module module: reason错误日志。该日志由模块加载器本身发出对每个扩展点都生效对于 bootstrap 阶段加载失败的模块它还是唯一的故障信号因为此时 Envoy 直接退出、统计尚未被抓取。5. 兼容性与安全边界使用动态模块 UDP filter 前必须了解两条硬性约束ABI 兼容动态模块运行时加载必须与加载它的 Envoy 二进制 ABI 兼容。目前官方保证的兼容范围是一个版本内前向兼容——用版本 X.Y 的 SDK 构建的模块可运行于 X.Y 与 X.(Y1)之后可能发生破坏性变更建议随目标 Envoy 版本及时用对应 SDK 重新构建模块。proto 中也说明 Envoy 会通过校验 ABI 头文件的 hash 确保模块与 Envoy 编译于同一版本 ABI。完全信任假设动态模块与 Envoy 同进程运行、共享同一地址空间与权限可以访问主进程的全部内存和资源因此无法在 Envoy 与模块之间建立安全边界。模块应与任何应用代码一样经过充分测试验证后再上线。6. 适用前提小结运行环境需支持dlopen的共享库加载Linux 等平台Windows 侧另有对应实现如 win32 目录 中的加载代码配置中dynamic_module_config.name必须能在搜索路径ENVOY_DYNAMIC_MODULES_SEARCH_PATH→ 当前工作目录 → 系统库路径中解析出lib{name}.so否则配置会被 NACK 拒绝远程模块拉取module.remote目前只对 HTTP filter 可用UDP listener filter 请使用本地文件模块必须同时导出第 3.1 节列出的 5 个envoy_dynamic_module_on_udp_listener_filter_*符号。完整模块编写方式可参考仓库中的 ABI 定义 source/extensions/dynamic_modules/abi/abi.h、C SDK 内部实现 sdk_internal.cc以及架构总览文档 docs/root/intro/arch_overview/advanced/dynamic_modules.rst 中关于 SDK 支持语言、统计与日志的完整说明。【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表