在 go-zero 微服务里"调外部接口"通常指调用第三方 HTTP API(支付、行情、短信等)。框架内置的 rest/httpc 包能在"几乎零额外代码"的情况下,自动叠加超时、熔断、链路追踪等治理能力。本文给出当前版本(master/latest)真实可用的写法与完整示例。
按"被调用方"与"治理强度"区分,推荐度依次递减。
| 场景 | 推荐做法 | 治理(超时/熔断/追踪) |
|---|---|---|
| 调用第三方外部 HTTP API | rest/httpc(httpc.NewService / Service.Do) | 自动:按服务名熔断 + OTEL 追踪 |
| 调用内部其他 RPC 服务 | zrpc client(client.NewXxx(zrpc.MustNewClient(...))) | 自动:服务发现 + 超时 + 熔断 |
| 快速脚本 / 临时调用 | 标准库 net/http | 无,需自己加(不推荐用于生产) |
httpc。它和 rest 服务端共用同一套中间件语义,调用失败会自动计入以"服务名"为维度的熔断器。
先说最重要的版本事实:当前 go-zero 的 httpc 没有 Get/Post 便捷方法,统一用 Service.Do(ctx, method, url, data);data 会按标签自动映射成路径/查询/JSON/Header。
Do(ctx, method, url, data),没有 client.Get(url, &v) 这种写法(老版本才有)。resp.Body.Close() 且手动判断 resp.StatusCode,否则 5xx 会被熔断计数而你的代码却以为成功了。
带 json 标签的结构体被编码进请求体,并自动设置 Content-Type: application/json。
Do 的 data 通过 go-zero 的 mapping 包解析,按结构体 tag 决定放哪里。这是 httpc 比裸 net/http 好用的地方。
| tag | 含义 | 放到请求的哪里 | 示例 |
|---|---|---|---|
| path | 路径变量 | 替换 URL 中的 :name | id int64 `path:"id"` → /users/123 |
| form | 表单/查询 | URL 查询字符串(GET 常用) | symbol string `form:"symbol"` |
| json | JSON 体 | 请求体(POST/PUT) | amount int64 `json:"amount"` |
| header | 请求头 | HTTP Header | token string `header:"X-Token"` |
真实项目里,外部客户端应创建一次并注入 ServiceContext,在 logic 里调用,避免每次请求都新建连接。
l.ctx:这样外部调用的链路追踪(OTEL span)能自动串联到当前请求,超时也会随父 context 一起生效——这是 httpc 相比裸客户端的隐藏价值。
httpc 的 Option 就是 func(r *http.Request) *http.Request,用它统一加签名、Token、Trace。
NewService 默认用 http.DefaultClient(无超时),生产务必用 NewServiceWithClient 指定 Timeout,否则下游卡死会拖垮你的服务。
如果"外部接口"指的是集群里另一个你自己的服务,优先用 gRPC(zrpc),比 HTTP 更高效且自带服务发现。
配置(etc/xxx.yaml)示例:
httpc 自动按"服务名"接入熔断器。理解它怎么判定成功/失败,能少踩很多坑。
HTTP 状态码 < 500(含 4xx)、context 被取消(用户主动取消)。
状态码 ≥ 500、超时(DeadlineExceeded)、网络错误(连接拒绝 / DNS 失败)。
Do 返回 err,一定要 resp.StatusCode 判断。NewServiceWithClient 设 Timeout。user-api)。把上面的点串起来:带超时、判状态码、读 JSON、处理错误。
net/http 更适合生产。