接口全调通那天我挺得意的,九个站一套脚本,全部跑起来了。
真正让我难受的是后面两个月。有两回站点上冒出重复的文章,还有一回整整三天一篇没发出去,我完全没察觉。都是自己翻站才发现的,日志干干净净。
这里说的是任务这一层的事,不是接口。接口那部分我吃过亏,另写过。这篇只讲我怎么被重复发、漏发、同站并发这三样东西坑的,以及最后改成什么样。
先说重复发。那天晚上十一点多,脚本跑一批任务,有个站的回读接口响应特别慢,等了三十秒没回来,脚本按超时算失败,进了重试队列。重试那次很顺,五秒就返回 200,回读也过了,标成功。第二天上午我登后台补别的设置,看见列表里两篇一模一样的标题,一篇时间 23:07,一篇 23:12。
第一次其实发出去了,只是回读慢。脚本看到超时才判失败,它不知道对面其实已经收下了。这种错最阴的地方在于,发布动作成功了,你却拿不到成功这个信号。
我后来的办法是给每条任务算一个幂等键,站点 ID 加内容 ID 加批次号,拼成一个字符串。发之前先查本地任务表,这个键要是已经是成功态,直接跳过,接口都不调。
光这个还不够。有一回人工在后台补发过一篇,本地表里没记录,脚本又发了一遍。所以我又加了第二层:发之前调站点的文章列表接口,取最近二十条,拿标题和 slug 比对,命中就跳过。
两层管的是两件事。第一层管我这边认为发过没,第二层管对面实际有没有。只信自己那张表,迟早出事。
漏发这件事比重复发难查。它一点提示都没有,这是最要命的地方。
我的流程是先算今天每站该发几篇,然后一条条生成任务,再执行。问题出在生成那步,关键词没取到、模板渲染抛异常、正文长度不够被校验拦下来。只要生成这步挂了,任务压根没进队列。执行环节什么都不缺,跑得漂漂亮亮,日志一条报错都没有。
我是这么发现的:有天想统计一周产出,手动数了数,发现某站少了两篇。回去翻日志,那两天的执行记录全绿。
后来改成把任务先落库再执行。状态四个:待执行、执行中、成功、失败。每天固定一个时间做对账,应发多少条、成功多少条,差额就是漏的,而且能直接定位到是卡在待执行,还是根本没生成出来。
另外每个任务发完必须回读。返回 200 只说明服务端收下了,不说明文章上线了。我的做法是发完立刻调列表接口取最新一条,比 id 和标题,一致才标成功。这一步真救过我。有回装了插件之后接口返回成功,但文章被存成草稿,前台压根看不见,回读一比就露了。
还有个问题是同一站点不能同时跑两个任务。一台机器跑九个站本身没问题,出事的是同一个站进来两个任务。
有些 CMS 发文不锁表。两个请求挤在一起,栏目排序会乱,slug 生成重复,两个请求拿到同一个自增号,再狠一点直接把数据库连接池占满,后面全超时。我遇到最离谱的一回是栏目树少了一个节点,后台那块空着,只能进数据库手工补。
解决办法是给站点加锁,不是全局锁。全局锁九个站排队,白等。我按站点 ID 加,同一站点的任务串行,不同站点照常并行。
调度这块我后来重写了一遍,定了几条,现在还在用。
不要拿一个定时任务去跑所有站。改成每站一个队列,各自带并发度。某个站卡死不至于拖累别的。
重试要分得清能不能重试。网络超时、502、连接重置,这些退避重试;401 认证挂掉、400 字段格式不对,重试一百次都是白搭,直接标失败等人看。以前我全扔进重试池,结果池子越滚越大,全是永远不可能成功的垃圾。
退避用指数,别用固定间隔。我这边是三十秒、两分钟、五分钟、十五分钟,四轮封顶。固定间隔猛冲,冲的是对面服务器。
每天出对账报告。应发、成功、失败、重试次数、哪几条卡着。这东西比实时日志有用,因为它逼你每天看一眼数字对不对。
现在这些活是怎么跑的。早期全是自己写脚本,一个站一个文件,改个配置要动五处。后来我把这套逻辑抽出来做成一个本地调度程序,九个 CMS 的适配、任务表、回读校验全收进去,配置文件比我原来那堆脚本清楚多了。
但上面这些我还是自己理过一遍。工具能替你跑,判断替不了。出问题的时候你得知道是幂等键没起作用,还是回读判错了,还是锁加错了粒度。
这几处不出事,别的基本都是小毛病。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。