Go 发一个请求只要一行,但它可能把服务拖垮

Go 发一个请求只要一行,但它可能把服务拖垮

一、基础:标准库就够用

Go 的 net/http 包同时提供客户端与服务端能力,客户端核心是 http.Client。发 GET 用 http.Get,发 POST 用 http.Post,提交表单用 http.PostForm;需要自定义请求头或认证时,用 http.NewRequest 构造请求,再交给 client.Do 发出。响应通过 resp.StatusCode 看状态码,通过 resp.Body 读内容。

四种发法对应四类需求:读数据、写 JSON、提交表单、带凭证调用接口。选错方法不会报错,只会让服务端收到不符合预期的内容。

二、陷阱一:错误其实有两层

很多人只检查了 err,就认为请求成功。但原文的最佳实践明确要求:除了请求过程中的错误,还要检查响应状态码是否为 200。网络通了、服务端返回 500,err 依然是空。

判断标准:err 回答"有没有发出去、有没有收到响应",状态码回答"对方是不是正常处理了"。两者都过了,才算成功。

三、陷阱二与三:连接池要配,Body 必须关

http.Client 内置了连接池管理,默认行为能用但不一定合适。原文给的做法是自定义 http.Transport,控制两个关键参数:最大空闲连接数(示例取 10)与空闲连接超时(示例取 30 秒)。

参数作用
MaxIdleConns控制可复用的空闲连接上限,直接影响高频调用时的握手开销
IdleConnTimeout空闲连接保留多久,太长会占资源,太短会频繁重建

这两个值没有通用最优解,取决于你的 QPS 与对端服务的连接策略。照抄示例数字往往比默认值更糟。

再说 Body。原文每个示例里都有一句 defer resp.Body.Close(),这不是样板代码。响应体不关闭,底层连接无法回到连接池,请求量一上来就会出现连接堆积、句柄耗尽。这个错误的隐蔽之处在于:小流量测试永远发现不了。

四、陷阱四:并发不等于开一堆协程

原文的性能优化示例用 sync.WaitGroup 管理并发请求:每启一个协程先计数加一,协程结束时减一,主流程等待归零。它解决的是"等所有请求都回来"的问题,但不解决"同时发太多把对方打挂"的问题——后者要靠限流,原文没有展开,属于需要自行补齐的部分。

五、认证与数据格式

  • JSON:把结构体编码成 JSON,同时把 Content-Type 设为 application/json,两者缺一不可。
  • 表单:用 url.Values 组织字段,再走 PostForm。
  • Basic Auth:请求对象自带 SetBasicAuth,不必手工拼请求头。提醒一句:Basic 认证只是 Base64 编码,必须走 HTTPS。

六、上线前的检查清单

  1. err 与状态码都判断了吗?
  2. 每个响应体都 defer 关闭了吗?
  3. 连接池参数是按业务量调的,还是照抄的?
  4. 并发请求有没有配上数量上限?
  5. 凭证走的是环境变量还是硬编码?

这五条都过了,一个"能跑"的客户端才算变成"敢上线"的客户端。