专业书籍精读 · SRE · 第 24 章

分布式周期任务调度:把 cron 做成一个不会重跑的服务

Site Reliability Engineering · Ch 24 · Google(Štěpán Davidovič)· 2016

EN →

这一章讲什么?

你银行卡上那笔「每月 8 号自动扣房租」——扣两次很麻烦,一次没扣也麻烦。服务器上负责「每天几点几分自动干一件事」的那个小管家,叫 cron(定时任务)。SRE 第 24 章讲的就是:当这件事不再交给一台机器、而是交给一整个机房时,这个小管家要怎么重做。

先说个怪事

在一台机器上,cron 是最不起眼的老工具,几十年没人操心过它。可一旦机器变成一百台,「每天凌晨两点跑一次」这句再普通不过的话,突然就保证不了了——要么那天根本没跑、而且没人发现;要么同时跑了两遍,把工资发了两次。

旧世界为什么难

老办法只有两种,都不行。把闹钟交给一台指定的机器:这台机器哪天坏了,闹钟和闹钟清单一起消失——而且是静悄悄地消失,因为没响的闹钟不会报警,等你发现时可能已经过去一个周末。给每台机器都装同一个闹钟:一到点,一百台机器一起开工,同一件事被干了一百遍。

办法:选个值班的,再记一本共用的流水账

先让这群机器选一个值班的——谁也不能自封,得过半数点头。只有值班那台按点开工,其余的不开工,但必须跟着记同一本账,随时能接班。

账怎么记才是关键。值班的每次开工要记两笔:开工前先写一句「我马上要干这件事了」,发起完再写一句「我发起了,编号 X」。为什么非要多写第一笔?因为值班的可能正好倒在开工那一瞬间。接班的翻开账本,看见只有前一笔、没有后一笔,就知道这件事可能干了、也可能没干——它卡在一个说不清的窗口里。这时接班的先去干活的车间问一声「有没有一个叫 X 的活儿在跑」;问得到就好办,问不到,就只能按事先定好的规矩办。

那一刀必须砍:宁可漏,不可重

Google 的规矩是:宁可漏跑一次,也不重跑。理由很实在——漏跑了你能看见,数据缺一块、报警响一声,补一次就行;重跑的后果(邮件发两遍、钱扣两回)常常收不回来。

代价也得说清楚:这意味着你的任务真的会偶尔不跑,所以必须专门为「这次没发生」配一个报警,否则你只是把吵闹换成了沉默。

还有件小事:别都挤在整点

几乎所有人写定时任务都写「凌晨 0 点」。于是整点一到,成千个任务同时向机房要机器,挤成一波洪峰,剩下 59 分钟机器闲着。所以这套 cron 允许你说一句「这一小时里你随便挑个时间」,把洪峰摊平。

一句话记住

定时任务一旦跨出一台机器,就不再是个小工具,而是一个要选主、要记账的服务;而它的正确性做不到两全——漏跑和重跑之间你必须先选一边,然后给没选的那一边配上报警。

想看崩溃窗口的时序、状态存哪、以及 Kubernetes 的公开对照? → 切到精读版