ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

API频繁断连?从抓包到证据链,两步证明问题不在你这边

API频繁断连?从抓包到证据链,两步证明问题不在你这边 1. 现象描述与初步猜测1.1 从“两天排查”说起这个标题写出来我自己都想笑。上上周四我们的订单回调任务又开始在凌晨三点报警日志里一堆Connection reset by peer和Read timed out。我本来以为是新上线的Java接口又没处理好连接池结果整整排查了两天最后把抓包记录甩给API服务商对面才承认是他们某区域网关机房在高峰期主动断连接。这篇文章就来复盘一下我是怎么一步步把问题从自己身上剥离干净证明“问题真不在我这边”的。先说下背景我们有一个自动化脚本每天定时通过Python调用某家大模型平台的接口做文本归类同时还有一个同事用VS Code里的Continue插件直接请求DeepSeek API做代码提示。两边在同一个时间段都开始频繁断连表现是请求发出去后要等很久才超时或者刚连上就被踢下来错误信息还不统一一会儿Connection reset一会儿Read timed out偶尔还会冒出来一个RemoteDisconnected。因为现象跨度太大第一反应就是“我这边出问题了”。1.2 第一轮自查清单按惯性思维我先列了一个自查清单把最可能出问题的地方全捋了一遍。这里建议大家都养成习惯遇到API断连不要急着改业务代码先把下面这些基础项确认过再动手。代码层面超时时间是否设置得太短有没有显式关闭连接是不是每次请求都新建了一个Session导致TCP握手开销大又容易出现半开连接服务器层面系统负载高不高TCPTime-Wait状态是不是堆积了ulimit -n文件描述符是否足够网络层面服务器DNS解析是否正常出口IP有没有被对方拉黑有没有经过一层不稳定的代理或负载均衡我全都过了一遍。代码用的是requests库设置了timeout(5, 10)每次调用后关闭连接服务器是4核8G的云主机负载常年不到0.3DNS用dig查过解析结果正常并且稳定出口IP也换过问题依旧。最气人的是我用curl -v手动请求同一个接口第一次能通第二次就卡住第三次直接Connection reset而我什么代码都没有跑还是在同一台服务器上。这就很说明问题了至少不是我的业务逻辑导致。2. 系统排查流程2.1 抓包让证据说话排查到第二天上午我开始认真抓包。用的是最土但最有效的组合tcpdump抓网络层数据包curl -v看HTTP层的交互过程。命令很简单sudo tcpdump -i eth0 host api.xxx.com -w api_problem.pcap同时另开一个窗口用curl重复请求for i in $(seq 1 20); do curl -sS -o /dev/null -w attempt $i: http_code%{http_code} time_total%{time_total}\n \ -H Authorization: Bearer YOUR_KEY \ https://api.xxx.com/v1/chat/completions -d {prompt:test} 21 done抓下来的数据包用Wireshark打开几个关键信息非常有意思TCP三次握手非常快SYN包发出去对端几十毫秒就回了SYN-ACK说明网络链路和服务器本身是通的。但问题出在第四次交互上——当客户端发完HTTP请求之后对端没有正常返回HTTP响应而是直接回了一个RST包。这说明连接被对端主动重置不是网络丢包导致的重传失败。因为我这边包都收到了是对方在应用层主动把连接断掉。与此同时我注意到一个细节curl -v显示的Connected to api.xxx.com后面有时候会卡住几秒才进入TLS handshake有时候TLS握手成功后读取响应的阶段又卡住。这种“能连上但读不到数据”的典型表现通常都指向两个方向要么是服务端在接收完请求后处理超时要么是对端网关在中间掐断了连接。2.2 分段排查从本地到远端每一跳为了排除中间网络设备的问题我对目标域名跑了mtr更高级的traceroute持续超过1000个包观察每个节点的丢包率和响应抖动。mtr -rw -c 1000 --no-dns api.xxx.com数据出来之后中间路由的丢包率基本都是0%只有最后一跳偶尔出现0.2%的丢包。这个0.2%其实也属于正常范围因为最后一跳是目标服务器本身它对自己发过来的ICMP应答会有一定优先级限制。如果中间任何一跳丢包率超过5%那我们还能怀疑是运营商骨干网的问题但这里没有。所以我基本可以排除“我们去机房的路上有干扰”这个假设。接下来我尝试了不同端口验证。因为HTTPS走的是443端口我用tcping测试了443端口的连接耗时同时也测了22端口SSH做对比tcping api.xxx.com 443 tcping api.xxx.com 22结论是443端口连接成功率比22端口差很多而且443端口还经常连接成功之后立即被断开。既然同一个IP地址、同一个网络路径只是端口不同表现完全不同那问题一定出在监听443的服务上也就是目标API的网关或负载均衡层而不是基础网络。2.3 排除代码与配置干扰很多人会说我是不是本地代理环境有问题。为了堵住同事的嘴我直接用一台全新的、没有配置任何代理的裸机跑最小复现脚本只用来测试脚本短得可怜import requests url https://api.xxx.com/v1/chat/completions headers { Authorization: Bearer test-key, Content-Type: application/json } data { model: example-model, prompt: hello, max_tokens: 32 } session requests.Session() for i in range(10): try: r session.post(url, headersheaders, jsondata, timeout(5, 15)) print(i, r.status_code, r.text[:50]) except Exception as exc: print(i, type(exc).__name__, str(exc))这台裸机上既没有跑业务服务也没有自定义路由或防火墙策略Python环境也是刚装好的。结果连续十次请求中有四次超时三次Connection reset三次成功。这已经足够说明问题了重度复现的条件完全不依赖我们现有环境。2.4 横向对比多API同一个脚本我顺手把市面上常用的几家API都测了一遍包括阿里云百炼、Kimi、豆包、讯飞星火和DeepSeek。我把它们的鉴权方式、请求URL和参数统一封装了一下然后对每个API连续请求50次统计成功率、平均响应时间、超时次数。最终结果很直观同一台服务器、同一段网络环境阿里云百炼和Kimi的稳定性很好50次请求只有一两次超时豆包和DeepSeek在高峰期会出现间歇性失败而出问题的那个API服务商成功率只有60%上下。这又给了我一个非常有力的证据如果是我本地网络或服务器的问题不可能只对某一家API生效其他家一点事都没有。这跟人去医院看病是一个道理你换个科室测同一项指标都正常那就是原科室的设备或流程出问题了。3. 核心问题定位对端服务或网络路径问题3.1 数据对比什么时间段断断的频率既然锅大概率不在我这边我就开始记录断连的规律。我把每一次请求的时间戳、耗时和错误类型都写进日志然后隔一会儿统计一次。连续跑了约15小时后数据里的规律非常明显。凌晨2点到6点成功率高达99%几乎没有断连。上午10点到下午4点成功率95%左右偶发超时。晚上8点到11点成功率直接掉到75%Connection reset频发。这个曲线刚好和对方API服务的客户端活跃时段重合。为了验证是不是我们自己服务器被攻击我检查了服务器上的连接数、CPU负载和网络流量数据都正常。而且我用同一个脚本去请求另一家API在同样的晚上时间段成功率还是稳定在95%以上。于是我有理由相信对方的网关在业务高峰时出现了过载或恶意限流。同时我去查了对方官方状态页和运维公告发现当天晚上确实有“服务异常”和“请求延迟增加”的提示虽然第二天早上他们更新说“已恢复”但我们的实际观察是仍然不稳定。这种“嘴上说恢复实际还在抖”的情况并不少见所以不能只看状态页要自己持续监控。3.2 是限流还是故障如何区分这里要分享一个区分经验限流通常是有意识的服务端一般会返回429 Too Many Requests或503 Service Unavailable并且带上Retry-After头部但故障导致的断连往往表现为连接层重置或者请求发出后长时间没有响应超时后客户端主动断开。从抓包数据看我们的问题几乎都是对端发了RST而HTTP响应码很少出现。这说明请求根本没到达后端的业务逻辑层在网关层就被丢弃或拒绝。你可以把网关想象成餐厅门口的迎宾员如果只是人太多迎宾员会告诉你“没位子请晚点再来”对应429但如果迎宾员自顾不暇门口乱成一团客人就根本进不了门也不会有人给一个明确答复对应RST。显然我们遇到的更像后者。为了进一步确认我尝试降低并发数到每5秒一次同样会断把请求body减小到十几个字节还是会断。这说明不是我们的单次请求参数触发了WAF之类的规则而是网关对所有客户端都在执行某种粗粒度策略。3.3 最终确认问题不在我这边抱着试试看的心理我提交了工单附带了三样证据最小复现脚本输出的错误日志tcpdump抓包文件关键部分截图mtr长时间统计报告。客服一开始还是老一套让我检查“本机防火墙、DNS、代理设置”甚至怀疑我用作弊性的并发工具。我回复说同一脚本请求阿里云百炼和Kimi都正常你们是唯一失败的并且我的抓包里严格显示TCP三次握手成功是你们在收到HTTP请求后主动返回RST。你可以去查服务器上的TCP连接记录或者网关日志看有没有来自我这个出口IP的入网数据。结果他们再回复时终于承认“经排查是我们华北区域某网关集群出现异常已触发自动运维”。这时候距离我开始处理已经超过了48小时。4. 如何让别人相信“问题不在我这边”4.1 用最小复现脚本留档很多开发者在排查问题时喜欢直接在项目里加日志改超时参数加一堆重试逻辑最后把自己代码改得面目全非还没解决。我的习惯是先隔离出一个最小复现脚本这个脚本只做一件事情循环调用接口并记录结果。它不依赖项目代码不依赖复杂的数据库和中间件保证别人拿到同样环境也能复现。最小脚本的输出尽量包含每次请求的时间戳精确到毫秒HTTP状态码如果有异常类型和消息响应耗时请求ID如果服务商返回了用Python写可以这样import time import requests from requests.exceptions import RequestException URL https://api.example.com/v1/your_endpoint HEADERS {Authorization: Bearer your_api_key, Content-Type: application/json} PAYLOAD {query: test} for attempt in range(50): t0 time.time() try: resp requests.post(URL, headersHEADERS, jsonPAYLOAD, timeout10) elapsed time.time() - t0 print(f[{time.strftime(%Y-%m-%d %H:%M:%S)}] attempt {attempt}: {resp.status_code}, time{elapsed:.3f}s, body{resp.text[:100]}) except RequestException as exc: elapsed time.time() - t0 print(f[{time.strftime(%Y-%m-%d %H:%M:%S)}] attempt {attempt}: ERROR, time{elapsed:.3f}s, type{type(exc).__name__}, msg{exc}) time.sleep(1)注意这里的timeout10是连接和读取的总超时如果接口本身响应慢会误报超时。但我们要抓的是“明明连接成功了读取时被对端重置”的情况所以超时设置可以稍微宽松一点比如(5, 20)。4.2 收集证据链跟服务商拉扯的时候光说“我的代码没问题”是没用的对方只会让你自查。你需要拿出一个完整的证据链让对面无话可说。我的证据链一般按这几步组织网络连通性证明mtr -rw -c 1000 host的输出展示中间链路没有明显丢包。TCP交互证明tcpdump抓包文件重点展示TCP三次握手成功随后对端发送RST或延迟很久不响应。接口对照实验同一脚本、同一时间窗口请求其他API成功而请求该API失败。服务状态截图对方官方状态页当时标记的异常时间点跟我本地时间轴对得上。把这些东西打包成一个压缩文件附上简要说明文档在工单里一次性发出去。大多数人连第一步都懒得做你跟他说“我给你抓包了”他至少会把你往“认真排查过”的队列里排。4.3 和对方客服有效沟通的技巧给API服务商提工单也有技巧。错误的大白话“你们的API有问题老是断。”基本不会得到有效响应。有效的提法应该是我们于2025年某月某日20:13至22:30期间调用贵方/v1/chat/completions接口出现大量Connection reset和Read timed out成功率约75%。我们已用最小脚本在纯净环境复现相同脚本调用其他API服务正常。抓包显示TCP握手成功对端在收到HTTP请求后返回RST问题疑似发生在贵方网关或负载均衡层。请求ID示例xxx-xxx-xxx如果有。可以提供抓包文件和日志。这样对方知道你已经做了功课他们能直接跳过多轮无效沟通直接去查网关。我自己这次就是工单来回了两轮后对方直接拉了内部团队处理效率高很多。5. 同类场景的避坑经验5.1 “API调用老是断”常见的5个原因虽然这次问题确实不在我这边但说实话我自己平时遇到的大多数“联动断开”问题有相当比例还是自身配置不合理。这里我把常见原因按出现频率排了个序方便你对照排查。原因分类典型现象自查方向超时设置过短请求稍微慢一点就直接Timeout检查timeout参数区分连接超时和读取超时连接池未复用频繁建立新TCP连接导致握手开销大、端口耗尽用requests.Session()或http.client长连接Java里使用连接池管理HttpClientDNS解析不稳定解析结果随机变慢或返回多个IP但某些IP不通用dig多次查看TTL尝试固定IP或换用HTTPDNS本地代理或防火墙干预请求时通时不通延迟抖动严重临时关闭代理、云盾/IP白名单、杀软再跑最小脚本服务商限流或网关故障高频时段固定失败并且多客户端共同失败查看官方状态页、对比多个API服务、抓包观察RST信号在排查时我建议先按表格里的前四项过一遍如果全排除了再往服务商那边靠不要下意识就降级到“对方问题”的心态。否则排查容易变成甩锅反而找不到真正的根因。5.2 排查工具清单与常用命令工欲善其事必先利其器。整理一些我实际用过且觉得高效的命令以后遇到了直接用。curl 带输出详情适合快速验证curl -v https://api.example.com/path可以看到TLS握手、HTTPS状态等。加-w %{http_code} %{time_total}\n能拿到耗时。tcpdump 抓包tcpdump -i eth0 -w /tmp/api.pcap host api.example.com抓完用Wireshark打开看有无RST。mtr 持续监测mtr -rw -c 1000 --no-dns api.example.com比traceroute更准能输出丢包率。tcping 端口测试tcping api.example.com 443纯TCP连接测试不受HTTP影响。strace 跟踪系统调用strace -f -e tracenetwork python3 test.py看程序每一步系统调用的返回值对定位connect和read阶段的超时很有帮助。netstat 查看连接状态netstat -antlp | grep :443查看ESTABLISHED和TIME_WAIT数量的变化判断连接池是否正常。如果你用的是Java开发API接口供外部调用特别要注意连接池参数。我之前写过一个Java应用的接口内部用Apache HttpClient默认连接池最大连接数太小底层连接空闲回收时间过长结果上游调用方频繁报连接超时。后来把setMaxTotal和setDefaultMaxPerRoute调大又设置了setValidateAfterInactivity情况立刻好转。这是很典型的“自己这边的问题但是表现成API不稳定”。5.3 针对大模型API与低代码平台的额外提醒最近关于“DeepSeek API如何调用”“阿里云百炼API调用示例”“Kimi API调用”“豆包如何调用API接口”这类问题特别多很多朋友是刚接触大模型API的。新手常犯的毛病是看着文档里给的curl示例能通但放到自己的Python脚本或Java程序里就开始断。这里我额外提醒几个点鉴权参数不要拼接错。DeepSeek用的是Authorization: Bearer keyKimi也是这样但有些老平台要用api_key字段。配置好之后先用官方在线调试工具试通再写代码。注意请求超时与流式响应。调用大模型API时如果开启stream服务端会分多次返回数据你的代码要能处理“接收中”的情况不要因为一时没数据就断开。读取超时时间至少要给到60秒或更长。低代码平台调用API时要留意出口IP。很多低代码平台会给你一个共享出口IP这个IP可能因为别人滥用而被目标API服务商限流。表现就是你做了很简单的请求也老断但换了本地环境就很稳。出现这种情况可以试着在低代码平台里配置自定义DNS或者联系平台运维查一下出口IP是否在对方黑名单里。本地调用大模型API的时候第一次冷启动特别慢。所以单独设置较长的连接超时5秒和读取超时60120秒是必要的。如果你读超时只有30秒碰上模型推理时间超过30秒的请求必然会报Read timed out但换个请求小一点的内容又正常。这个也容易被误判为“服务端断连”。把Dify工作流转成API给其他软件调用本质上你是做了一层“API二次封装”。你要同时考虑上游大模型API的稳定性、中间工作流的处理耗时以及下游客户端的超时设置。我建议你在你的API层给下游一个明确的Retry-After或503响应而不是直接断开连接否则下游会以为你系统崩了。5.4 经验遇到“无缘无故断连”先别改代码最后说个实在的经验。遇到API断连最忌讳的事是“瞎改”。你可能把这几次超时归咎于超时阈值太小于是把超时从10秒改到30秒又发现还是断于是加重试重试之后发现原来是一次性的偶发错误却因为回调里没有幂等设计导致重复数据最后越扯越深甚至动了业务逻辑。而真正的原因可能只是对端某个节点在特定时间段的负载抖动。我的建议是先花10分钟做对照组实验同一请求连续发50次或100次统计成功率。如果成功率不是100%哪怕只是98%都说明这条API链路本身存在间歇性故障不是偶然噪声。这时候再决定要不要改超时或重试逻辑。有一次我排查一个Python调用讯飞星火API的问题就是因为间歇性断连我在代码里加了复杂的重试和缓存机制结果不仅没解决问题还让请求变得更慢。后来冷静下来把重复调用改成session然后抓包确认是对端需要携带app_id参数放在请求头里少传一个就会概率性断开。这毛病改完后稳定性直线上升。所以遇到“断连”先别着急加防御代码先通过“最小脚本抓包横向对比”三步法确定责任域。排查API断连没有玄学就是缺什么补什么一层一层剥开。希望我这个折腾两天才搞定的案例能让你下次花20分钟就定位问题。想更快可以直接把本文里提到的命令和脚本复制过去用按顺序跑一遍大概率能少走很多弯路。
返回列表