前面三篇把进程内 RPC、注册发现、东西向 Service Mesh 串成了服务之间的骨架。还缺两块面对外部与面对排障的拼图:API 网关(南北向入口)与分布式追踪(一次请求如何在几十个服务里被看见)。二者常落在同一条链路上——网关是注入 Trace 的最佳点,也是鉴权限流的总闸。
广告 API——出价回调、投放配置、报表拉取——全部从网关进。没有统一入口,鉴权与限流会散落;没有 Trace 传播,p99 毛刺只能靠「几个团队对表」。本篇作为系列收官,并与 异步削峰 交叉。
本文是微服务与 RPC 系列的第 4 篇(收官)。 系列顺序:① RPC 框架(开篇);② 服务注册与发现深挖;③ 服务网格 Service Mesh 深挖;④ API 网关与分布式追踪(本篇)。
一句话定位:网关收敛南北向的身份、配额与路由;分布式追踪用 TraceId/SpanId 把一次请求在网关→RPC→DB 上的故事讲完整——没有传播,排障就只剩猜。
TL;DR
- 网关职责:鉴权认证、限流配额、路由与协议转换、TLS 终结、灰度、作为可观测入口——不要塞满业务规则。
- 南北向 vs 东西向:网关管进来;服务间交给 Mesh / RPC SDK。
- OpenTelemetry 统一 Trace/Metrics/Logs 的埋点与导出;Collector 做缓冲、采样与多后端扇出。
- 传播标准:W3C
traceparent(或 B3)沿 HTTP/gRPC metadata 传递;网关创建根 Span,下游创建子 Span。 - 采样:头部采样保基线;错误/慢请求强制采样;全量仅短时排障,否则存储与费用爆炸。
- Jaeger / Tempo 等后端负责查询与展示;要和日志里的 TraceId 字段对齐。
- AdTech 血泪:网关剥掉自定义 header、未接 OTel,竞价超时只能按服务各自日志「时间线人肉对齐」,定位一次故障耗时数小时。
Table of contents
Open Table of contents
1. API 网关:南北向总闸
网关是边界控制器,不是第二个业务单体。
1.1 该做什么
- 身份与鉴权:JWT/OAuth2/API Key、服务账号;把验签放在边缘,避免每个服务重复实现。对内服务账号与对外用户身份分开模型,别混用一套 token。
- 限流与配额:按租户、AppId、IP 令牌桶/漏桶;与广告「客户 QPS 合约」对齐。尖峰还可挡在门外,把突发交给队列(见 异步削峰)。
- 路由:按路径、header、版本把流量导向不同服务或金丝雀集合;HTTP→gRPC 协议转换也常放这里。
- 安全:TLS 终结、WAF、IP 允许列表;与 Mesh 的 mTLS(东西向)形成纵深。
- 可观测入口:创建/透传 Trace,产出入口 RED 指标——这是全链路追踪的第一环。
网关还常做请求体大小限制、超时预算注入、响应缓存(仅对幂等读)。每加一项能力,都要问:这是边界职责,还是业务在偷懒把逻辑往上推?
1.2 不该做什么
- 把复杂业务编排、定价、预算逻辑堆进网关插件;
- 替代服务间 Mesh 去做细粒度东西向策略(除非体量极小、有意合并);
- 无审核的「热更新插件市场」——网关一挂或一错,全站入口没了;
- 在网关里做重 CPU 的业务计算(压缩/鉴权以外)——它会成为垂直扩展瓶颈。
常见产品:Kong、APISIX、Envoy Gateway、云厂商 ALB/API Gateway、自研基于 Netty/OpenResty。选型看插件生态、配置热更、与 K8s/Mesh 的集成,而不是功能清单长度。多活场景要规划跨机房入口与调度,避免单区域网关成为全球单点。
1.3 与 Mesh Gateway 的关系
Istio Gateway / Envoy Gateway 可以把南北向也做成「网格的门」。组织上有两种常见切分:
- 专用 API 网关产品负责商业化插件(计费、开发者门户)、粗粒度限流;Mesh Gateway 只管集群入口与 TLS;
- 统一 Envoy 家族:南北向与东西向同一套技术栈,降低心智,但插件与多租户能力要自己补。
无论哪条,限流与鉴权的权威来源要唯一——两侧各做一套且规则不一致,是排障噩梦。
2. 分布式追踪:把调用链画出来
没有统一的 TraceId 传播,微服务排障就退化成「几个人对着几份日志对表」。
2.1 Trace / Span / Context
- Trace:一次外部请求的完整故事,唯一 TraceId;
- Span:故事里的一步(处理 HTTP、发 RPC、查 Redis),有 SpanId 与 ParentSpanId;
- Context 传播:把 TraceId 与当前 SpanId 塞进出站请求的 header/metadata,下游据此续写。
W3C Trace Context 的 traceparent 形如:00-<trace-id>-<span-id>-<flags>。gRPC 用 metadata,HTTP 用 header;Mesh Sidecar 也可自动繁殖,但应用自己出站若不透传,仍会断链——异步线程池、消息队列尤其是重灾区,要显式传递 context 或用 instrumented 执行器。
2.2 网关作为根
最佳实践:
- 外部请求进入网关时,若无
traceparent则生成新 Trace;若有则继续(便于外部上游关联); - 根 Span 覆盖「网关处理总耗时」;
- 转发上游时必须写入传播头,并记录路由目标、租户、HTTP 状态等属性;
- 与鉴权失败、限流拒绝类短路径也要留 Span——否则「被网关挡下的流量」在 Trace 里消失,误判为客户没打进来;
- 把 TraceId 打进网关访问日志,便于从「客户投诉的 request id」一键跳转。
2.3 跨异步边界
RPC 同步链路上的传播相对直观。难点在:
- 线程池切换:新线程丢了 ThreadLocal context;
- 消息队列:生产时写入 header,消费时恢复;
- 定时任务 / 批处理:人为创建根 Trace,并带上业务主键属性。
没有跨异步传播,「竞价 → 异步写明细 → 对账」在 Jaeger 上会碎成三段,排障价值大减。
消息队列场景额外注意:一条消息被多次投递时,每次消费可以是新 Span,但应共享同一条业务 Trace(或至少用 messaging.message_id link)。否则你会在系统里看到「很多短 Trace」,却拼不出一次客户请求的全貌。OpenTelemetry 的 span link 就是为此准备的。
时钟也重要:跨主机 Trace 的时间线依赖 NTP;时钟漂移严重时,火焰图会出现「子 Span 开始时间早于父 Span」的怪相。可观测基建和机房基础一样,时钟是隐形依赖。
3. OpenTelemetry 采集链路
SDK 负责产数,Collector 负责治数,后端负责存与查。
3.1 为什么经 Collector
应用不要每个实例直连 Jaeger:
- 后端升级/宕机不应反压业务;
- 尾部采样、属性脱敏、降基数放在 Collector 统一做;
- 可同时 fan-out 到 Trace 后端 + 对象存储 + 分析管道。
Collector 本身也要高可用与容量规划——它挂了或积压了,观测盲区会系统性出现。把它当「可观测数据面的网关」看待。
3.2 采样策略
| 策略 | 含义 | 适用 |
|---|---|---|
| 头部(Head)采样 | 根 Span 决定是否采整条 | 常态基线,如 1%–10% |
| 尾部(Tail)采样 | 看完整条再决定留不留 | 保留错误/慢请求 |
| 强制采样 | 错误、特定租户、debug header | 排障与大客户 |
广告竞价 QPS 极高时,全量 Trace 会先打爆存储和钱。正确做法是:低比例头部采样 + 错误/高延迟强制保留 + 关键租户提高采样。指标侧用高基数谨慎的 RED/SLO,Trace 负责「为什么慢」。
尾部采样需要 Collector 缓冲完整 Trace,成本更高、也更强——值得为「只留失败」买单。
3.3 与日志、指标对齐
每条日志打上 trace_id;Metrics 示例带 service.name 等资源属性与 Trace 一致。排障路径变成:告警 → 看入口成功率 SLO → 抽一条 Trace → 跳日志——而不是三套系统各猜各的。
属性要降基数:campaign_id 可以放进 Span attributes(按需查询),不要打进 Prometheus label。否则你会先见证监控系统崩,再轮到业务系统。
4. 网关 × Mesh × RPC:边界怎么划
- 网关:南北向身份、配额、TLS、入口 Trace、粗路由;
- Mesh:东西向 mTLS、细路由、重试熔断(注意与 SDK 别双重)、自动 span;
- RPC SDK:业务语义超时、幂等、编解码、同构栈的极致延迟路径。
清晰边界比「所有能力叠三层」重要——本系列讲到的雪崩,根因往往是责任叠乘(重试×重试、发现脏列表×重试、入口无 Trace×各自为战)。
一张可执行的协作表:
| 关注点 | 权威层 |
|---|---|
| 对外鉴权 / 租户配额 | 网关 |
| 服务间 mTLS / AuthZ | Mesh |
| 业务幂等与写重试 | 应用 / RPC SDK |
| Trace 根与传播 | 网关创建 + 全链路透传 |
| SLO 入口成功率 | 网关指标为准 |
4.1 限流:网关挡尖峰,下游仍要自保
网关限流保护的是入口合约;它挡不住:
- 内部重试放大(系列里反复出现);
- 同集群其它服务互调绕过网关;
- 批任务与脚本走集群内地址。
所以限流是纵深:网关配额 + Mesh/应用舱壁 + 依赖方超时。只用网关一道闸,东西向一抖照样死。
4.2 开发者体验:Portal 与契约
成熟的 API 网关背后往往还有开发者门户:密钥申请、配额查看、OpenAPI/Protobuf 文档、沙箱环境。这不改变技术分层,但决定「对外 RPC/HTTP 会不会变成无人维护的口头协议」。契约变更走网关侧的版本与废弃策略,与 §1 的路由版本相呼应。
4.3 BFF 与网关:别混为一谈
BFF(Backend for Frontend) 为特定客户端聚合接口,常含业务编排;API 网关做边界策略。把 BFF 逻辑写进网关插件,短期快、长期不可测:发布耦合、多端互相踩踏。正确姿势是网关后接独立 BFF 服务,网关只负责把 /mobile/* 路由过去。
对外 OpenAPI 与对内 Protobuf 可以并存:网关做协议转换时,要在 span 上标清 http.route 与下游 gRPC method,否则 Trace 里只看见一堆 POST /,广告排障时毫无辨识度。
参考
- OpenTelemetry. OTel Documentation:API/SDK、传播与 Collector。
- W3C. Trace Context:
traceparent/tracestate标准。 - Jaeger. Jaeger Architecture:采样、存储与查询路径。
- CNCF. Cloud Native API Gateway landscape:网关与网关类项目总览。
- Google. Dapper, a Large-Scale Distributed Systems Tracing Infrastructure:现代分布式追踪思想源头。