場景
好多系統中會用到郵件系統,我們假設有一個 PHP 指令碼用來傳送郵件。使用 Linux cron 每分鐘執行一次
我們暫時不引入佇列系統,其實使用佇列處理此方式更優。
我們得到下面的基本配置
* * * * * php /home/app/email.php
問題分析和解決
如果這個郵件服務出現異常,程式僵死怎麼辦?
假設由於未知因素, email.php
指令碼一直執行,沒有退出。極端的情況,進入一個 while 死迴圈。
這下倒好,原來說好的一分鐘執行一次,現在一直死這邊了,後面的指令碼也不能跑了
解決辦法:
使用 timeout,假設我們設定每個指令碼最多執行時間位 200秒,超過 200秒 就自動死掉。
* * * * * timeout 200 php /home/app/email.php
如果這個指令碼執行時間超過 60秒,下一分鐘又會執行 php email.php
,如果避免重複執行?
這樣會出現,有兩個程式同時在執行 php email.php
,那會不會出現同一個任務被執行了兩次?
解決辦法:
使用 flock 進行互斥控制
用法:
flock [選項] <檔案|目錄> <命令> [<引數>...]
flock [選項] <檔案|目錄> -c <命令>
flock [選項] <檔案描述符號碼>
通過 shell 指令碼管理檔案鎖。
選項:
-s, --shared 獲取共享鎖
-x, --exclusive 獲取排他鎖(預設)
-u, --unlock 移除鎖
-n, --nonblock 失敗而非等待
-w, --timeout <秒> 等待限定的時間
-E, --conflict-exit-code <數字> 衝突或超時後的退出程式碼
-o, --close 執行命令前關閉檔案描述符
-c, --command <命令> 通過 shell 執行單個命令字串
-F, --no-fork 執行命令時不 fork
--verbose 增加詳盡程度
-h, --help display this help
-V, --version display version
我們用到其中的排他設定
* * * * * flock -xn /tmp/test.lock -c "timeout 200 php /home/app/email.php"
記錄好日誌
定時任務可能要記錄日誌呀,不然後期怎麼排查
* * * * * flock -xn /tmp/test.lock -c "timeout 200 php /home/app/email.php >> /home/log/test.log 2>&1"
總結
* * * * * flock -xn /tmp/test.lock -c "timeout 200 php /home/app/email.php >> /home/log/test.log 2>&1"
番外篇 頻率提升
我覺得一分鐘一次頻率太低,想 10s 執行一次怎麼辦?
* * * * * php /home/app/email.php >> /home/log/test.log 2>&1
* * * * * ( sleep 10 ; php /home/app/email.php >> /home/log/test.log 2>&1 )
* * * * * ( sleep 20 ; php /home/app/email.php >> /home/log/test.log 2>&1 )
* * * * * ( sleep 30 ; php /home/app/email.php >> /home/log/test.log 2>&1 )
* * * * * ( sleep 40 ; php /home/app/email.php >> /home/log/test.log 2>&1 )
* * * * * ( sleep 50 ; php /home/app/email.php >> /home/log/test.log 2>&1 )
番外篇 flock 測試
準備一個 php 指令碼 /home/rovast/Code/flock/test.php
<?php
$i = 10000;
while ($i > 0) {
echo --$i . \PHP_EOL;
sleep(1);
}
執行
flock -xn /tmp/mytest.lock -c "timeout 30 php /home/rovast/Code/flock/test.php"
我們看到終端不停輸出數值
9999
9998
9997
9996
9995
9994
9993
9992
9991
9990
我們再開啟另外一個終端,執行
flock -xn /tmp/mytest.lock -c "timeout 30 php /home/rovast/Code/flock/test.php"
我們發現:
- 第二次執行的沒有輸出(因為 flock 互斥)
- 第一個執行的,30秒後自動關閉程式(因為 timeout 30)