關(guān)于異步消息,大家都知道,如下:
這些用起來都是比較復(fù)雜的,RabbitMQ先要?jiǎng)?chuàng)建Exchange,在創(chuàng)建Queue,還要將Queue和Exchange通過某種規(guī)則綁定起來。發(fā)消息之前要指定routing-Key,還要控制頭部信息。 即使你只需要一組消息者。那么你就要經(jīng)歷上面繁瑣的過程。 但是Redis對(duì)于那些輕量級(jí)和只有一組消息者的消息隊(duì)列; Redis的list數(shù)據(jù)結(jié)構(gòu)常用來作為異步消息隊(duì)列來使用。使用rpush/lpush操作入隊(duì)列,用lpop/rpop來出隊(duì)列。我們都知道list是一個(gè)鏈表,所以操作方式如下
當(dāng)然我們會(huì)遇到一些問題,比如隊(duì)列空了會(huì)怎么樣?客戶端是通過隊(duì)列的 pop 操作來獲取消息,然后進(jìn)行處理。處理完了再接著獲取消息,再進(jìn)行處理。如此循環(huán)往復(fù),這便是作為隊(duì)列消費(fèi)者的客戶端的生命周期。 可是如果隊(duì)列空了,客戶端就會(huì)陷入 pop 的死循環(huán),不停地 pop,沒有數(shù)據(jù),接著再 pop,又沒有數(shù)據(jù)。這就是浪費(fèi)生命的空輪詢。空輪詢不但拉高了客戶端的 CPU,redis 的 QPS 也會(huì)被拉高,如果這樣空輪詢的客戶端有幾十來個(gè),Redis 的慢查詢可能會(huì)顯著增多。 通常我們使用 sleep 來解決這個(gè)問題,讓線程睡一會(huì),睡個(gè) 1s 鐘就可以了。不但客戶端的 CPU 能降下來,Redis 的 QPS 也降下來了。(摘自Redis深度歷險(xiǎn)) 睡眠會(huì)導(dǎo)致消息的延遲增大·,多個(gè)消費(fèi)者情況下,延遲會(huì)有所下降,因?yàn)槊總€(gè)消費(fèi)者都是的睡眠時(shí)間是岔開來的。通過阻塞讀:blocking,也就是這兩個(gè)命令:blpop,brpop。 當(dāng)然還有會(huì)空閑連接自動(dòng)斷開,顧名思義,一直阻塞,Redis客戶端就成了閑置的,時(shí)間長了,服務(wù)器會(huì)斷開連接,減少閑置資源,這時(shí)候就會(huì)拋出異常,所以編寫消費(fèi)者的時(shí)候,注意捕獲異常,重試。 延時(shí)隊(duì)列: 這種方式比較適合異步消息處理,將當(dāng)前沖突的請(qǐng)求扔到另一個(gè)隊(duì)列延后處理以避開沖突 我們將消息序列化成一個(gè)字符串作為 zset 的 value,這個(gè)消息的到期處理時(shí)間作為 score,然后用多個(gè)線程輪詢 zset 獲取到期的任務(wù)進(jìn)行處理,多個(gè)線程是為了保障可用性,萬一掛了一個(gè)線程還有其它線程可以繼續(xù)處理。因?yàn)橛卸鄠€(gè)線程,所以需要考慮并發(fā)爭搶任務(wù),確保任務(wù)不能被多次執(zhí)行。 |
|