用redis做簡單的任務隊列(一)


隊列本身其實是個有序的列表,而Redis是支持list的,我們可以查看Redis的官方文檔 http://redis.io/commands#list ,其中我們可以對這個隊列的兩端分別進行操作,所以其實Redis中的list即可以當做普通的先進先出的queue,也可以作為先進后出的stack。

 

如果當做隊列來用,我們可以用LPUSH(頭部插入)和RPOP(尾部彈出)或RPUSH(尾部插入)和LPOP(頭部彈出),這兩種方式都可以,只要是搭配使用即可。但我們平時一般搭配使用 LPUSH和RPOP。

 

一般開發的時候我們會分為生產者和消費者,生產者生產消息,消費者獲取消息進行處理。

Producer: redis->lpush(‘joblist’, ‘this is Job-1’); redis->lpush(‘joblist’, ‘this is Job-2’); …. 
Cosumer: job = redis->rpop(‘joblist’);// return Job-1 done the job…. job = redis->rpop(‘joblist’);// return Job-2 done the job… 

正常情況下上面這些都是沒問題的,但是我們也經常遇到這種情況,當任務進行到一半(即任務沒有完全執行完)的時候,突然服務器宕機或者網絡中斷,這時候任務其實是沒有真正完成的,這時候出現隊列中的任務“丟失”了,並不是說隊列任務真的丟失了,而是我們把任務從隊列拿出來之后並沒有完成這個任務,這時候我們就需要考慮如何能在任務真正完成的時候才把任務從隊列中刪除。

 

幸運地是,redis其實給我們提供了這樣的可能。繼續翻看redis的官方文檔,我們發現 RPOPLPUSH ,從字面含義來解釋就是尾部彈出頭部插入,它后面跟兩個參數,一個是彈出的list,一個是要插入的list,而且文檔上告訴我們它可以實現 可靠的隊列 。我們可以把任務從隊列中取出來放到另外一個執行中的隊列,等到任務真正完成之后再從執行中的隊列中刪除。

 

那么上面的代碼我們可以修改成如下,producer不變:

Cosumer: job = redis->rpoplpush(‘joblist’, ‘job-doing’);// 把Job-1從joblist轉移到job-doing done the job….// 完成Job-1 redis->lrem(‘job-doing’, 1, job);// 等Job-1完成之后把它從job-doing隊列中刪除 

這樣如果Job失敗,那Job的任務還存在在隊列job-doing中,我們可以單獨啟一個進程來掃描job-doing列表,如果一個job長時間在隊列中,則重新執行該Job或重新插入的job列表。

 

當然,這也不是完全100%靠譜的解決方案,因為如果存在很多相同的job,那我們從job-doing刪除job的時候就無法確認是哪個job真正應該刪除,但是像我們上面描述的,既然Job是相同的,那我們刪除哪個也無所謂了,反正執行的結果都一樣。上面的解決方案還有一個問題,我們必須要長時間地監控job-doing列表,這需要額外的資源,還有一種方式是job-doing和job用同一個列表,即:

Cosumer: job = redis->rpoplpush(‘joblist’, ‘jobllist’); 把Job-1從joblist尾部轉移到joblist頭部 done the job….// 完成Job-1 redis->lrem(‘job-doing’, 1, job);// 等Job-1完成之后把它從joblist頭部刪除 

其實,文章寫到這里,大家應該明白了,這樣一來,我們利用循環隊列,即可實現可靠的隊列。當然,相比較世面上比較成熟的隊列,例如 RabbitMQ、 Beanstalkd 、 IronMQ 等還是有很多的不足,但是如果是簡單地項目或者項目本身就已經在使用redis又不想添加新的組件,試試redis list也不錯。

 


免責聲明!

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



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