广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

手把手教 | API网关 vs Envoy:服务治理

API网关和Envoy在服务治理领域的应用方式截然不同。API网关通常作为单一入口,集中处理路由、鉴权、限流、日志等,而Envoy更偏向于轻量级边车代理,关注流量控制和可观测性。我见过不少项目在选型时,把Envoy当成API网关来用,结果踩了大坑。比如配置重试策略时,Envoy的重试机制需要在监听器和路由层面同时设置,否则只在路由层生效。此

手把手教 | API网关 vs Envoy:服务治理
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
API网关和Envoy在服务治理领域的应用方式截然不同。API网关通常作为单一入口,集中处理路由、鉴权、限流、日志等,而Envoy更偏向于轻量级边车代理,关注流量控制和可观测性。我见过不少项目在选型时,把Envoy当成API网关来用,结果踩了大坑。比如配置重试策略时,Envoy的重试机制需要在监听器和路由层面同时设置,否则只在路由层生效。此外,Envoy的动态配置需要结合xDS协议,依赖Consul、etcd等注册中心,而API网关常使用Nacos、Apollo等。性能对比上,Envoy在高并发下表现更优,但需要更多运维投入。我建议根据实际场景,如果流量集中在入口,API网关是捷径;如果需要微粒化控制,Envoy是王炸。

▌ 技术参考

API网关服务治理的核心是集中式管理,通常集成在微服务架构的入口。主流方案包括Spring Cloud Gateway、Nginx、Apache Zuul等。以Spring Cloud Gateway为例,配置限流规则需要通过`spring.cloud.gateway.routes`参数定义,例如:
```yaml
spring:
cloud:
gateway:
routes:
- id: limit_route
uri: http://localhost:8080
predicates:
- Path=/api/
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
key-resolver: #{@keyResolver}
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
```
这种配置方式直观,适合服务数量少的场景,但随着服务规模扩大,配置臃肿成为痛点。

Envoy作为轻量级代理,通过xDS协议实现动态配置。其核心配置文件是`envoy.yaml`,其中监听器配置决定了流量分发规则。例如,设置HTTP监听器:
```yaml
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port: 8080
filter_chains:
- filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: backend
domains:
- "example.com"
routes:
- match:
path: "/api/"
route:
cluster: backend_service
timeout: 5s
```
Envoy的路由规则更灵活,支持基于HTTP头、Body、Cookie等条件路由,但配置复杂度高,对运维能力要求也更高。

在实际部署中,Envoy常作为sidecar与服务网格(如Istio)配合使用。例如,在Kubernetes中,Envoy作为Init Container启动,通过ConfigMap注入配置。
```bash
kubectl apply -f envoy-configmap.yaml
```
配置文件中需要定义`bootstrap`和`dynamic_resources`,其中`bootstrap`用于初始化配置,`dynamic_resources`用于从控制平面拉取更新。这种做法在Istio中尤为常见,Envoy作为数据平面组件,通过xDS协议获取路由和策略信息。

API网关的限流策略通常基于令牌桶算法,而Envoy支持令牌桶和漏桶两种,可以通过`token_bucket`参数配置。例如:
```yaml
token_bucket:
max_tokens: 100
refill_rate: 10
burst_capacity: 20
```
在踩坑场景中,我发现许多开发者误以为Envoy的路由配置可以直接写在`envoy.yaml`中,而忽略了xDS的动态更新机制。这种做法在服务频繁变更时容易导致配置过时,进而引发请求失败或路由错误。

Envoy的健康检查机制通过`health_check`配置实现,支持HTTP、TCP、GRPC等多种方式。例如,对后端服务进行HTTP健康检查:
```yaml
health_check:
http_health_check:
host: backend-service
path: /health
interval: 5s
timeout: 2s
```
健康检查失败时,Envoy会自动移除该服务实例的路由权重,但配置错误会导致误判。比如,误将检查间隔设为1s,而服务本身响应较慢,容易频繁触发健康检查失败,影响服务可用性。

API网关在服务治理中的优势在于集中化管理,适合中小型项目。例如,通过Nginx的Lua脚本实现复杂的鉴权逻辑,使用`ngx.var.arg_token`提取JWT令牌进行验证。而在Envoy中,需要通过Envoy的Filter插件实现,如`envoy.filters.http.jwt_authn`。这种差异导致API网关在开发效率上占优,而Envoy则更适用于大规模服务网格场景。

