redis+crontab+php異步處理任務


2016年1月8日 16:08:43 星期五

情景: 用戶登錄日志, 發郵件, 發短信等等實時性要求不怎么高的業務通常會異步執行

之前接觸過幾種redis+crontab配套的實現方法,

比如: crontab定時執行curl腳本

  1. 用curl 訪問URL執行PHP腳本去pop隊列

  2. PHP程序pop一次, 處理后返回同樣的URL

  3. curl收到這個URL后就可以再次跟蹤訪問並執行該PHP程序, 這樣就可以實現循環pop的效果

  4. 這樣需要給curl設定下最大跟蹤次數(--max-redirs), 就可以限定每次pop的最大值

  但總感覺那么不順暢

  1. 不是實時的, 最高頻率是每隔一分鍾執行一次(當然也有其它方法使之能每秒都運行)

  2. 不同時間段打入隊列的數據是不確定的, 比如白天登錄用戶會比晚上多, crontab執行頻率設定不合理的話, 比如,白天入隊列的數據可能大於出隊列的數據從而導致延時加大

這兩天學到了一個新的方法, 可以解決實時性的問題:

PHP程序: 一個阻塞型的死循環去pop隊列

crontab:去監控這個PHP循環是不是在運行, 沒有則啟動

PHP程序:

1 while (TRUE) {
2     $element = $redis->brPop($key, 10); // 阻塞執行, 超時為10s
3     $content = json_decode($element[1], TRUE);// 返回為PHP數組, 其中$element[0]是隊列的名字, $element[1]是內容 (因為brpop可以同時監控多個隊列)
4 }

注意:

1. while true, 看似是死循環, 但是里邊的brpop是阻塞型的代碼, 隊列里邊沒有數據的時候會讓出CPU直到數據的到來, 等待10秒鍾后還沒有數據就停掉, 進入下一次循環(下一個10s)

2. 因為是死循環, 要一直執行下去, 所以最好的方式是php_cli模式下執行, 這個模式下是不會有超時限制的

3. "read error on connection" 報錯, 因為brpop阻塞10s, 所以PHP在連接redis時設置的timeout時間必須得大於這個時間, 或者干脆就不設置超時:

1 $this->connect('REDIS_HOST', 'REDIS_PORT', 'GLOBAL_TIME_OUT');
2 $this->auth('REDIS_AUTH');

crontab怎么配合: 定期檢查這個PHP腳本是否是在執行, 如果沒有就啟動它

* * * * * /bin/sh /path/to/watch/bash_script/start.sh online

start.sh:

 1 #!/bin/bash
 2 # 使用了 Redis 的 bRpop 函數去實時監控, 發送報警郵件和短信
 3 # 先檢查是否啟動了該進程, 沒有的話就啟動
 4 # 保證該文件是可執行的
 5 # crontab:  /bin/sh /path/to/shell.sh env #env: online/test/someone
 6 
 7 count=`ps -ef | grep process_name | grep -v "grep" | wc -l`
 8 start_time=`date +%Y-%m-%d_%X`
 9 if [ $count -eq 0 ] && [ online = "$1" ] #生產環境
10 then
11     echo ${start_time}" start redisLog "
12     #code here: cd /path/to/onlien/webroot && /path/to/php/bin/php /path/to/shell/file/cli.php args >> /path/to/error.log 2>&1
13 
14 elif [ $count -eq 0 ] && [ test = "$1" ] #公共測試環境
15 then
16     echo ${start_time}" start redisLog "
17     #code here: cd /path/to/test/webroot && /path/to/php/bin/php /path/to/shell/file/cli.php args >> /path/to/error.log 2>&1
18 
19 elif [ $count -eq 0 ] #個人測試環境
20 then
21     echo ${start_time}" start redisLog "
22     #code here: cd /path/to/someone/webroot && /path/to/php/bin/php /path/to/shell/file/cli.php args >> /path/to/error.log 2>&1
23 
24 else
25     echo ${start_time}" redisLog already running "
26 fi

 

這中實現方式可以達到實時的

但是沒數據的時候, 死循環會一直阻塞下去, 但貌似也不耗費什么資源

注意 windows 和 Linux的換行符是不一樣的, windows轉Linux:

vi xxx.sh

:set fileformat=unix

:x

 

 summerPHP


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM