使用redis的list列表來實現消息隊列功能,相信大家都聽過消息隊列,但是在業務中可能並沒有真正去使用它。在公司項目中正好有個場景使用到了消息隊列,因此就來說一下流程。在web界面上有個功能是群發郵件,用戶可以創建一個5000郵箱的郵件列表,編輯完郵件內容後可以給這個郵件列表發信。如果使用同步的方 ...
使用redis的list列表來實現消息隊列功能,相信大家都聽過消息隊列,但是在業務中可能並沒有真正去使用它。在公司項目中正好有個場景使用到了消息隊列,因此就來說一下流程。
在web界面上有個功能是群發郵件,用戶可以創建一個5000郵箱的郵件列表,編輯完郵件內容後可以給這個郵件列表發信。如果使用同步的方式,直接在瀏覽器-伺服器模式下調用smtp服務發信,肯定速度非常慢,也有可能會超時。現在就是在點完發信後,進入到redis的一個list中,然後直接給用戶返回發送成功。至於這些信什麼時候發送和完成,都是對用戶透明的。在後臺進程任務中會去消費list數據,逐條進行發信。
web點擊後的入隊列很簡單,就是單純的for迴圈,然後使用lPush在隊列的頭部增加元素。
後臺使用定時任務每隔30分鐘啟動php進程,去消費list中的數據,並且kill掉之前的php進程。這樣做是為了防止PHP進程僵死,定時重啟進程來進行操作。
在kill掉進程的時候,有可能會導致正在發信的過程被結束,取出的數據沒有被髮送成功。redis有提供一個功能是在彈出數據的同時,可以插入到另一個隊列中,並且這個操作是原子性的,這樣就使用另一個備用隊列來存儲發送失敗的數據,重新進行補償發送
<?php /** * 模擬web入隊列 */ $redis=new Redis(); $redis->connect('192.168.1.114', 6379); for($i=0;$i<5000;$i++){ $message="mes {$i}"; $redis->lPush("emails",$message); } echo "ok";
<?php //後臺進程 $redis=new Redis(); $redis->connect('192.168.1.114', 6379); while(true){ $email=$redis->bRPopLPush ("emails","emails_bak",20); var_dump($email); usleep(100);//模擬發信耗時 $redis->lRem("emails_bak",$email); }
redis提供的所以關於list的函數
blPop, brPop 阻塞式讀取和刪除第一個/最後一個元素
bRPopLPush 從列表中彈出一個值,並把它推到另一個列表中
rPopLPush 彈出列表中的最後一個元素,並把他推到另一個列表中
lRange, lGetRange 獲取列表中一系列元素
lIndex, lGet 從其索引中獲取元素
lSet 通過索引設置元素
lRem, lRemove 通過索引刪除元素
lInsert 在列表中一個元素之前或之後插入一個元素
lLen, lSize 獲取列表的長度/大小
lPop 彈出列表中的第一個元素
lPush 在列表前添加一個元素
rPop 彈出列表最後一個元素
rPush 在列表末尾增加一個元素
lPushx 僅在列表存在時才在列表前添加元素
rPushX 僅在列表存在時,才在列表結尾增加元素
lTrim, listTrim 將列表修剪到指定範圍
cron的規則:
*/30 * * * * root ps -ef |grep SendMassMail | grep -v ps |awk '{print $2}' | xargs kill -9 ;php /usr/local/sinamail/tools/SendMassMailAndMonitor.php
我的視頻教程
https://www.bilibili.com/video/av73188455/