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