目前主流的MQ產(chǎn)品
1.ZeroMQ
號(hào)稱最快的消息隊(duì)列系統(tǒng)炎码,尤其針對(duì)大吞吐量的需求場(chǎng)景。
擴(kuò)展性好舱卡,開(kāi)發(fā)比較靈活,采用C語(yǔ)言實(shí)現(xiàn)队萤,實(shí)際上只是一個(gè)socket庫(kù)的重新封裝轮锥,如果做為消息隊(duì)列使用,需要開(kāi)發(fā)大量的代碼要尔。ZeroMQ僅提供非持久性的隊(duì)列舍杜,也就是說(shuō)如果down機(jī),數(shù)據(jù)將會(huì)丟失赵辕。其中既绩,Twitter的Storm中使用ZeroMQ作為數(shù)據(jù)流的傳輸。
2.RabbitMQ
結(jié)合erlang語(yǔ)言本身的并發(fā)優(yōu)勢(shì)还惠,支持很多的協(xié)議:AMQP饲握,XMPP, SMTP, STOMP,也正是如此,使的它變的非常重量級(jí)救欧,更適合于企業(yè)級(jí)的開(kāi)發(fā)衰粹。
性能較好,但是不利于做二次開(kāi)發(fā)和維護(hù)笆怠。
3.ActiveMQ
歷史悠久的開(kāi)源項(xiàng)目铝耻,是Apache下的一個(gè)子項(xiàng)目。已經(jīng)在很多產(chǎn)品中得到應(yīng)用蹬刷,實(shí)現(xiàn)了JMS1.1規(guī)范瓢捉,可以和spring-jms輕松融合,實(shí)現(xiàn)了多種協(xié)議办成,不夠輕巧(源代碼比RocketMQ多)泡态,支持持久化到數(shù)據(jù)庫(kù),對(duì)隊(duì)列數(shù)較多的情況支持不好诈火。
4.Redis
做為一個(gè)基于內(nèi)存的K-V數(shù)據(jù)庫(kù)兽赁,其提供了消息訂閱的服務(wù),可以當(dāng)作MQ來(lái)使用冷守,目前應(yīng)用案例較少刀崖,且不方便擴(kuò)展。對(duì)于RabbitMQ和Redis的入隊(duì)和出隊(duì)操作拍摇,各執(zhí)行100萬(wàn)次亮钦,每10萬(wàn)次記錄一次執(zhí)行時(shí)間。
測(cè)試數(shù)據(jù)分為 128Bytes充活、512Bytes蜂莉、1K和10K四個(gè)不同大小的數(shù)據(jù)。
實(shí)驗(yàn)表明:入隊(duì)時(shí)混卵,當(dāng)數(shù)據(jù)比較小時(shí)Redis的性能要高于RabbitMQ映穗,而如 果數(shù)據(jù)大小超過(guò)了10K,Redis則慢的無(wú)法忍受幕随;出隊(duì)時(shí)蚁滋,無(wú)論數(shù)據(jù)大小,Redis都表現(xiàn)出非常好的性能赘淮,而RabbitMQ的出隊(duì)性能則遠(yuǎn)低于 Redis辕录。
5.Kafka/Jafka
Kafka是Apache下的一個(gè)子項(xiàng)目,是一個(gè)高性能跨語(yǔ)言分布式發(fā)布/訂閱消息隊(duì)列系統(tǒng)梢卸,而Jafka是在Kafka之上孵化而來(lái)的走诞,即Kafka的一個(gè)升級(jí)版。
具有以下特性:
- 快速持久化蛤高,可以在O(1)的系統(tǒng)開(kāi)銷下進(jìn)行消息持久化蚣旱;
- 高吞吐碑幅,在一臺(tái)普通的服務(wù)器上既可以達(dá)到10W/s的吞吐速率;完全的分布式系統(tǒng)姻锁,Broker枕赵、Producer、Consumer都原生自動(dòng)支持分布式位隶,自動(dòng)實(shí)現(xiàn)負(fù)載均衡拷窜;
- 支持Hadoop數(shù)據(jù)并行加載,對(duì)于像Hadoop的一樣的日志數(shù)據(jù)和離線分析系統(tǒng)涧黄,但又要求實(shí)時(shí)處理的限制篮昧,這是一個(gè)可行的解決方案。
- Kafka通過(guò)Hadoop的并行加載機(jī)制統(tǒng)一了在線和離線的消息處理笋妥。Apache Kafka相對(duì)于ActiveMQ是一個(gè)非常輕量級(jí)的消息系統(tǒng)懊昨,除了性能非常好之外,還是一個(gè)工作良好的分布式系統(tǒng)春宣。
何時(shí)需要消息隊(duì)列
當(dāng)你需要使用消息隊(duì)列時(shí)酵颁,首先需要考慮它的必要性。
可以使用mq的場(chǎng)景有很多月帝,最常用的幾種:
- 做業(yè)務(wù)解耦
- 最終一致性
- 廣播
- 錯(cuò)峰流控等
反之躏惋,如果需要強(qiáng)一致性,關(guān)注業(yè)務(wù)邏輯的處理結(jié)果嚷辅,則RPC顯得更為合適簿姨。
消息隊(duì)列使用場(chǎng)景
1.解耦
解耦是消息隊(duì)列要解決的最本質(zhì)問(wèn)題。所謂解耦簸搞,簡(jiǎn)單點(diǎn)講就是一個(gè)事務(wù)扁位,只關(guān)心核心的流程。而需要依賴其他系統(tǒng)但不那么重要的事情趁俊,有通知即可域仇,無(wú)需等待結(jié)果。換句話說(shuō)寺擂,基于消息的模型暇务,關(guān)心的是“通知”,而非“處理”沽讹。
舉一個(gè)例子般卑,關(guān)于訂單系統(tǒng)武鲁,訂單最終支付成功之后可能需要給用戶發(fā)送短信積分什么的爽雄,但其實(shí)這已經(jīng)不是我們系統(tǒng)的核心流程了。
如果外部系統(tǒng)速度偏慢(比如短信網(wǎng)關(guān)速度不好)沐鼠,那么主流程的時(shí)間會(huì)加長(zhǎng)很多挚瘟,用戶肯定不希望點(diǎn)擊支付過(guò)好幾分鐘才看到結(jié)果叹谁。那么我們只需要通知短信系統(tǒng)“我們支付成功了”,不一定非要等待它立即處理完成乘盖。
2.最終一致性
最終一致性指的是兩個(gè)系統(tǒng)的狀態(tài)保持一致焰檩,要么都成功,要么都失敗订框。
當(dāng)然有個(gè)時(shí)間限制析苫,理論上越快越好,但實(shí)際上在各種異常的情況下穿扳,可能會(huì)有一定延遲達(dá)到最終一致?tīng)顟B(tài)衩侥,但最后兩個(gè)系統(tǒng)的狀態(tài)是一樣的。
業(yè)界有一些為“最終一致性”而生的消息隊(duì)列矛物,如:
- Notify(阿里)
- QMQ(去哪兒)等
其設(shè)計(jì)初衷茫死,就是為了交易系統(tǒng)中的高可靠通知。
以一個(gè)銀行的轉(zhuǎn)賬過(guò)程來(lái)理解最終一致性履羞,轉(zhuǎn)賬的需求很簡(jiǎn)單峦萎,如果A系統(tǒng)扣錢(qián)成功,則B系統(tǒng)加錢(qián)一定成功忆首。反之則一起回滾爱榔,像什么都沒(méi)發(fā)生一樣。
然而雄卷,這個(gè)過(guò)程中存在很多可能的意外:
- A扣錢(qián)成功搓蚪,調(diào)用B加錢(qián)接口失敗。
- A扣錢(qián)成功丁鹉,調(diào)用B加錢(qián)接口雖然成功妒潭,但獲取最終結(jié)果時(shí)網(wǎng)絡(luò)異常引起超時(shí)。
- A扣錢(qián)成功揣钦,B加錢(qián)失敗雳灾,A想回滾扣的錢(qián),但A機(jī)器down機(jī)冯凹。
可見(jiàn)谎亩,想把這件看似簡(jiǎn)單的事真正做成,真的不那么容易宇姚。
所有跨VM的一致性問(wèn)題匈庭,從技術(shù)的角度講通用的解決方案是:
- 強(qiáng)一致性,分布式事務(wù)浑劳,但落地太難且成本太高阱持,后文會(huì)具體提到。
- 最終一致性魔熏,主要是用“記錄”和“補(bǔ)償”的方式衷咽。在做所有的不確定的事情之前鸽扁,先把事情記錄下來(lái),然后去做不確定的事情镶骗,結(jié)果可能是:成功桶现、失敗或是不確定,“不確定”(例如超時(shí)等)可以等價(jià)為失敗鼎姊。成功就可以把記錄的東西清理掉了骡和,對(duì)于失敗和不確定,可以依靠定時(shí)任務(wù)等方式把所有失敗的事情重新搞一遍相寇,直到成功為止即横。
- 回到剛才的例子,系統(tǒng)在A扣錢(qián)成功的情況下裆赵,把要給B“通知”這件事記錄在庫(kù)里(為了保證最高的可靠性可以把通知B系統(tǒng)加錢(qián)和扣錢(qián)成功這兩件事維護(hù)在一個(gè)本地事務(wù)里)东囚,通知成功則刪除這條記錄,通知失敗或不確定則依靠定時(shí)任務(wù)補(bǔ)償性地通知我們战授,直到我們把狀態(tài)更新成正確的為止页藻。
- 整個(gè)這個(gè)模型依然可以基于RPC來(lái)做,但可以抽象成一個(gè)統(tǒng)一的模型植兰,基于消息隊(duì)列來(lái)做一個(gè)“企業(yè)總線”份帐。
- 具體來(lái)說(shuō),本地事務(wù)維護(hù)業(yè)務(wù)變化和通知消息楣导,一起落地(失敗則一起回滾)废境,然后RPC到達(dá)broker,在broker成功落地后筒繁,RPC返回成功噩凹,本地消息可以刪除。否則本地消息一直靠定時(shí)任務(wù)輪詢不斷重發(fā)毡咏,這樣就保證了消息可靠落地broker驮宴。
- broker往consumer發(fā)送消息的過(guò)程類似,一直發(fā)送消息呕缭,直到consumer發(fā)送消費(fèi)成功確認(rèn)堵泽。
- 我們先不理會(huì)重復(fù)消息的問(wèn)題,通過(guò)兩次消息落地加補(bǔ)償恢总,下游是一定可以收到消息的迎罗。然后依賴狀態(tài)機(jī)版本號(hào)等方式做判重,更新自己的業(yè)務(wù)片仿,就實(shí)現(xiàn)了最終一致性纹安。
最終一致性不是消息隊(duì)列的必備特性,但確實(shí)可以依靠消息隊(duì)列來(lái)做最終一致性的事情。
另外钻蔑,所有不保證100%不丟消息的消息隊(duì)列,理論上無(wú)法實(shí)現(xiàn)最終一致性奸鸯。好吧咪笑,應(yīng)該說(shuō)理論上的100%,排除系統(tǒng)嚴(yán)重故障和bug娄涩。
像Kafka一類的設(shè)計(jì)窗怒,在設(shè)計(jì)層面上就有丟消息的可能(比如定時(shí)刷盤(pán),如果掉電就會(huì)丟消息)蓄拣。哪怕只丟千分之一的消息扬虚,業(yè)務(wù)也必須用其他的手段來(lái)保證結(jié)果正確。
2.廣播
消息隊(duì)列的基本功能之一是進(jìn)行廣播球恤。
如果沒(méi)有消息隊(duì)列辜昵,每當(dāng)一個(gè)新的業(yè)務(wù)方接入,我們都要聯(lián)調(diào)一次新接口咽斧。有了消息隊(duì)列堪置,我們只需要關(guān)心消息是否送達(dá)了隊(duì)列,至于誰(shuí)希望訂閱张惹,是下游的事情舀锨,無(wú)疑極大地減少了開(kāi)發(fā)和聯(lián)調(diào)的工作量。
比如本文開(kāi)始提到的產(chǎn)品中心發(fā)布產(chǎn)品變更的消息宛逗,以及景點(diǎn)庫(kù)很多去重更新的消息坎匿,可能“關(guān)心”方有很多個(gè),但產(chǎn)品中心和景點(diǎn)庫(kù)只需要發(fā)布變更消息即可雷激,誰(shuí)關(guān)心誰(shuí)接入替蔬。
3.錯(cuò)峰與流控
試想上下游對(duì)于事情的處理能力是不同的。
比如屎暇,Web前端每秒承受上千萬(wàn)的請(qǐng)求进栽,并不是什么神奇的事情,只需要加多一點(diǎn)機(jī)器恭垦,再搭建一些LVS負(fù)載均衡設(shè)備和Nginx等即可快毛。
但數(shù)據(jù)庫(kù)的處理能力卻十分有限,即使使用SSD加分庫(kù)分表番挺,單機(jī)的處理能力仍然在萬(wàn)級(jí)唠帝。由于成本的考慮,我們不能奢求數(shù)據(jù)庫(kù)的機(jī)器數(shù)量追上前端玄柏。
這種問(wèn)題同樣存在于系統(tǒng)和系統(tǒng)之間襟衰,如短信系統(tǒng)可能由于短板效應(yīng),速度卡在網(wǎng)關(guān)上(每秒幾百次請(qǐng)求)粪摘,跟前端的并發(fā)量不是一個(gè)數(shù)量級(jí)瀑晒。
但用戶晚上個(gè)半分鐘左右收到短信绍坝,一般是不會(huì)有太大問(wèn)題的。如果沒(méi)有消息隊(duì)列苔悦,兩個(gè)系統(tǒng)之間通過(guò)協(xié)商轩褐、滑動(dòng)窗口等復(fù)雜的方案也不是說(shuō)不能實(shí)現(xiàn)。
但系統(tǒng)復(fù)雜性指數(shù)級(jí)增長(zhǎng)玖详,勢(shì)必在上游或者下游做存儲(chǔ)把介,并且要處理定時(shí)、擁塞等一系列問(wèn)題蟋座。而且每當(dāng)有處理能力有差距的時(shí)候拗踢,都需要單獨(dú)開(kāi)發(fā)一套邏輯來(lái)維護(hù)這套邏輯。所以向臀,利用中間系統(tǒng)轉(zhuǎn)儲(chǔ)兩個(gè)系統(tǒng)的通信內(nèi)容巢墅,并在下游系統(tǒng)有能力處理這些消息的時(shí)候,再處理這些消息券膀,是一套相對(duì)較通用的方式砂缩。
消息隊(duì)列使用總結(jié)
1.消息隊(duì)列不是萬(wàn)能的,對(duì)于需要強(qiáng)事務(wù)保證而且延遲敏感的三娩,RPC是優(yōu)于消息隊(duì)列的庵芭。
2.對(duì)于一些無(wú)關(guān)痛癢,或者對(duì)于別人非常重要但是對(duì)于自己不是那么關(guān)心的事情雀监,可以利用消息隊(duì)列去做双吆。
3.支持最終一致性的消息隊(duì)列,能夠用來(lái)處理延遲不那么敏感的“分布式事務(wù)”場(chǎng)景会前,而且相對(duì)于笨重的分布式事務(wù)好乐,可能是更優(yōu)的處理方式。
4.當(dāng)上下游系統(tǒng)處理能力存在差距的時(shí)候瓦宜,利用消息隊(duì)列做一個(gè)通用的“漏斗”蔚万,在下游有能力處理的時(shí)候,再進(jìn)行分發(fā)临庇。
5.如果下游有很多系統(tǒng)關(guān)心你的系統(tǒng)發(fā)出的通知的時(shí)候反璃,果斷地使用消息隊(duì)列吧。
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布假夺!