上周排查一个接口超时问题,测试同事说“我本地curl是测试通的”,我让他把命令发过来。测试他发的测试是:curl http://api.example.com/health。问题出在哪?测试他测的是HTTP,线上跑的测试是HTTPS。curl默认不跟随重定向,测试也不校验证书链,测试更不会告诉你响应时间分布。测试一条命令跑通,测试跟接口真的测试健康,是测试两码事。
curl最容易被低估的测试地方,是测试它默认只输出响应体。状态码、测试响应头、连接耗时、TLS握手细节,全被吞掉了。-i能带上响应头,-v能看到握手过程,-w才是真正干活的那个参数。我习惯用这样一条:
curl -o /dev/null -s -w "dns:%{ time_namelookup} connect:%{ time_connect} tls:%{ time_appconnect} ttfb:%{ time_starttransfer} total:%{ time_total} code:%{ http_code}\n" https://api.example.com/health 输出大概长这样:dns:0.003 connect:0.012 tls:0.045 ttfb:0.187 total:0.190 code:200。一眼就能看出时间花在哪。ttfb减去tls,是服务端处理时间。如果这个差值超过200ms,基本可以怀疑后端有慢查询或者锁竞争。total和ttfb接近,说明响应体很小,传输不是瓶颈。
有个坑我踩过不止一次。-w里的%{ time_total}包含重定向跳转的累计时间,但%{ time_connect}只算最后一次连接。如果接口有302跳转,这些数字会对不上。要测重定向,加-L,但加了之后%{ http_code}拿到的可能是最终页面的状态码,中间跳转的状态码得靠-v或者--trace-time去看。
证书问题也常被忽略。内网自签证书的接口,curl默认直接报错退出,很多人加个-k就完事。但-k跳过的是证书校验,不是TLS握手。握手本身如果因为协议版本或者加密套件不匹配失败,-k救不了。用--tlsv1.2强制指定版本,或者--ciphers指定套件,才能定位到具体哪一层出了问题。
POST请求的测试又是另一回事。-d默认发application/x-www-form-urlencoded,现在大部分API要JSON。得写成:
curl -X POST https://api.example.com/v1/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "name":"test","qty":1}' \ -w "\ncode:%{ http_code} time:%{ time_total}\n" 注意-d和-X POST同时用的时候,-d本身就会把方法变成POST,-X POST是多余的。但如果用了-I(HEAD请求)又想发body,那就得显式指定-X POST,否则curl会忽略body。这种细节文档里写得含糊,试一次才知道。
批量测试的时候,我不用循环脚本,直接用--parallel加--config文件。把几十个URL写进一个文件,每行一个url = "...",然后curl --parallel --parallel-max 10 -K urls.txt -o /dev/null -s -w "%{ url_effective} %{ http_code} %{ time_total}\n"。比写for循环快得多,而且并发控制更干净。
curl的--resolve参数也值得单独说。测试还没切流量的新节点时,curl --resolve api.example.com:443:10.0.1.5 https://api.example.com/health,不用改hosts,不用动DNS,直接指定IP。配合-w看时间,能对比新旧节点的延迟差异。这个用法在灰度发布阶段特别顺手。
说到底,curl测试的核心不是“能不能通”,而是“通得怎么样”。一条命令加上-w和-o /dev/null,信息量比默认输出大一个数量级。别再用curl url糊弄自己了,那只能证明DNS没挂。