延遲隊列,顧名思義它是一種帶有延遲功能的消息隊列。 那麼,是在什麼場景下我才須要這樣的隊列呢?php
先看看一下業務場景:html
一般解決以上問題,最簡單直接的辦法就是定時去掃表。mysql
掃表存在的問題是:git
延時隊列能對於上述需求能很好的解決github
調研了市場上一些開源的方案,如下:web
基本以上緣由打算本身寫一個,日常使用php多,項目基本redis的zset結構做爲存儲,用php語言實現 ,實現原理參考了有贊團隊:https://tech.youzan.com/queuing_delay/redis
整個延遲隊列主要由4個部分sql
消息結構
每一個Job必須包含一下幾個屬性:docker
對於同一類的topic delaytime,ttr通常是固定,job能夠在精簡一下屬性數據庫
1.topic:Job類型。能夠理解成具體的業務名稱
2.id:Job的惟一標識。用來檢索和刪除指定的Job信息。
3.body:Job的內容,供消費者作具體的業務處理,以json格式存儲。
delaytime,ttr在topicadmin後臺配置
整體架構
採用master-work架構模式,主要包括6個模塊:
消息寫入:
timer查找到期消息:
timer查找到期消息:
consumer消費流程:
6、部署
環境依賴:PHP 5.4+ 安裝sockets,redis,pcntl,pdo_mysql 拓展
ps: 熟悉docker的同窗能夠直接用鏡像: shareclz/php7.2.14 裏面包含了所需拓展
step1:安裝數據庫用於存儲一些topic以及告警信息
執行:
step2:在DqConfg.文件中配置數據庫信息: DqConf::$db step3: 啓動http服務mysql> source dq.sql
在DqConf.php文件中修改php了路徑
命令:
php DqHttpServer.php --port 8088
訪問:http://127.0.0.1:8088,出現配置界面
redis信息格式:host:port:auth 好比 127.0.0.1:6379:12345
stop4:配置告信息(好比redis宕機)
stop5:註冊topic
重試標記說明:
1.接口返回爲空默認重試
2.知足指定返回表達會重試,res表示返回的json數組,好比:
回調接口返回json串:{ "code": 200, "data":{ "status": 2, "msg": "返回失敗"}},重試條件能夠這樣寫
{res.code}!= 200
{res.code}!= 200&& {res. data.status}!= 2
{res.code}== 200&& {res. data.status}== 2|| {res. data.msg}== '返回失敗'
step6:啓動服務進程:
php DqInit.php --port 6789 &
執行 ps -ef | grep dq 看到以下信息說明啓動成功
step7: 寫入數據,參考demo.phpstep8:查看日誌
默認日誌目錄在項目目錄的logs目錄下,在DqConf.php修改$logPath
7、性能測試ps -ef | grep dq-master| grep -v grep | head -n 1 | awk '{print $2}' | xargs kill -USR2
須要安裝pthreads拓展:
測試原理:使用多線程模擬併發,在1s內能成功返回請求成功的個數
php DqBench concurrency requests
concurrency:併發數
requests: 每一個併發產生的請求數
測試環境:內存 8G ,8核cpu,2個redis和1個dq-server 部署在一個機器上,數據包64字節
qps:2400
8、值得一提的性能優化點:
1.若是調用通知接口在超時時間內,沒有收到回覆認爲通知失敗,系統會從新把數據放入隊列,從新通知,系統默認最大通知10次(能夠在Dqconf.php文件中修改$notify_exp_nums)通知間隔爲2n+1,好比第一次1分鐘,通知失敗,第二次3分鐘後,直到收到回覆,超出最大通知次數後系統自動丟棄,同時發郵件通知
2.線上redis每隔1s持久化一次,存在丟失1s數據的狀況,出現這種狀況能夠對比request_ymd.txt和notify_ymd.txt日誌手動恢復過來
3.redis宕機通知:
ps:網絡抖動在所不免,通知接口若是涉及到核心的服務,必定要保證冪等!!
10、線上狀況
線上部署了兩個實例每一個機房部一個,4個redis共16G內存做存儲,服務穩定運行數月,各項指標均符合預期
主要接入業務:
1.因爲團隊使用的鏡像缺乏libevent拓展,因此dq-server基於select模型,併發高的場景下性能存在瓶頸,後續能夠改成基於libevent事件模型,提高併發性能
2.timer和consumer目前是採用多進程來作的,這個粒度感受有點粗,能夠考慮使用多線程模式,而且支持動態建立線程數來提升consumer的性能,最大程度保證消費及時
3.dq-server與redis是同步調用,這也是性能的瓶頸點,計劃基於swoole_redis來異步處理