
上周三晚上,一个做跨境电商的朋友给我发消息,说他一个比价采集任务卡住了:四百多万条数据已经进了库,进度条停在 61%,将近四十分钟没动。更烦的是,日志里一条报错都没有——进程活着,队列活着,就是没东西出来。
他开口就是"要不重启一下试试"。我让他先把手指从回车键上挪开:卡住的任务一重启,案发现场就没了。那天晚上我们俩对着监控曲线看了一个多小时,最后把问题定位到一段被目标站限流的出口 IP。顺带地,我把这套排查顺序完整理了一遍,今天写出来,下次你遇到"速度掉下来、进度条卡住",可以照着走。
这两个是完全不同的病,混在一起看会走弯路。
判断方法很简单:别看进度条,看每分钟新增入库条数的曲线。缓慢下滑、从 200 条掉到 80 条再掉到 30 条,这是"变慢",大概率是成功率或单请求耗时在恶化;直接归零、队列水位只涨不消,这是"卡死",大概率是并发资源被占满或者下游堵住了。
我吃过亏的地方在这:早期版本的监控只有进度条,没有吞吐曲线。进度条是累计值,它只告诉你"总量没怎么涨",不告诉你"为什么没涨"。后来我把每分钟吞吐、失败率、单请求耗时的 P50/P95 做成三个基础指标,每次出事先看这三张图,定位速度至少快了一半。
所以在动手之前,先把"慢"拆成可测量的量。没有打点的任务,该做的不是猜,是先补打点。
这是高发原因里排最前的。你的代码一行没改,站点那边动了。
特征很好认:单请求耗时整体抬升,状态码里 429 和 503 冒头,或者——更隐蔽的——返回 200 但内容不对。这种情况是我 2024 年踩过最阴的坑:某站点把一批 IP 段拉进了"软黑名单",不拒绝请求,而是返回一个状态码正常、正文为空的壳页面。抓回来的"数据"每条都是空字段,入库一切正常,看起来采集得很顺利。
验证也简单,两个动作:
如果是限流,对策是降速、拉长请求间隔、换出口,而不是加大重试——重试次数堆上去,只会让限流更狠。
变慢的另一个高发原因,是出口本身不行了。
这里有个经典机制叫重试风暴,值得单独讲。很多采集框架默认"失败就重试三次",这个配置在一切正常时没毛病;可一旦某批出口被目标站标记,链条就变成了:请求失败 → 触发重试 → 重试继续失败 → 重试中的请求占着并发位不放 → 正常请求排不进队列 → 整体吞吐归零。你看到的"卡住",底层是并发位全被注定失败的请求占满了。
那天卡在 61% 的任务就是这个机制:并发 64,其中 58 个在跑已被标记的 IP 段,全部在等 30 秒超时再重试,循环往复。
验证方法是按出口分组统计成功率。把最近一小时的任务按 IP 段(或者隧道出口)拆开,你会发现失败不是均匀分布的,而是集中在某一段。定位到段之后,该熔断熔断,该换段换段,别让任务硬扛。
站点和出口都排除了,才轮到怀疑自己——但这一层的问题往往最有意思。
最常见的是资源泄漏。连接没释放、session 对象一直建、异步任务只提交不回收,表现是内存曲线单调上涨,涨到触发 swap 之后速度出现断崖式下跌——CPU 占用不高,但每一步都慢得离谱,这是典型的 IO 等待特征。诊断用 py-spy dump 看一眼线程栈,或者干脆盯着内存曲线看它是不是"只涨不跌",基本都能锁定。
其次是无效开销。每个请求都重新做 DNS 解析、没开 keep-alive、每次都完整走一遍 TLS 握手——这些单次只有几十毫秒,乘上百万级请求就是几十个小时。去年一个项目把"每次请求新建 session"改成连接复用后,整体耗时直接砍了三成,一行配置的事。
还有一个特别土但真实发生过的:磁盘满了。日志在同步写盘,盘满了之后写阻塞,写阻塞导致入库协程卡住,入库卡住导致队列堆积,整个流水线停摆。表象和"被反爬了"一模一样,df -h 一敲就现形。那次之后,"磁盘剩余空间"被我加进了任务启动前的检查项。
最后一类是任务之外的东西:数据库锁表、慢查询堆积、宿主机被其他进程抢走资源、容器网络配置问题。特点是"你的采集代码怎么看都没毛病",但速度就是上不去。
排查也简单:采集侧没问题,就往下游看。写库耗时是不是涨了、数据库连接池是不是被打满、宿主机是不是有别的任务在抢 IO。这类问题占比不高,但一旦中招,在前三类绕圈的时间会很久——所以顺序上放在最后查,心里要有它。
这套流程跑下来,我给自己沉淀了三条纪律:
进度条是个很诚实的汇报员,但它只汇报结果,不汇报原因。把"慢"拆成指标,一层层往下问,卡住的原因迟早会自己浮出来。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。