引語:在許多的web應用中,我們都是通過同步操作的方式去處理我們的業務,但是往往也有這樣的業務訴求,即一個操作可能比較耗時,或者有許多的不確定性(如支付操作需要等待第三方結果通知)。在這種業務場景下,再使用同步的方式去操作,可能就不太合理了。那我們想到的是,使用同步先返回臨時結果,再通過異步通知最終結果的方式,進行處理,這是必須的。
那么對於這種場景,怎么處理呢?
對於有自己處理異步操作能力的語言(如java),那么對於這種情況,只需要拋一個線程到代碼里,指定一個回調處理即可。
對於沒有自行處理異步操作的語言(如php),相對來說就麻煩一點,一般需要借助於后台一些一直運行的進程,它會一直去檢測是否有需要處理的事情,如果有,根據需求,依次去操作。像這種操作,我們可以稱之為,消息處理。那么,當遇到耗時操作的時候,我們只需要立即將消息存放到消息隊列中,以便后台進程能讀取到該消息!
市面上有很多現成的消息隊列的軟件如:rabbitmq,kafka等,如果真有需求,可以選擇其中一個進行使用,本文只為解釋一些基本原理及個人實踐。
因為我用的是php語言,所以,也只能就php的這種特性來表達一下自己的處理方式。對於有自己處理能力的語言,他們可以自行處理各種並發問題,但是對於需要cron腳本輔助的語言,則需要更多的操作!
1. 怎樣將消息存入隊列?
既然操作很耗時,那我們可以將不耗時的操作,先處理了,然后將后續工作交給服務器,那么我們怎樣存儲這些要操作的事項呢?方法應該是很多的,比如存儲至文件,存儲到數據庫,存儲到緩存。當然了,最好的方式當然是存儲到緩存,借助於redis或者memcache這樣的緩存工具,可以輕松的實現push和pop操作。但是消息可能就只是一些字符串,怎樣表達清楚需要做的事呢?這個,方式當然也很多了,自己定義好消息的結構,如:{msgType: aa, params: [{p1:2, p2:3}], addTime:2016-05-15 10:30:03, from:[{ip:1.1.1.1, who:lee}]}, 然后將消息序列化或者json化,存儲起來即可!
2. 怎樣處理消息?
消息存入隊列后,怎樣去處理呢?一般有兩種:1. 寫一個死循環,一直去檢測消息內容,檢測到后,根據消息類型,就立即做相應處理。2. 使用cron腳本的方式,定期去檢測消息,有就處理,沒有就退出!其實還有第三種,使用cron定期執行腳本,但是一次執行就需要處理完所有的消息,再進行返回,提高效率!(每處理完一條消息,就必須將該消息刪除或者存放到其他地方)
3. 怎樣避免並發執行?
消息應該是只能一次執行的,如果被多次執行,則已超出預期結果,所以,一定要注意多進程同時執行一條消息情況,主要有兩種方式,1. 程序中自己創建文件鎖,執行前先檢測是否存在該鎖,沒有則創建,執行完后刪除,但是這種情況一定要注意程序意外終止的情況(如果意外終止,鎖將一直存在,后續將永遠無法執行)。2. 讓cron生成鎖,控制該文件只被一個進程同時執行;
4. 注意事項?
如果消息只是存儲於緩存中,執行完成之后,就將其刪除,那么,對於排查及恢復來說,都是致命的,所以,一般需要在刪除之前,先將其存儲到一個備份的位置或者以日志的方式將消息保存下來!
簡單來說,消息隊列就是一個消息推入,一個消息執行的過程。其他的,只是為了保障執行過程中的各種意外,各種恢復,各種去重操作罷了!(好像也找不到其他話說了我,哈哈哈...)