envoy是基于C++开发的,虽然提供了很完善的功能实现,但是还是会存在一些定制化的功能不支持的情况,而直接二次开发envoy 的门槛比较高,所以envoy提供了ext这种扩展技术。
1. ext_proc 是什么?
External Processing (ext_proc) 是 Envoy 提供的一个 HTTP 过滤器,它允许将请求和响应的处理逻辑委托给一个外部 gRPC 服务。这个外部服务被称为“外部处理器”(External Processor),可以运行在独立的进程中,甚至可以是完全独立的容器或服务。
与编写 C++ 原生扩展或 Wasm 插件不同,ext_proc 是一种进程外集成机制,旨在将复杂的业务逻辑从数据面解耦,实现灵活的流量处理。
2. 解决的核心问题
传统网关在应对复杂业务需求时面临三大困境:
1)功能耦合问题:
扩展逻辑需编译进网关二进制,迭代周期长
ext方式优势:逻辑完全外置,可独立开发、部署和升级
2)资源隔离问题:
自定义逻辑异常可能导致整个网关崩溃 ext方式优势:外部服务故障不影响 Envoy 主进程稳定性
3)语言限制问题:
多数网关仅支持特定语言(如 Lua)开发扩展
ext方式优势:任何支持 gRPC 的语言(Go/Java/Python/Node.js 等)均可实现
典型适用场景:
· 复杂认证授权:
多因素认证、RBAC 等需与外部系统交互的场景
· 实时流量分析:
请求/响应数据的实时处理、用户行为追踪、异常检测
· 深度内容检查:
DLP(数据防泄漏)、病毒扫描、敏感信息过滤
· 动态路由调整:
根据请求内容或外部系统状态实现灰度发布、A/B 测试
· 协议转换与适配:
在 HTTP 与 gRPC 或 Legacy 系统协议间转换
3. 工作原理
3.1 架构与通信流程
ext_proc 过滤器与外部服务之间通过双向 gRPC 流(Bidirectional gRPC Stream) 进行通信。每个 HTTP 请求都会创建其专属的 gRPC 流,确保请求级隔离。
完整处理流程:

