首页
/ Envoy 扩展开发指南:扩展点全景、架构原理与自定义过滤器实战

Envoy 扩展开发指南:扩展点全景、架构原理与自定义过滤器实战

2026-09-13 12:23:55作者:卓艾滢Kingsley

Envoy 的架构通过**扩展(Extension)**机制实现了高度的可插拔性:核心只负责连接管理、路由与转发框架,而访问日志、过滤器、服务发现、健康检查、遥测等大量功能都以扩展形式挂载进来。本文以仓库 docs/root/extending/extending.rst 为骨架,系统梳理 Envoy 的 22 类扩展点,并结合 source/extensions 下的真实实现与测试用例,从源码级讲解扩展的注册机制、构建接入方式和编写流程,帮助你掌握"在不改动核心的前提下,为 Envoy 添加自定义能力"的完整方法论。

一、为什么 Envoy 如此容易扩展

Envoy 的设计哲学之一,是把几乎一切可以变化的部分都抽象为工厂(Factory)注册的扩展。核心进程(source/exesource/server)只维护一套稳定的框架接口,例如:

  • 网络/HTTP 过滤器链的调用与迭代;
  • 连接池、集群管理与路由决策;
  • 配置的加载、校验与动态下发。

而具体的协议处理、可观测性接入、安全策略等,全部由 source/extensions 目录下的扩展实现,并通过统一的**注册表(Registry)**在启动时被装配进框架。扩展注册表实现 定义了 FactoryRegistryRegisterFactory 等模板类,负责按名称(如 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.FilterConfigdisabled: 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:config
  • envoy.clusters.aggregate//source/extensions/clusters/aggregate:cluster
  • envoy.compression.gzip.compressor//source/extensions/compression/gzip/compressor:config
  • envoy.grpc_credentials.file_based_metadata//source/extensions/grpc_credentials/file_based_metadata:config
  • envoy.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.hNetworkFilterNameValues 中(Echo = "envoy.filters.network.echo"),同一文件还列出了 http_connection_managertcp_proxyredis_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 扩展(以网络过滤器为例)的完整步骤如下:

  1. 定义配置消息:在 api 目录按 envoy.extensions.*.v3 规范编写 protobuf 配置消息;
  2. 实现核心逻辑:继承对应扩展接口(如 Network::ReadFilter),实现回调方法;
  3. 实现工厂:继承 FactoryBase<ConfigProto> 模板(或 ExceptionFreeFactoryBase),覆写 createFilterFactoryFromProtoTyped 返回装配回调,必要时覆写 isTerminalFilterByProtoTyped 声明终结过滤器;
  4. 注册:用 LEGACY_REGISTER_FACTORY 或新式 RegisterFactory 宏把工厂注册进 NamedNetworkFilterConfigFactory 注册表,名称遵循 envoy.filters.network.<name> 规范(见 well_known_names.h);
  5. 构建接入:在扩展目录的 BUILD 文件中用 envoy_cc_extension 声明配置 target,将注册文件与逻辑库分离;
  6. 编写测试:按 echo_integration_test.cc 的模式编写集成测试,验证配置解析与端到端行为;
  7. 按需裁剪:如需控制二进制体积,可在 extensions_build_config.bzl 中增删扩展映射。

不同类型的扩展仅在第 2、3 步的接口基类上不同(例如 HTTP 过滤器实现 Http::StreamDecoderFilter 等接口并注册为 NamedHttpFilterConfigFactory),其余注册、构建、测试流程完全一致。

五、写在最后:以源码为师的扩展开发路线

extending.rst 的结论值得反复强调:Envoy 官方没有一份完备的高层扩展开发文档,但这不是障碍,反而指明了最可靠的学习路径——source/extensions 当作教科书。该目录按扩展类型组织(filters/networkfilters/httpaccess_loggersclusterstracersstat_sinks 等),每个子目录都包含逻辑实现、配置工厂、BUILD 文件与配套测试,构成了完整的"接口契约 + 最佳实践"样本库。

对于希望编写全新网络过滤器的开发者,建议按以下顺序阅读:

  1. 本仓库 echo 过滤器全套源码(逻辑、工厂、BUILD、集成测试);
  2. 网络过滤器架构文档 中关于 Read/Write/Read-Write 三类过滤器与过滤器链匹配的说明;
  3. HTTP 过滤器架构文档 中关于顺序、终结过滤器、路由级配置的约束;
  4. 社区示例工程 envoy-filter-example(仓库文档中推荐的官方配套样例),它展示了如何在 Envoy 仓库之外独立组织扩展仓库与 Bazel 依赖;
  5. 结合 注册表源码构建宏 理解装配机制。

掌握这套"接口实现 → 工厂注册 → 构建接入 → 测试验证"的流程后,无论是新增协议过滤器、接入新的可观测后端,还是定制服务发现与重试策略,都可以在不动 Envoy 核心代码的前提下,以标准扩展形式完成。

登录后查看全文
热门项目推荐
相关项目推荐