Envoy 扩展开发指南:扩展点全景、架构原理与自定义过滤器实战
Envoy 的架构通过**扩展(Extension)**机制实现了高度的可插拔性:核心只负责连接管理、路由与转发框架,而访问日志、过滤器、服务发现、健康检查、遥测等大量功能都以扩展形式挂载进来。本文以仓库 docs/root/extending/extending.rst 为骨架,系统梳理 Envoy 的 22 类扩展点,并结合 source/extensions 下的真实实现与测试用例,从源码级讲解扩展的注册机制、构建接入方式和编写流程,帮助你掌握"在不改动核心的前提下,为 Envoy 添加自定义能力"的完整方法论。
一、为什么 Envoy 如此容易扩展
Envoy 的设计哲学之一,是把几乎一切可以变化的部分都抽象为工厂(Factory)注册的扩展。核心进程(source/exe、source/server)只维护一套稳定的框架接口,例如:
- 网络/HTTP 过滤器链的调用与迭代;
- 连接池、集群管理与路由决策;
- 配置的加载、校验与动态下发。
而具体的协议处理、可观测性接入、安全策略等,全部由 source/extensions 目录下的扩展实现,并通过统一的**注册表(Registry)**在启动时被装配进框架。扩展注册表实现 定义了 FactoryRegistry 与 RegisterFactory 等模板类,负责按名称(如 envoy.filters.network.echo)查找工厂、禁用工厂、列出已注册扩展等能力——这正是 Envoy 能在运行时按配置字符串实例化任意扩展的根本原因。
官方文档明确指出:截至目前,Envoy 尚没有一份高层级的扩展开发文档,学习扩展开发的最好途径,就是阅读仓库中已有的 source/extensions 目录下的现有扩展源码,并参考社区提供的示例工程。本文即沿着这条路径,从扩展点清单、注册机制、构建接入三个维度展开。
二、Envoy 支持的扩展类型全景
extending.rst 列出了 Envoy 架构允许扩展的全部类型。下面按功能域分组逐一说明,并给出仓库内对应的实现目录与配置入口:
1. 可观测性类扩展
| 扩展类型 | 作用 | 仓库参考 |
|---|---|---|
| Access loggers(访问日志器) | 将连接/请求的访问日志写入不同后端 | source/extensions/access_loggers(file、grpc、stdout、stderr、fluentd、open_telemetry、wasm 等) |
| Access log filters(访问日志过滤器) | 按条件决定哪些请求/响应进入日志 | arch_overview_access_logs 中列出的内置过滤器及 extension_filter 扩展点 |
| Stat sinks(统计输出器) | 将内部统计指标导出到监控系统 | source/extensions/stat_sinks(statsd、dog_statsd、metric_service 等) |
| Tracers(追踪器) | 接入分布式追踪后端 | source/extensions/tracers,架构见 tracing.rst |
| Request ID(请求 ID 生成器) | 定制 x-request-id 头生成策略 |
按 HTTP 连接管理器 request_id_extension 字段配置,实现位于 source/extensions/request_id |
关于访问日志,架构文档 access_logging.rst 补充了可扩展的细节:每个连接/流可配置多条访问日志,日志可按周期(access_log_flush_interval)或会话开始(如 TCP Proxy 的 flush_access_log_on_connected、HTTP 连接管理器的 flush_access_log_on_new_request)生成;日志sink 本身也是可插拔的,File sink 采用异步 I/O 刷写、不阻塞主网络线程,另有 gRPC、Stdout、Stderr、Fluentd(Forward 协议)等实现。
2. 流量处理类扩展
| 扩展类型 | 作用 | 仓库参考 |
|---|---|---|
| Listener filters(监听器过滤器) | 对新建 socket 的连接元数据进行预处理 | source/extensions/listener_filters,架构见 listener_filters.rst |
| Network filters(网络/L3-L4 过滤器) | 核心连接处理,读/写/读写三种类型 | source/extensions/filters/network(echo、tcp_proxy、redis_proxy、thrift_proxy 等) |
| HTTP filters(HTTP 过滤器) | 在连接管理器内处理 HTTP 级消息,与底层物理协议无关 | source/extensions/filters/http,架构见 http_filters.rst |
| Transport sockets(传输层 socket) | 自定义 TLS/明文等传输实现 | source/extensions/transport_sockets |
| Compression libraries(压缩库) | 替换/新增压缩与解压缩实现 | source/extensions/compression,底层库说明见 libraries.rst |
| Connection balance extensions(连接均衡扩展) | 定制监听器在 worker 线程间的连接分配策略 | 按 Listener.ConnectionBalanceConfig.extend_balance 字段配置 |
http_filters.rst 特别强调了 HTTP 过滤器链的几个可扩展行为,编写自定义 HTTP 过滤器时必须注意:
- 过滤器顺序:
http_filters列表中 decoder 方向按 A→B→C 顺序调用,encoder 方向按 C→B→A 逆序调用;最后一个过滤器必须是终结过滤器(terminal filter),由NamedHttpFilterConfigFactory::isTerminalFilterByProto()判定(最常见的是 router 过滤器)。 - 路由变更:下游过滤器可在路由解析后通过
setRoute()修改缓存路由,可继承 DelegatingRoute 覆盖超时、集群名等属性;clearRouteCache()会强制重算路由;refreshRouteCluster()则只刷新集群选择(当前仅 matcher 与 dynamic_modules 集群选择器支持)。 - 路由级配置:通过路由/虚拟主机/路由配置上的
typed_per_filter_config按过滤器名下发过滤器的路由级参数。 - 路由级过滤器链:可在
HttpFilter上设置disabled: true默认禁用某过滤器,再通过路由配置中的envoy.config.route.v3.FilterConfig(disabled: true)按路由启用/停用。
3. 路由与集群类扩展
| 扩展类型 | 作用 | 仓库参考 |
|---|---|---|
| Clusters(集群/服务发现) | 实现不同服务发现机制 | source/extensions/clusters(static、strict_dns、logical_dns、eds、redis、aggregate、composite、original_dst 等) |
| Health checkers(健康检查器) | 定制上游健康检查逻辑 | source/extensions/health_checkers |
| Retry implementations(重试实现) | 定制 HTTP 重试判定策略 | source/extensions/retry |
| Internal redirect policy(内部重定向策略) | 定制内部重定向的谓词 | 按 InternalRedirectPolicy.predicates 字段配置,实现位于 source/extensions/internal_redirect |
| Resource monitors(资源监视器) | 监测内存/CPU/文件描述符压力,供过载管理器使用 | source/extensions/resource_monitors,架构见 overload_manager.rst |
其中**过载管理器(overload manager)**是一个典型的扩展化保护组件:它周期性轮询资源监视器得到的压力值(范围 [0,1]),经过触发器(trigger)转换为 scaling/saturated 状态,再驱动注册的过载动作。资源监视器与过载动作均可由扩展提供。
4. 安全与平台集成类扩展
| 扩展类型 | 作用 | 仓库参考 |
|---|---|---|
| gRPC credential providers(gRPC 凭据提供者) | 为 gRPC 连接提供认证凭据 | source/extensions/grpc_credentials(file_based_metadata 等) |
| BoringSSL private key methods(私钥方法) | 将 TLS 私钥操作(如签名)外包给外部设备/服务 | 由 BoringSSL 私钥方法接口实现,见 source/extensions/private_key_providers |
| Bootstrap extensions(引导扩展) | 在 Envoy 启动早期执行定制逻辑 | 按 Bootstrap.bootstrap_extensions 字段配置,见 source/extensions/bootstrap(wasm、dynamic_modules、reverse_tunnel 等) |
| Fatal actions(致命动作) | 进程即将因致命错误退出时执行的动作 | 按 Bootstrap.fatal_actions 字段配置,见 source/extensions/fatal_actions |
| Watchdog action(看门狗动作) | 主线程/worker 线程被判定无响应时触发动作 | 按 WatchdogAction 消息配置,见 source/extensions/watchdog |
| Formatters(访问日志格式化器) | 扩展访问日志命令操作符 | 见 access_log 命令操作符文档 |
从构建清单 extensions_build_config.bzl 可以确认这些扩展在仓库中的真实注册情况,例如:
envoy.access_loggers.file→//source/extensions/access_loggers/file:configenvoy.clusters.aggregate→//source/extensions/clusters/aggregate:clusterenvoy.compression.gzip.compressor→//source/extensions/compression/gzip/compressor:configenvoy.grpc_credentials.file_based_metadata→//source/extensions/grpc_credentials/file_based_metadata:configenvoy.bootstrap.wasm→//source/extensions/bootstrap/wasm:config
这张映射表(扩展名 → Bazel target)正是"扩展注册到构建系统"的直接证据,也是裁剪 Envoy 二进制大小时修改的核心文件。
三、源码级实战:从零看懂一个网络过滤器扩展
extending.rst 特别指出,新增网络过滤器的仓库结构与构建依赖示例可参考社区 envoy-filter-example 工程;而在本仓库内,Echo 过滤器就是最精简、最完整的教学样例,完整实现了"接口实现 → 工厂注册 → 构建接入 → 集成测试"全链路。
3.1 过滤器本体:实现 Network::ReadFilter
echo.h 定义了过滤器类,继承 Network::ReadFilter(读过滤器,在下游连接收到数据时被调用)与日志基类:
class EchoFilter : public Network::ReadFilter, Logger::Loggable<Logger::Id::filter> {
public:
Network::FilterStatus onData(Buffer::Instance& data, bool end_stream) override;
Network::FilterStatus onNewConnection() override { return Network::FilterStatus::Continue; }
void initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callbacks) override {
read_callbacks_ = &callbacks;
}
private:
Network::ReadFilterCallbacks* read_callbacks_{};
};
对应的 echo.cc 展示了过滤器链的核心交互语义——读回原始数据并停止迭代:
Network::FilterStatus EchoFilter::onData(Buffer::Instance& data, bool end_stream) {
ENVOY_CONN_LOG(trace, "echo: got {} bytes", read_callbacks_->connection(), data.length());
read_callbacks_->connection().write(data, end_stream);
ASSERT(0 == data.length());
return Network::FilterStatus::StopIteration;
}
这里有两个值得深挖的机制:
- FilterStatus 返回值:
StopIteration表示过滤器消费了数据、停止向链中后续过滤器传递;Continue则继续迭代。Echo 把数据写回下游后返回StopIteration,因为它是终结过滤器。 - 回调对象:
initializeReadFilterCallbacks()保存的ReadFilterCallbacks是过滤器与连接管理器的通信通道,用于读写数据、关闭连接、访问连接级信息等。
3.2 工厂与注册:LEGACY_REGISTER_FACTORY
config.cc 是扩展的"装配说明"。它继承 factory_base.h 中的 Common::FactoryBase<ConfigProto> 模板——该模板通过 MessageUtil::downcastAndValidate 完成 protobuf 配置的类型转换与校验,替开发者省去大量样板代码:
class EchoConfigFactory
: public Common::FactoryBase<envoy::extensions::filters::network::echo::v3::Echo> {
public:
EchoConfigFactory() : FactoryBase(NetworkFilterNames::get().Echo) {}
private:
Network::FilterFactoryCb
createFilterFactoryFromProtoTyped(const envoy::extensions::filters::network::echo::v3::Echo&,
Server::Configuration::FactoryContext&) override {
return [](Network::FilterManager& filter_manager) -> void {
filter_manager.addReadFilter(std::make_shared<EchoFilter>());
};
}
bool isTerminalFilterByProtoTyped(const envoy::extensions::filters::network::echo::v3::Echo&,
Server::Configuration::ServerFactoryContext&) override {
return true;
}
};
LEGACY_REGISTER_FACTORY(EchoConfigFactory, Server::Configuration::NamedNetworkFilterConfigFactory,
"envoy.echo");
关键点:
- 工厂返回的是回调(
FilterFactoryCb)而非过滤器实例:回调在过滤器链装配时执行,把EchoFilter以读过滤器身份加入FilterManager。 LEGACY_REGISTER_FACTORY静态注册:注册宏利用静态初始化把工厂实例放入 registry.h 的强类型注册表,配置中出现的名称envoy.filters.network.echo会在此查表定位工厂。- 终结过滤器语义:
isTerminalFilterByProtoTyped返回true,表明该过滤器位于链尾、数据到此为止。 - 规范名称:过滤器名定义在 well_known_names.h 的
NetworkFilterNameValues中(Echo = "envoy.filters.network.echo"),同一文件还列出了http_connection_manager、tcp_proxy、redis_proxy等全部内置网络过滤器名,编写新扩展时应遵循envoy.filters.network.<name>的命名规范。
3.3 构建接入:envoy_cc_extension 宏
Bazel BUILD 文件 展示了扩展的标准构建结构,分为纯逻辑库与配置工厂两个 target:
envoy_cc_library(
name = "echo",
srcs = ["echo.cc"],
hdrs = ["echo.h"],
deps = [
"//envoy/buffer:buffer_interface",
"//envoy/network:connection_interface",
"//envoy/network:filter_interface",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
],
)
envoy_cc_extension(
name = "config",
srcs = ["config.cc"],
extra_visibility = ["//test/integration:__subpackages__"],
deps = [
":echo",
"//envoy/registry",
"//envoy/server:filter_config_interface",
"//source/extensions/filters/network:well_known_names",
"//source/extensions/filters/network/common:factory_base_lib",
"@envoy_api//envoy/extensions/filters/network/echo/v3:pkg_cc_proto",
],
)
envoy_cc_extension(定义于 bazel/envoy_build_system.bzl)是 Envoy 为扩展类 target 封装的专用宏,它与 envoy_cc_library 配合,保证扩展只依赖公开接口、并能在 extensions_build_config.bzl 中被整体引用或裁剪。依赖中 @envoy_api 指向的 protobuf 包对应扩展的配置消息(envoy.extensions.filters.network.echo.v3.Echo),配置消息本身定义在 api/envoy/extensions/filters/network/echo/v3/echo.proto。
3.4 验证:集成测试如何驱动真实扩展
Echo 过滤器配有一份完整的集成测试 echo_integration_test.cc,它直接在测试配置中以 envoy.filters.network.echo 名称装配过滤器,并验证端到端行为:
filter_chains:
filters:
name: envoy.filters.network.echo
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.echo.v3.Echo
测试用例 Hello 建立真实连接、发送 "hello",断言回包内容与发送内容一致(EXPECT_EQ("hello", response)),并覆盖了 IPv4/IPv6 双栈参数化以及监听器热增删(AddRemoveListener)场景。这份测试是理解"扩展如何被配置、实例化、运行"的完整可执行范例。
四、扩展开发的通用路径:接口、工厂、注册、构建、测试
综合上述源码证据,编写一个 Envoy 扩展(以网络过滤器为例)的完整步骤如下:
- 定义配置消息:在 api 目录按
envoy.extensions.*.v3规范编写 protobuf 配置消息; - 实现核心逻辑:继承对应扩展接口(如
Network::ReadFilter),实现回调方法; - 实现工厂:继承
FactoryBase<ConfigProto>模板(或ExceptionFreeFactoryBase),覆写createFilterFactoryFromProtoTyped返回装配回调,必要时覆写isTerminalFilterByProtoTyped声明终结过滤器; - 注册:用
LEGACY_REGISTER_FACTORY或新式RegisterFactory宏把工厂注册进NamedNetworkFilterConfigFactory注册表,名称遵循envoy.filters.network.<name>规范(见 well_known_names.h); - 构建接入:在扩展目录的 BUILD 文件中用
envoy_cc_extension声明配置 target,将注册文件与逻辑库分离; - 编写测试:按 echo_integration_test.cc 的模式编写集成测试,验证配置解析与端到端行为;
- 按需裁剪:如需控制二进制体积,可在 extensions_build_config.bzl 中增删扩展映射。
不同类型的扩展仅在第 2、3 步的接口基类上不同(例如 HTTP 过滤器实现 Http::StreamDecoderFilter 等接口并注册为 NamedHttpFilterConfigFactory),其余注册、构建、测试流程完全一致。
五、写在最后:以源码为师的扩展开发路线
extending.rst 的结论值得反复强调:Envoy 官方没有一份完备的高层扩展开发文档,但这不是障碍,反而指明了最可靠的学习路径——把 source/extensions 当作教科书。该目录按扩展类型组织(filters/network、filters/http、access_loggers、clusters、tracers、stat_sinks 等),每个子目录都包含逻辑实现、配置工厂、BUILD 文件与配套测试,构成了完整的"接口契约 + 最佳实践"样本库。
对于希望编写全新网络过滤器的开发者,建议按以下顺序阅读:
- 本仓库 echo 过滤器全套源码(逻辑、工厂、BUILD、集成测试);
- 网络过滤器架构文档 中关于 Read/Write/Read-Write 三类过滤器与过滤器链匹配的说明;
- HTTP 过滤器架构文档 中关于顺序、终结过滤器、路由级配置的约束;
- 社区示例工程
envoy-filter-example(仓库文档中推荐的官方配套样例),它展示了如何在 Envoy 仓库之外独立组织扩展仓库与 Bazel 依赖; - 结合 注册表源码 与 构建宏 理解装配机制。
掌握这套"接口实现 → 工厂注册 → 构建接入 → 测试验证"的流程后,无论是新增协议过滤器、接入新的可观测后端,还是定制服务发现与重试策略,都可以在不动 Envoy 核心代码的前提下,以标准扩展形式完成。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.22 K637- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python520
cherry-studio🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端TypeScript2.01 K146
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python50872
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go21745
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java35251