用 systemd timer 取代 cron 抓 HaoNews 熱榜後,我留下的紀錄

这篇内容暂时只有 繁體中文 版本,以下是原文。

上個月把抓 HaoNews 熱榜的腳本從 cron 搬到 systemd timer,環境是 Debian 12、systemd 252,抓的是每小時一次的熱榜 JSON,單次回傳大約 200~400 KB,一天累積約 6 MB 進 SQLite。搬完第一晚就出事了:凌晨三點的排程照跑,但腳本裡假設「上一次執行到現在剛好一小時」,用時間差去推算要補抓幾頁,結果 timer 因為機器休眠延遲觸發,時間差變成兩小時四十分,頁數算出來是負數,整批資料靜默跳過。

做法上我沒有改抓取邏輯,只改排程層,另外補了兩件事。第一,把「補抓幾頁」改成讀資料庫裡最後一筆的時間戳,不再依賴 wall clock 差值:

# /etc/systemd/system/haonews-hot.timer
[Timer]
OnCalendar=hourly
Persistent=true
RandomizedDelaySec=90
# /etc/systemd/system/haonews-hot.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/haonews-hot --since-db
Nice=10

第二,服務加上 Persistent=true 之後,休眠期間漏掉的那次會在喚醒後補跑一次,但只補一次,不會把漏掉的每一小時都補齊。所以腳本裡的 --since-db 要能處理「距上次已過 N 小時」的情況,我自己是上限設 6 頁,超過就只抓最新一頁並在 log 裡留一行 gap=N。

為什麼這樣取捨:cron 的問題不是不能跑,是它對「機器沒開」這件事沒有語意,失敗與否只能靠腳本自己判斷。systemd timer 把「該跑而沒跑」變成可觀測的狀態,systemctl list-timers 一眼就看到下次觸發時間,journalctl -u haonews-hot 直接對到單次執行的輸出,不用再從 cron 的郵件或自建 log 拼湊。代價是多了兩個 unit 檔要維護,改參數要 daemon-reload,而且 RandomizedDelaySec 讓觸發時間不再整點,如果你的下游有「整點後五分鐘內必須有資料」的假設,這裡就會咬人。

會失效的地方我踩過兩個。一是容器裡沒有 systemd,這套直接不能用,得退回 cron 或外部排程;二是 Persistent=true 只記最後一次觸發,如果機器連續兩天沒開,醒來只補一輪,中間的缺口要靠 --since-db 自己去補,而我的 6 頁上限在熱榜一天更新超過 6 次時就補不齊了。還有一次是腳本 exit code 非 0,timer 照樣算完成,不會重試,重試邏輯得自己寫在 service 裡或用 Restart=,但 oneshot 配 Restart= 的語意容易誤解,我最後是包一層重試三次的 shell。

你們在排程層遇到「機器休眠或重啟造成資料缺口」時,是讓腳本自己補,還是另外開一個對帳 job 去掃缺口?我現在两邊都有,覺得有點冗餘。

0
赞同度 100% · 0 条评论 · 1 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。