3.2 处理阶段与模式
ext_proc 支持最多六个独立处理阶段,每个阶段可通过 processing_mode 单独控制是否启用:
不同阶段介绍:
1)Request Headers
方向: 入站
描述: 在请求到达上游前检查或修改请求头
2)Request Body
方向: 入站
描述:检查或修改请求体
3)Request Trailers
方向:入站
描述:检查或修改 HTTP/2 请求尾部
4)Response Headers
方向:出站
描述:在响应返回下游前检查或修改响应头
5)Response Body
方向: 出站
描述:检查或修改响应体
6)Response Trailers
方向:出站
描述:检查或修改 HTTP/2 响应尾部
Body 处理模式:
模式 特点 适用场景 内存占用 延迟
1)STREAMED
特点:流式传输
适用场景:body 片段 大文件上传/下载
内存占用:低
延迟:低(首包)
2)BUFFERED
特点:缓存完整 body 后处理
适用场景:小请求体(如 JSON)
内存占用:高
延迟:高(等待完整 body)
3)BUFFERED_PARTIAL
特点:缓存超限则截断
适用场景:未知大小的中等请求
内存占用:中
延迟:中
4)NONE
适用场景:不发送 body 仅需处理 headers 的场景
内存占用: 无
延迟: 无
4. 使用方式
4.1 开发外部处理服务
需要先实现一个 gRPC 服务,满足 Envoy 定义的 ExternalProcessor 接口。
以下是一个 Go 语言示例,在请求头和响应头中添加自定义字段:
```go
package main
import (
"log"
"net"
"io"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corePb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
extProcPb "github.com/envoyproxy/go-control-plane/envoy/service/ext_proc/v3"
)
type extProcServer struct {
extProcPb.UnimplementedExternalProcessorServer
}
func (s *extProcServer) Process(srv extProcPb.ExternalProcessor_ProcessServer) error {
for {
req, err := srv.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return status.Errorf(codes.Unknown, "recv error: %v", err)
}
resp := &extProcPb.ProcessingResponse{}
switch v := req.Request.(type) {
case *extProcPb.ProcessingRequest_RequestHeaders:
// 处理请求头:添加自定义 header
resp = &extProcPb.ProcessingResponse{
Response: &extProcPb.ProcessingResponse_RequestHeaders{
RequestHeaders: &extProcPb.HeadersResponse{
Response: &extProcPb.CommonResponse{
HeaderMutation: &extProcPb.HeaderMutation{
SetHeaders: []*corePb.HeaderValueOption{
{
Header: &corePb.HeaderValue{
Key: "x-ext-proc-handled",
RawValue: []byte("true"),
},
},
},
},
Status: extProcPb.CommonResponse_CONTINUE,
},
},
},
}
case *extProcPb.ProcessingRequest_ResponseHeaders:
// 处理响应头:添加自定义 header
resp = &extProcPb.ProcessingResponse{
Response: &extProcPb.ProcessingResponse_ResponseHeaders{
ResponseHeaders: &extProcPb.HeadersResponse{
Response: &extProcPb.CommonResponse{
HeaderMutation: &extProcPb.HeaderMutation{
SetHeaders: []*corePb.HeaderValueOption{
{
Header: &corePb.HeaderValue{
Key: "x-ext-proc-from",
RawValue: []byte("ext-proc-service"),
},
},
},
},
Status: extProcPb.CommonResponse_CONTINUE,
},
},
},
}
default:
// 其他阶段:不处理,直接继续
continue
}
if err := srv.Send(resp); err != nil {
return err
}
}
}
func main() {
lis, err := net.Listen("tcp", ":9002")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
extProcPb.RegisterExternalProcessorServer(s, &extProcServer{})
log.Println("ext_proc server listening on :9002")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
```
4.2 配置 Envoy Filter(以 Istio 为例)
在 Istio 服务网格中,通过 EnvoyFilter 资源将 ext_proc 过滤器插入 Sidecar 的 HTTP 过滤链:
```yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: httpbin-ext-proc
namespace: your-namespace
spec:
workloadSelector:
labels:
app: httpbin
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
portNumber: 80
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.ext_proc
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor
grpc_service:
envoy_grpc:
cluster_name: outbound|9002||ext-proc.default.svc.cluster.local
failure_mode_allow: true # 外部服务故障时是否允许请求继续
processing_mode:
request_header_mode: SEND # 发送请求头
response_header_mode: SEND # 发送响应头
request_body_mode: NONE # 不发送请求体
response_body_mode: NONE # 不发送响应体
message_timeout: 200ms # 单条消息超时时间
```
4.3 部署外部处理服务
将 gRPC 服务打包为容器镜像,部署到 Kubernetes 集群中:
```yaml
apiVersion: v1
kind: Service
metadata:
name: ext-proc
spec:
ports:
- name: grpc
port: 9002
targetPort: 9002
selector:
app: ext-proc
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ext-proc
spec:
replicas: 2
selector:
matchLabels:
app: ext-proc
template:
metadata:
labels:
app: ext-proc
spec:
containers:
- name: ext-proc
image: your-registry/ext-proc:latest
ports:
- containerPort: 9002
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
```
5. 优缺点分析
5.1 优点
1)语言无关:
任何支持 gRPC 的语言均可开发,技术选型灵活
2)强隔离性
外部服务故障不影响 Envoy 主进程稳定性
3)功能全面
可读写 Headers、Bodies、Trailers,甚至直接返回响应终止请求
4)独立部署升级
逻辑更新无需重启或重建 Envoy,支持独立扩缩容
5)API 稳定
gRPC 接口跨 Envoy 版本兼容性好,升级风险低
6)动态控制
支持在处理过程中动态修改后续阶段的 processing_mode
5.2 缺点
1)额外网络延迟
跨进程/跨网络调用引入延迟,长尾延迟增加 部署为 Sidecar 模式,使用本地通信;推荐策论:关键路径避免启用 Body 处理
2)资源开销
外部服务消耗额外 CPU 和内存 合理设置副本数和资源配额;
推荐策略:按需启用处理阶段
3)序列化开销
Protobuf 序列化/反序列化成本 使用流式模式(STREAMED)
推荐策略:避免大块数据缓存
4)运维复杂度
需额外管理一个服务的生命周期 使用 Kubernetes 标准运维实践;
推荐策略:完善监控告警
5)故障处理复杂性
需设计优雅降级和超时策略
推荐策略:配置 failure_mode_allow: true,设置合理超时
6)不适合超低延迟场景
相比进程内扩展延迟明显更高
推荐策略:对延迟敏感的核心路径建议使用 C++ 扩展或 Dynamic Module
6. 最佳实践
6.1 架构设计
1. Sidecar 部署模式
· 将外部处理服务作为 Sidecar 与 Envoy 同 Pod 部署,使用 localhost 通信,大幅降低网络延迟
· 示例配置:grpc_service 地址设为 127.0.0.1:9002
2. 按需启用处理阶段
· 默认情况下不发送任何数据,只显式启用需要的阶段
· 若仅需修改 Headers,设置 request_body_mode: NONE 和 response_body_mode: NONE
3. 合理选择 Body 处理模式
· 小 JSON/Protobuf 请求:使用 BUFFERED 模式,便于完整解析
· 大文件上传/下载:使用 STREAMED 模式,避免内存溢出
· 不确定大小:使用 BUFFERED_PARTIAL 并设置缓冲区上限
6.2 可靠性配置
1. 启用故障恢复
```
failure_mode_allow: true # 外部服务故障时继续转发请求,避免整体中断
```
2. 设置合理超时
```
message_timeout: 200ms # 根据服务响应时间调整,避免请求堆积
```
3. 实现优雅降级
· 外部服务内部设置超时和熔断
· 关键逻辑失败时返回 CommonResponse_CONTINUE 而非报错
6.3 性能优化
1. 连接复用
· 确保 gRPC 客户端配置了连接池和 keepalive
· Envoy 端使用 envoy_grpc 客户端类型,支持连接复用
2. 避免阻塞操作
· 外部服务中避免同步调用慢速外部依赖
· 使用异步非阻塞 I/O(如 Go goroutine、Java Netty)
3. 监控关键指标
· 利用 Envoy 暴露的 ext_proc 统计信息:
· streams_started:创建的 gRPC 流数量
· message_timeouts:超时次数
· streams_failed:流失败次数
· 在 Access Log 中记录各阶段延迟:
```yaml
log_format:
json_format:
ext_proc_header_latency: "%FILTER_STATE(envoy.filters.http.ext_proc:FIELD:request_header_latency_us)%"
ext_proc_body_calls: "%FILTER_STATE(envoy.filters.http.ext_proc:FIELD:request_body_call_count)%"
```
参考资料:
https://www.envoyproxy.io/docs/envoy/v1.17.4/_sources/api-v3/extensions/filters/http/ext_proc/v3alpha/ext_proc.proto.rst.txt
https://envoy.k8s.ac.cn/docs/envoy/latest/api-v3/extensions/filters/http/ext_proc/v3/ext_proc.proto#envoy-v3-api-field-extensions-filters-http-ext-proc-v3-externalprocessor-allow-mode-override
https://tetrate.io/blog/envoy-extension-vs-integration
https://jimmysong.io/zh/book/envoy-handbook/extensibility/overview/
https://www.alibabacloud.com/help/en/asm/sidecar/use-envoy-external-processing-for-custom-processing-of-requests

1108

被折叠的 条评论
为什么被折叠?