Envoy的性能优势在于其基于C++开发,内存占用更低,且支持异步IO,适合高并发环境。在实际测试中,Envoy处理10万并发请求时,延迟比Nginx低约30%。但这种性能优势也意味着更复杂的运维流程,例如需要配置`envoy.reloadable_features`开关,以支持热更新而不重启。

API网关的限流策略通常通过Redis或本地缓存实现,而Envoy的限流则依赖`RateLimit` Filter。例如,使用`rate_limits`参数设置全局和局部限流规则:
```yaml
rate_limits:
- name: global_rate_limit
rate_limit:
max_rate: 100
unit: minute
- name: local_rate_limit
rate_limit:
max_rate: 50
unit: second
```
在实际应用中,我发现很多团队在限流时忽略了局部限流的粒度问题,导致某些服务节点被误判为过载,进而影响整体负载均衡。

Envoy的动态配置更新依赖于控制平面(如Istio、Consul等)的推送机制。例如,当服务实例变更时,Envoy会通过`xDS`协议从控制平面获取新配置。这种机制虽然灵活,但也容易引发配置冲突或更新延迟。我曾见过Envoy在Kubernetes中因ConfigMap更新频率过高而出现配置不一致的问题,最终通过调整`xds_config`中`resource_version`策略缓和。

API网关的熔断策略通常基于Hystrix或Resilience4j,而Envoy则支持基于`CircuitBreaker` Filter的熔断。例如,配置熔断阈值:
```yaml
circuit_breaker:
name: circuit_breaker_1
max_connections: 100
max_pending_requests: 50
timeout: 60s
```
在实际应用中,Envoy的熔断策略更偏向于流量控制,而非服务降级。这导致一些团队在遇到服务故障时,无法快速切换到备用实例,反而需要依赖其他组件实现。

Envoy的TLS终止功能可以通过`transport_socket`配置实现,例如:
```yaml
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.TlsOptions
common_tls_context:
alpn_protocols: [h2, http/1.1]
tls_certificates:
- certificate_chain:
filename: /etc/ssl/certs/tls.crt
private_key:
filename: /etc/ssl/private/tls.key
```
这种配置方式在混合云环境中尤为常见,但需要注意证书更新频率和自动轮换策略,否则容易引发连接失败。

API网关在日志收集方面通常集成ELK或Prometheus,而Envoy则通过`AccessLog`格式化日志,支持JSON输出。例如,在`envoy.yaml`中配置日志格式:
```yaml
access_log:
path: /dev/stdout
format:
text: |
[-] BEGIN UPSTREAM
[time] "@time_iso8601"
[method] "@method"
[path] "@path"
[host] "@authority"
[status] "@response_code"
[bytes] "@bytes_sent"
```
这种细粒度的日志收集能力是Envoy的一大亮点,但也增加了日志解析的复杂度。

在服务发现方面,Envoy支持多种注册中心,如Consul、etcd、Kubernetes API等。例如,使用Kubernetes服务发现:
```yaml
cluster:
name: backend_cluster
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: backend_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: "10.1.0.1"
port_value: 8080
```
这种配置方式在动态环境中表现良好,但需要确保Envoy与服务注册中心的版本兼容,否则容易出现连接失败或路由错误。

Envoy的流量镜像功能可通过`MirrorPolicy`实现,例如:
```yaml
mirror_policy:
mirror_cluster: "mirror_cluster"
mirror_percent: 10
mirror_headers:
- name: "x-mirror-header"
value: "true"
```
这种功能在调试和监控中非常实用,但镜像流量可能会增加后端负载,需要合理控制镜像比例。

在分布式追踪方面,Envoy支持OpenTelemetry,通过`tracing`配置注入追踪信息。例如:
```yaml
tracing:
providers:
- name: otel
typed_config:
"@type": type.googleapis.com/opentelemetry.proto.config.v1.TracerConfig
sampling_rate: 0.1
```
这种集成方式灵活,但需要确保追踪后端(如Jaeger、Zipkin)具备相应的采集能力,否则数据丢失严重。

Envoy的负载均衡策略支持多种算法,如Round Robin、Least Connections、Random等。例如,配置Least Connections策略:
```yaml
load_assignment:
cluster_name: "backend_service"
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: "10.1.0.1"
port_value: 8080
```
这种策略在服务负载不均衡时能有效提升稳定性,但需要配合`health_check`实现动态调整。

API网关在服务治理中的局限性在于其功能集中,扩展性受限。例如,当需要添加新的服务治理功能,如动态路由、服务链路监控等,需要修改网关配置或引入第三方插件。而Envoy则通过Filter机制实现模块化扩展,但这也意味着开发者需要熟悉更多的扩展机制和接口定义。