引言
API网关是微服务架构中的流量入口,承担着路由转发、协议转换、统一鉴权、流量控制等核心职责。一个优秀的网关设计能够显著降低服务复杂度,提升系统可观测性。
本文将从架构设计角度出发,深入分析API网关的核心模式,并提供多种技术栈的实战配置。
API网关的核心职责
统一入口与路由
API网关作为系统的单一入口,对外暴露统一的服务地址,内部通过路由规则将请求分发到不同的后端服务。
# Kong 路由配置示例
services:
- name: user-service
url: http://user-service:8080
routes:
- name: user-routes
paths:
- /api/v1/users
methods:
- GET
- POST
- PUT
- DELETE
strip_path: false
preserve_host: true
协议转换
网关可以将外部HTTP/REST请求转换为内部gRPC调用,实现协议解耦。
# Envoy HTTP to gRPC 转换配置
routes:
- match:
prefix: "/api/v1/orders"
route:
cluster: order-service-grpc
grpc: {}
统一鉴权
在网关层实现JWT验证、OAuth2.0认证,避免每个服务重复实现。
-- Kong JWT 插件配置
local jwt = require "resty.jwt"
local function validate_token()
local token = ngx.req.get_headers()["Authorization"]
if not token then
return ngx.exit(401)
end
local jwt_obj = jwt:verify(SECRET_KEY, token:sub(8))
if not jwt_obj.verified then
return ngx.exit(401)
end
ngx.ctx.user_id = jwt_obj.payload.sub
end
动态路由策略
基于权重的灰度发布
通过配置流量权重,实现蓝绿部署或金丝雀发布。
# Kong 蓝绿部署配置
upstreams:
- name: order-service
targets:
- target: order-service-blue:8080
weight: 90
- target: order-service-green:8080
weight: 10
基于Header的流量路由
根据请求头中的特定字段(如租户ID、用户等级)路由到不同版本的服务。
// 自研网关路由决策示例
func (r *Router) Route(req *http.Request) string {
tenantID := req.Header.Get("X-Tenant-ID")
userTier := req.Header.Get("X-User-Tier")
// VIP用户路由到高性能集群
if userTier == "premium" {
return "http://premium-service:8080"
}
// 特定租户路由到独立集群
if tenant, ok := r.tenantRoutes[tenantID]; ok {
return tenant.Endpoint
}
return "http://default-service:8080"
}
流量控制与限流
多维度限流策略
支持基于IP、用户ID、API Key等多个维度的限流。
# Kong Rate Limiting 插件配置
plugins:
- name: rate-limiting
config:
minute: 100
hour: 1000
limit_by: consumer
policy: redis
redis_host: redis-cluster
fault_tolerant: true
自适应限流
根据系统负载动态调整限流阈值,避免雪崩效应。
// 自适应限流实现
type AdaptiveLimiter struct {
baseLimit int
currentLimit int
cpuThreshold float64
memThreshold float64
}
func (a *AdaptiveLimiter) AdjustLimit() {
cpuUsage := metrics.GetCPUUsage()
memUsage := metrics.GetMemoryUsage()
if cpuUsage > a.cpuThreshold || memUsage > a.memThreshold {
// 系统负载高,降低限流阈值
a.currentLimit = int(float64(a.baseLimit) * 0.7)
} else if cpuUsage < a.cpuThreshold*0.5 {
// 系统负载低,恢复限流阈值
a.currentLimit = a.baseLimit
}
}
熔断与降级
服务熔断配置
当后端服务失败率达到阈值时,自动触发熔断,快速失败避免级联故障。
# Envoy 熔断器配置
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1024
max_pending_requests: 1024
max_requests: 1024
max_retries: 3
track_remaining: true
降级策略
当服务不可用时,返回缓存数据或默认响应。
// 网关层降级处理
func (g *Gateway) handleWithFallback(ctx context.Context, req *http.Request) (*http.Response, error) {
resp, err := g.forward(ctx, req)
if err != nil || resp.StatusCode >= 500 {
// 尝试从缓存获取降级响应
cachedResp, cacheErr := g.cache.Get(g.buildCacheKey(req))
if cacheErr == nil {
return cachedResp, nil
}
// 返回默认响应
return g.buildDefaultResponse(req), nil
}
return resp, nil
}
可观测性增强
分布式追踪集成
在网关层注入Trace ID,串联整个调用链路。
// OpenTelemetry 追踪注入
func (g *Gateway) InjectTrace(ctx context.Context, req *http.Request) context.Context {
tracer := otel.Tracer("api-gateway")
ctx, span := tracer.Start(ctx, fmt.Sprintf("%s %s", req.Method, req.URL.Path))
defer span.End()
// 将Trace ID注入请求头
traceID := span.SpanContext().TraceID().String()
req.Header.Set("X-Trace-ID", traceID)
return ctx
}
指标采集
采集网关层的关键指标:QPS、延迟、错误率、流量分布。
# Prometheus 指标配置
metrics:
- name: gateway_requests_total
type: counter
labels:
- method
- path
- status_code
- upstream_service
- name: gateway_request_duration_seconds
type: histogram
labels:
- method
- path
- upstream_service
buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
边缘缓存与性能优化
在网关层实现缓存可以显著降低后端压力:
# Envoy 缓存过滤器配置
http_filters:
- name: envoy.filters.http.cache
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.cache.v3.CacheConfig
typed_config:
"@type": type.googleapis.com/envoy.extensions.http.cache.simple_http_cache.v3.SimpleHttpCacheConfig
缓存策略设计:
| 策略 | 适用场景 | TTL 建议 |
|---|---|---|
| 全量缓存 | 静态数据(配置、字典、目录) | 1-24 小时 |
| 条件缓存 | 用户无关但可能变更的数据 | ETag + 短 TTL |
| 部分缓存 | 响应中的公共部分 | 响应片段缓存 |
| 禁止缓存 | 用户私有数据、交易类 | no-store |
缓存的关键决策点:
- 缓存键设计:需包含影响响应结果的所有因素(用户角色、租户、版本),排除无关因素(Trace ID、随机数)
- 缓存失效:主动失效(Pub/Sub 或 xDS 推送)优于被动 TTL 过期
- 一致性权衡:缓存最大收益来自读多写少的场景,写密集型 API 缓存可能造成数据不一致
WebSocket 与长连接支持
现代 API 网关需支持实时通信场景:
# Nginx WebSocket 代理
location /ws {
proxy_pass http://websocket-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400; # 长连接保持
}
# Envoy WebSocket 路由
routes:
- match:
prefix: "/ws"
route:
cluster: ws-cluster
upgrade_configs:
- upgrade_type: websocket
长连接网关的设计考量:
- 连接状态管理:网关需维护大量持久连接,内存占用高于短连接数倍
- 水平扩展挑战:WebSocket 连接有状态,跨网关实例迁移需要粘性会话或共享会话存储
- 心跳与健康检查:应用层 ping/pong 与网关层 keepalive 需配合,避免误断
- gRPC-Web 桥接:浏览器不支持原生 gRPC,Envoy/Kong 可作为 gRPC-Web ↔ gRPC 的翻译层
多租户网关设计
SaaS 场景下,网关需支持多租户隔离:
// 基于 Host 头的租户路由
func TenantRouter(req *http.Request) string {
host := req.Host
tenant := db.LookupTenantByDomain(host) // tenant1.example.com
if tenant == nil {
tenant = db.LookupTenantByHeader(req.Header.Get("X-Tenant-ID"))
}
// 租户级限流
limiter := tenantRateLimiters.Get(tenant.ID)
if !limiter.Allow() {
return "rate_limited"
}
// 租户专属上游集群
return tenant.UpstreamCluster
}
多租户网关的关键能力:
| 维度 | 实现方式 |
|---|---|
| 路由隔离 | 子域名 / 路径前缀 / Header |
| 限流隔离 | 租户独立令牌桶 |
| 认证隔离 | 租户独立 JWT Issuer / Key |
| 审计隔离 | 日志自动注入 tenant_id 标签 |
| 计费统计 | 网关层按租户聚合 QPS / 流量 |
网关函数与边缘计算
现代网关支持在请求/响应链路上注入自定义逻辑:
| 平台 | 扩展机制 | 语言 | 适用场景 |
|---|---|---|---|
| Kong | Lua Plugin | Lua | 认证、变换、日志 |
| Envoy | WASM Filter | Rust /C++/Go | 复杂逻辑、高性能 |
| Nginx | NJS / Lua | JavaScript / Lua | 轻量变换 |
| AWS API Gateway | Lambda Authorizer | 多语言 | Serverless 集成 |
Envoy WASM 是云原生网关扩展的未来方向:WASM 模块在沙箱中运行,支持热加载,且性能接近原生 C++ 插件。
主流网关技术选型对比
| 特性 | Kong | Envoy | Nginx | 自研网关 |
|---|---|---|---|---|
| 性能 | 高 | 极高 | 极高 | 取决于实现 |
| 动态配置 | 支持 | 支持(xDS) | 需reload | 完全可控 |
| 插件生态 | 丰富 | 较少 | 中等 | 需自研 |
| 学习曲线 | 中等 | 陡峭 | 平缓 | 陡峭 |
| 适用场景 | 通用网关 | Service Mesh | 简单代理 | 定制化需求 |
总结
API网关是微服务架构的关键组件,设计时需要综合考虑:
- 路由灵活性:支持多维度路由策略,满足灰度发布、A/B测试等需求
- 统一治理:在网关层实现鉴权、限流、熔断等横切关注点
- 性能与稳定性:选择高性能技术栈,配合熔断降级保障可用性
- 可观测性 :集成追踪、指标、日志,提供全链路可视化
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。