首先 我先引入一個(gè)大家熟知的觀點(diǎn):Reids可以作為消息隊(duì)列來(lái)使用
redis提供了兩種方式來(lái)做消息隊(duì)列错邦,一種是生產(chǎn)者消費(fèi)者模式,一種是發(fā)布訂閱模式池摧。
本篇文章將從 異步橙数,解耦,分布式公壤,可靠四部分來(lái)探討Redis中的消息隊(duì)列以及應(yīng)用場(chǎng)景
異步
異步的使用場(chǎng)景【符合我們的真實(shí)的世界换可,真實(shí)世界本來(lái)就是異步的】,生活中大部分的使用都是基于異步的厦幅,比如發(fā)送郵件與回復(fù)郵件的請(qǐng)求響應(yīng)模型。
一個(gè)service與另外一個(gè)service有三種交互方式:命令(Commands)慨飘、事件(Events)以及查詢(Queries)确憨。一次請(qǐng)求可以理解為由主服務(wù)與觸發(fā)服務(wù)和關(guān)聯(lián)服務(wù)組成。
Commands 瓤的。命令是一個(gè)操作休弃。希望在另一個(gè)服務(wù)中執(zhí)行某些操作的一個(gè)請(qǐng)求。 會(huì)改變系統(tǒng)狀態(tài)的東西圈膏。 命令期待有響應(yīng)塔猾。
Events 。事件既是一個(gè)事實(shí)也是一個(gè)觸發(fā)器稽坤。 發(fā)生了一些事情丈甸,表示為通知。
Queries 尿褪。查詢是一個(gè)請(qǐng)求睦擂,是一個(gè)查找一些東西的請(qǐng)求(request)。重要的是杖玲,查詢不會(huì)使得系統(tǒng)狀態(tài)發(fā)生改變顿仇。
解耦
解耦的基礎(chǔ)含義倡導(dǎo)一種是由上而下,分而治之的思想摆马。
解耦又是消息隊(duì)列最本質(zhì)的目的臼闻。把消息的送達(dá)和處理分開(kāi),才真正實(shí)現(xiàn)消息系統(tǒng)的解耦囤采。
基于消息的模型述呐,關(guān)心的是通知,而非處理 斑唬。只關(guān)心核心流程市埋,多個(gè)任務(wù)的情況下黎泣,發(fā)送通知就行了。
經(jīng)典的生產(chǎn)者消費(fèi)者模式的消息模型缤谎,通過(guò)Broker分離生產(chǎn)與消息抒倚,Broker簡(jiǎn)單來(lái)說(shuō)就是消息服務(wù)器,負(fù)責(zé)消息的接受坷澡,存取托呕。可以這樣理解:
在服務(wù)型項(xiàng)目開(kāi)發(fā)上频敛,服務(wù)型項(xiàng)目的意思就是項(xiàng)目本質(zhì)上不是單體應(yīng)用项郊,會(huì)為多個(gè)業(yè)務(wù)服務(wù),上游對(duì)下游的調(diào)用斟赚,不直接通過(guò)觸發(fā)方式完成即可着降,而是通過(guò)消息中心隔離上下游
可靠
001
可靠性簡(jiǎn)單來(lái)說(shuō)就是程序把需要處理的任務(wù)進(jìn)行編號(hào),每個(gè)編號(hào)的任務(wù)在任務(wù)運(yùn)行期間都是可以被跟蹤的拗军。每一個(gè)任務(wù)擁有自己的唯一標(biāo)記任洞。比如命名規(guī)則可以是:業(yè)務(wù)組件名稱加時(shí)間戳的生成規(guī)則。
以下 我們看一個(gè)網(wǎng)絡(luò)資料的公開(kāi)案例
用戶最近N條訂單記錄的Redis存儲(chǔ)
對(duì)于這個(gè)需求需要滿足幾個(gè)條件
1 消息需要有序存儲(chǔ)发侵,來(lái)確定數(shù)據(jù)結(jié)構(gòu)SortSet
2 全局跟蹤每條記錄交掏,對(duì)數(shù)據(jù)進(jìn)行唯一編碼
【訂單有序集合中的每個(gè)元素是將時(shí)間毫秒數(shù)+訂單號(hào)最后3位作為分?jǐn)?shù)進(jìn)行排序的。為什么不只用毫秒數(shù)作為分?jǐn)?shù)呢刃鳄?因?yàn)槲覀兊南聠螘r(shí)間只精確到秒盅弛,如果不加訂單號(hào)最后3位,若同一秒有兩個(gè)或兩個(gè)以上訂單時(shí)叔锐,排序分?jǐn)?shù)就會(huì)一樣挪鹏,從而導(dǎo)致根據(jù)分?jǐn)?shù)從緩存查詢訂單時(shí)不能保證唯一性。而我們的訂單號(hào)的生成規(guī)則可以保證同一秒內(nèi)的訂單號(hào)的最后3位肯定不一樣】
002
每個(gè)階段在處理任務(wù)時(shí)掌腰,都需要有任務(wù)回執(zhí)狰住,來(lái)表明這條任務(wù)的處理狀態(tài),是處理成功還是失敗齿梁,還是別拒絕處理等催植。我們以SortSet集合為例,隊(duì)列處理消費(fèi)時(shí)勺择,一定是按照一定順序创南,從前往后或者從后往前依次N條的獲取,獲取之后省核,索取元素被消費(fèi)程序處理稿辙,處理的結(jié)果如何就是前文提到的任務(wù)回執(zhí),如果這時(shí)因?yàn)榫W(wǎng)絡(luò)抖動(dòng)或者調(diào)用鏈下游原因?qū)е孪M(fèi)失敗气忠,所取元素代表的業(yè)務(wù)元數(shù)據(jù)也會(huì)隨之消失邻储。這時(shí)候就需要根據(jù)回執(zhí)來(lái)判斷是否需要另外處理所取元素赋咽。
Redis下的發(fā)布訂閱
使用redis的pubsub功能,訂閱者訂閱頻道吨娜,發(fā)布者發(fā)布消息到頻道了脓匿,頻道就是一個(gè)消息隊(duì)列。
我們可以認(rèn)為發(fā)布訂閱方式是一種實(shí)時(shí)的通訊模式宦赠。
001
redis 發(fā)布訂閱使用場(chǎng)景明顯是構(gòu)建實(shí)時(shí)消息系統(tǒng)陪毡,依賴于redis服務(wù)端長(zhǎng)連接的穩(wěn)定性。php連接redis的長(zhǎng)鏈接本身就是不靠譜的勾扭,而且pubsub也不能使用在可靠性要求比較高的系統(tǒng)中毡琉。【不靠譜】體現(xiàn)在訂閱模式服務(wù)器端開(kāi)啟訂閱后妙色,過(guò)一段時(shí)間訂閱會(huì)失效桅滋,需要不停的輪訓(xùn)開(kāi)啟訂閱。
針對(duì)Redis的發(fā)布訂閱功能身辨,網(wǎng)上找到一種說(shuō)明
一個(gè)生產(chǎn)者可以對(duì)應(yīng)多個(gè)消費(fèi)者虱歪,但是必須保證消息發(fā)布者和消息的訂閱者同時(shí)在線,否則栅表,否則一旦消息訂閱者由于各種異常情況而被迫斷開(kāi)連接,在其重新連接后师枣,其離線期間的消息是無(wú)法被重新通知的(即發(fā)即棄)怪瓶。
對(duì)于這種理解,最重要的是在應(yīng)用開(kāi)發(fā)中如何保證雙發(fā)都在線的長(zhǎng)連接狀態(tài)践美?
002
對(duì)【不靠譜】的一種解釋如下:
因?yàn)镽edis的監(jiān)聽(tīng)其實(shí)是打開(kāi)了一個(gè)長(zhǎng)連接操作的洗贰。任何網(wǎng)絡(luò)波動(dòng)都會(huì)斷開(kāi)的。服務(wù)器內(nèi)網(wǎng)絡(luò)穩(wěn)定的情況下是可以的陨倡。
分布式
涉及到消息隊(duì)列的三個(gè)角色敛滋,發(fā)布者,Broker和消費(fèi)者兴革,都可以以集群的形式進(jìn)行部署和發(fā)布绎晃。消費(fèi)能力可以通過(guò)增加機(jī)器數(shù)進(jìn)行擴(kuò)展。
補(bǔ)充:根據(jù)參考文檔來(lái)
Q1:分布式消息系統(tǒng)中杂曲,如何避免消息重復(fù)庶艾?
造成消息重復(fù)的根本原因是:網(wǎng)絡(luò)不可靠。只要通過(guò)網(wǎng)絡(luò)交換數(shù)據(jù)擎勘,就無(wú)法避免這個(gè)問(wèn)題咱揍。所以解決這個(gè)問(wèn)題的辦法就是繞過(guò)這個(gè)問(wèn)題。那么問(wèn)題就變成了:如果消費(fèi)端收到兩條一樣的消息棚饵,應(yīng)該怎樣處理煤裙?
a. 消費(fèi)端處理消息的業(yè)務(wù)邏輯保持冪等性;
b. 保證每條消息都有唯一編號(hào)且保證消息處理成功與去重表的日志同時(shí)出現(xiàn)掩完。
通過(guò)冪等性,不管來(lái)多少條重復(fù)消息硼砰,可以實(shí)現(xiàn)處理的結(jié)果都一樣且蓬。再利用一張日志表來(lái)記錄已經(jīng)處理成功的消息的ID,如果新到的消息ID已經(jīng)在日志表中夺刑,那么就可以不再處理這條消息缅疟,避免消息的重復(fù)處理。
參考文檔
REST?RPC?是時(shí)候改變你對(duì)微服務(wù)的認(rèn)知了遍愿!
https://segmentfault.com/p/1210000010216291/read
高可用 Redis 服務(wù)架構(gòu)分析與搭建 https://mp.weixin.qq.com/s/1N4sMAFbfJozdvsEyOuuwA