MQ的消息模型及在工作上應(yīng)用場(chǎng)景
MQ介紹
根據(jù)某科的介紹,MQ(message queue),叫消息隊(duì)列,是基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)中先進(jìn)先出的一種數(shù)據(jù)機(jī)構(gòu)。
一般用來(lái)解決應(yīng)用解耦,異步消息,流量削鋒等問(wèn)題,實(shí)現(xiàn)高性能,高可用,可伸縮和最終一致性架構(gòu)。
名詞 | 解釋 |
---|---|
解耦 | 簡(jiǎn)單說(shuō)就是積木化,每個(gè)東西都相互獨(dú)立,比如漢堡包,面包跟肉餅是相互獨(dú)立,可以單獨(dú)使用,也可以組合成一個(gè)食物 |
異步 | 去買(mǎi)漢堡包,下單之后就去玩手機(jī),等服務(wù)員叫號(hào)通知領(lǐng)取,這就叫異步;而同步是下單后,什么都不能干,直到服務(wù)員叫號(hào)才能做其他事 |
限流 | 大家 9 點(diǎn)上班,地鐵進(jìn)不去,門(mén)口做限流 |
削峰 | 遵從最后落地到數(shù)據(jù)庫(kù)的請(qǐng)求數(shù)要盡量少的原則,比如讓 1/2 的人下午開(kāi)始上班、局部停電,感興趣可以查看削峰填谷 |
消息 | 要傳輸?shù)膬?nèi)容,比如說(shuō)話、寫(xiě)信,形式不重要,按照雙方約定的格式即可 |
隊(duì)列 | 是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),排隊(duì)打疫苗,從隊(duì)尾入隊(duì),從隊(duì)頭出隊(duì) |
MQ 主要產(chǎn)品包括:RabbitMQ、ActiveMQ、RocketMQ、ZeroMQ、Kafka
通過(guò)上述的內(nèi)容,不難發(fā)現(xiàn),MQ是一種跨進(jìn)程的通信機(jī)制,用于上下游傳遞消息,而個(gè)人覺(jué)得MQ有點(diǎn)像中介, 房東發(fā)布出租信息,信息放在中介處,租客來(lái)通過(guò)中介來(lái)租房子。
使用 MQ 的好處
舉個(gè)通俗點(diǎn)的例子:
面試官希望 HR 早點(diǎn)招聘到合適的人選,于是一開(kāi)始是這樣的:
HR 問(wèn)面試官什么時(shí)候有空,把候選人資料送過(guò)去,并且親自看到面試官看完并給出結(jié)論后才離開(kāi),時(shí)間一長(zhǎng),大家都覺(jué)得很麻煩,HR 覺(jué)得候選人不錯(cuò),面試官覺(jué)得不合適,容易發(fā)生爭(zhēng)執(zhí)。
后面,HR 跟面試官說(shuō),我把資料放在桌子上,你有空記得看,然后每次面試官看到桌子有資料后,都會(huì)拿起來(lái)看。
在這個(gè)場(chǎng)景上,HR就是生產(chǎn)者,面試官就是消費(fèi)者,桌子就是MQ。
使用MQ帶來(lái)的好處是解決應(yīng)用解耦,異步消息,流量削鋒:
- HR想給資料時(shí),無(wú)需知道面試官是否有空,只需要把資料放桌子上即可,這樣大家都有時(shí)間做別的事,節(jié)省大家的時(shí)間。。應(yīng)用解耦,每個(gè)成員都是獨(dú)立的,不受其他成員影響。面試官不關(guān)心誰(shuí)放的資料,HR 不關(guān)心誰(shuí)哪個(gè)面試官看的資料
- 如果別的組也有招聘需求(且當(dāng)是同一工種,比如 Java 后端開(kāi)發(fā)),HR依然把資料放在桌子上,兩個(gè)面試官只需要各自從桌子上取資料查閱即可。異步消息,HR 把資料放在桌子上即可,就可以去做別的事,比一開(kāi)始親自看著的效率高太多了
- HR無(wú)需關(guān)注面試官什么時(shí)候查看資料,也不關(guān)注看資料用多長(zhǎng)的時(shí)間,減少矛盾。流量削峰,HR 給資料的頻率不固定,面試官看資料的時(shí)長(zhǎng)也不固定,面試官只需要在固定時(shí)間內(nèi)看完給結(jié)論即可,不會(huì)有那么大的壓力。
使用 MQ 的壞處
名詞 | 解釋 |
---|---|
引入復(fù)雜度 | 「桌子」這東西是使用 MQ 后多出來(lái)的,需要有地方放桌子,而且流程會(huì)變長(zhǎng),更復(fù)雜 |
不一致性 | HR會(huì)以為面試官應(yīng)該看了資料,但實(shí)際面試官可能還沒(méi)開(kāi)始看,這就導(dǎo)致了不一致性的問(wèn)題,但在約束好的時(shí)間內(nèi),面試官最終的查閱狀態(tài)與HR的認(rèn)知必須是要一致的,這就是所謂的最終不一致性 |
系統(tǒng)可用性降低 | 如果桌子壞了,后面的流程是不是就中斷了 |
當(dāng)然,使用MQ
還有很多問(wèn)題要解決,比如資料無(wú)辜丟了、一樣的資料,給了好多份、資料被搶、本來(lái)資料給面試官 A,結(jié)果給到面試官 B等場(chǎng)景都是需要處理的。
什么時(shí)候用 MQ
名詞 | 解釋 |
---|---|
生產(chǎn)者不需要從消費(fèi)者處獲得反饋 | 面試官到底看了沒(méi)有,HR 根本不需關(guān)注,默認(rèn)面試官是看了,否則就只能采取監(jiān)督看完的方式 |
容許不一致性 | HR 可能會(huì)發(fā)現(xiàn)有時(shí)候面試官說(shuō)看了資料,但實(shí)際沒(méi)看的情況,只有 HR 愿意相信面試官最后看了即可 |
有效 | 解耦、提速等帶來(lái)的收益大于放置書(shū)桌是有成本的,那說(shuō)明是有效的。比如一個(gè)月甚至半年才有一份簡(jiǎn)歷,那還不如直接當(dāng)面給更高效 |
消息模型
什么是 JMS
Java 消息服務(wù)指的是兩個(gè)應(yīng)用程序之間進(jìn)行異步通信的 API,它為標(biāo)準(zhǔn)消息協(xié)議和消息服務(wù)提供了一組通用接口,包括創(chuàng)建、發(fā)送、讀取消息等,用于支持 JAVA 應(yīng)用程序開(kāi)發(fā)。
為什么需要 JMS
在JAVA
中,如果兩個(gè)應(yīng)用程序之間對(duì)各自都不了解,甚至這兩個(gè)程序可能部署在不同地方上,那么它們之間如何發(fā)送消息呢?
舉個(gè)例子,一個(gè)應(yīng)用程序 A 部署在印度,另一個(gè)應(yīng)用程序部署在美國(guó),然后每當(dāng) A 觸發(fā)某件事后,B 想從 A 獲取一些更新信息。
當(dāng)然,也有可能不止一個(gè) B 對(duì) A 的更新信息感興趣,可能會(huì)有 N 個(gè)類似 B 的應(yīng)用程序想從 A 中獲取更新的信息。
在這種情況下,JAVA
提供了最佳的解決方案-JMS
,完美解決了上面討論的問(wèn)題。
點(diǎn)對(duì)點(diǎn)模型
在該模型中,有下列概念:
消息隊(duì)列 (Queue)、發(fā)送者 (Sender)、接收者 (Receiver)
每個(gè)消息都被發(fā)送到一個(gè)特定的隊(duì)列,接收者從隊(duì)列中獲取消息。隊(duì)列保留著消息,直到它們被消費(fèi)或超時(shí)。
- 支持存在多個(gè)消費(fèi)者
- 每個(gè)消息只有一個(gè)消費(fèi)者(一旦消息被消費(fèi),消息就不再在消息隊(duì)列中)
- 發(fā)送者和接收者之間在時(shí)間上沒(méi)有依賴性,也就是說(shuō)當(dāng)發(fā)送者發(fā)送了消息之后,不管接收者有沒(méi)有正在運(yùn)行,它不會(huì)影響到消息被發(fā)送到隊(duì)列
- 接收者在成功接收消息之后需向隊(duì)列應(yīng)答成功
如果希望發(fā)送的每個(gè)消息都應(yīng)該被成功處理的話,那么就需要點(diǎn)對(duì)點(diǎn)模型。
女神想找備胎 A 聊天,就單聊備 A,這就是點(diǎn)對(duì)點(diǎn),只有一個(gè)人能收到消息
發(fā)布訂閱模型
消息生產(chǎn)者(發(fā)布)將消息發(fā)布到topic
中,同時(shí)有多個(gè)消息消費(fèi)者(訂閱)消費(fèi)該消息。和點(diǎn)對(duì)點(diǎn)方式不同,發(fā)布到 topic 的消息會(huì)被所有訂閱者消費(fèi)。。
在該模型中,有下列概念:
主題(Topic)、發(fā)布者(Publisher)、訂閱者(Subscriber)
客戶端將消息發(fā)送到主題。多個(gè)發(fā)布者將消息發(fā)送到 Topic,系統(tǒng)將這些消息傳遞給多個(gè)訂閱者。
- 每個(gè)消息可以有多個(gè)消費(fèi)者
- 發(fā)布者和訂閱者有時(shí)間依賴性,只有當(dāng)客戶端創(chuàng)建訂閱后才能接受消息,且訂閱者需一直保持活動(dòng)狀態(tài)以接收消息
- 訂閱者創(chuàng)建一個(gè)可持久化的訂閱。這樣,即使訂閱者沒(méi)有被激活(運(yùn)行),它也能接收到發(fā)布者的消息。
如果希望發(fā)送的消息可以不被做任何處理、或者被一個(gè)消費(fèi)者處理、或者可以被多個(gè)消費(fèi)者處理的話,那么可以采用 Pub/Sub 模型。。
女神發(fā)了個(gè)朋友圈,她的備胎們都能看到,這就是發(fā)布/訂閱。
兩個(gè)模型之間的區(qū)別
點(diǎn)對(duì)點(diǎn)模型下,不可重復(fù)消費(fèi)。
點(diǎn)對(duì)點(diǎn)下,一個(gè)隊(duì)列可以有多個(gè)消費(fèi)者,生產(chǎn)者發(fā)送一條消息到隊(duì)列,消費(fèi)者能用隊(duì)列取出并且消費(fèi)消息,一旦消息被消費(fèi)后,隊(duì)列不再有存儲(chǔ),所以其他消費(fèi)者不能消費(fèi)到已經(jīng)被消費(fèi)的消息,如果一直沒(méi)有消費(fèi)者處理,這條消息就會(huì)被保存,直到有可用的消費(fèi)者為止。
發(fā)布訂閱模型,可以重復(fù)消費(fèi)。
發(fā)布訂閱下,發(fā)布者發(fā)送到 topic 的消息,只有訂閱了 topic 的訂閱者才會(huì)收到消息,注意是所有訂閱這個(gè) topic 的服務(wù)都能收到,所以能達(dá)到消息拷貝的效果
MQ 的在工作上應(yīng)用場(chǎng)景
雖然上面以一個(gè)招聘的例子來(lái)講解 MQ 的應(yīng)用場(chǎng)景,但可能還是會(huì)有疑問(wèn),不知道工作上是如何的,因此再講講工作上的場(chǎng)景。
異步
之前負(fù)責(zé)的一個(gè)需求叫老帶新,大致流程如下:
1)用戶下單后,會(huì)先判斷下單者身份
2)如果是新用戶,再判斷是否有邀請(qǐng)人
3)如果有,再判斷邀請(qǐng)人身份
4)如果是老用戶,就給雙方發(fā)積分
這樣的話,用戶的流程就會(huì)發(fā)生變化:
很明顯,這樣做的問(wèn)題是:新增的邏輯存在堵塞下單主流程的風(fēng)險(xiǎn)。
既然同步處理會(huì)有問(wèn)題,那就改異步吧,改完變成這樣:
異步的好處是,即使老帶新邏輯有問(wèn)題,也不會(huì)堵塞下單流程。
這樣的好日子沒(méi)過(guò)幾天,問(wèn)題又來(lái)了:老帶新業(yè)務(wù)頻繁改動(dòng),導(dǎo)致下單系統(tǒng)頻繁發(fā)版本,存在質(zhì)量隱患。
使用 MQ
由于依賴訂單系統(tǒng)的業(yè)務(wù)越來(lái)越多,為了保證下單系統(tǒng)的穩(wěn)定性,業(yè)務(wù)層面必須解耦,只需要把支付成功的消息告訴別的業(yè)務(wù),他們收到了通知后自行處理,我們只管自己的流程,后續(xù)還有其他業(yè)務(wù)系統(tǒng),直接訂閱我們發(fā)送的支付成功消息。
「MQ 帶來(lái)的問(wèn)題」
- 如何保證消息隊(duì)列的高可用?
- 如何保證消息不被重復(fù)消費(fèi)?
- 如何處理消息丟失的問(wèn)題?
- 如何保證消息的順序性?
- 如何處理消息隊(duì)列大量消息積壓?
上面這些問(wèn)題,都是實(shí)際工作上會(huì)遇到的,往往也都是測(cè)試點(diǎn),下面也會(huì)有提及到,簡(jiǎn)單了解下即可。
「MQ 產(chǎn)品的對(duì)比」
產(chǎn)品 | 單機(jī)吞吐量 | 時(shí)效性 | 可用性 | 消息可靠性 | 功能支持 |
---|---|---|---|---|---|
ActiveMQ | 萬(wàn)級(jí) | 毫秒級(jí) | 高 | 較低概率出現(xiàn)丟失數(shù)據(jù) | 極其完備 |
RabbitMQ | 萬(wàn)級(jí) | 微妙級(jí) | 高 | 基本不丟 | erlang 開(kāi)發(fā) |
RocketMQ | 十萬(wàn)級(jí) | 毫秒級(jí) | 非常高 | 可配置 0 丟失 | 分布式 |
Kafka | 十萬(wàn)級(jí) | 毫秒級(jí) | 非常高高 | 可配置 0 丟失 | 分布式 |
而在選擇上,一般公司都是用Kafka
跟RocketMQ
較多。
「MQ 的測(cè)試點(diǎn)」
生產(chǎn)者
- 生成的數(shù)據(jù)格式是否跟定義的一致
- 數(shù)據(jù)是否有成功推送到隊(duì)列里
- 數(shù)據(jù)是否有成功推動(dòng)到對(duì)應(yīng)的 topic
- 推送失敗時(shí)如何處理
- 重復(fù)推送同一條數(shù)據(jù),如何處理
- 不同順序推送消息,注意隊(duì)列優(yōu)先級(jí)
- 推消息耗時(shí)
- 隊(duì)列容量達(dá)到上限,無(wú)法推送后如何處理
消費(fèi)者
- 消費(fèi)的消息是否來(lái)自訂閱的 topic
- 消息被消費(fèi)了,是否有清除
- 生產(chǎn)者推送過(guò)快,消費(fèi)速度過(guò)慢(堵塞),會(huì)如何
- 無(wú)法消費(fèi)沒(méi)訂閱的 topic 消息
- 生產(chǎn)者推送消息后,消費(fèi)者接受到的消息內(nèi)容跟生產(chǎn)者推的一致
- 如何處理重復(fù)消息,比如冪等
- 處理超時(shí)
- 消息處理失敗
- 消費(fèi)消息的優(yōu)先級(jí)是否跟推的一致
- 消費(fèi)消息耗時(shí)
- 消費(fèi)者宕機(jī),消息堆積,無(wú)人處理,會(huì)如何處理
- 是否能正常消費(fèi)消息
隊(duì)列
- 宕機(jī)恢復(fù)后,消息是否丟失
- 宕機(jī)預(yù)案,多久能恢復(fù),如果無(wú)法恢復(fù)的預(yù)案
- 不同的消息格式,是否能正常識(shí)別及轉(zhuǎn)發(fā)
小結(jié)
來(lái)來(lái)去去,花了一周的時(shí)間來(lái)整理這堆信息,之前有測(cè)過(guò)mq,但沒(méi)有太了解這玩意,從介紹、選型、測(cè)試點(diǎn),加深了對(duì) mq 的印象,但由于沒(méi)做過(guò) mq 的性能測(cè)試跟自動(dòng)化測(cè)試,所以這塊暫時(shí)沒(méi)有心得能輸出,如果后續(xù)有類似的經(jīng)歷,也會(huì)分享下。
本文留下了一個(gè)懸念,針對(duì)消息不一致的問(wèn)題,大家是怎么解決的,這邊非常好奇,所以下篇計(jì)劃會(huì)寫(xiě)分布式事務(wù),想深入了解下細(xì)節(jié)~更多關(guān)于MQ工作場(chǎng)景消息模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談MyBatis通用Mapper實(shí)現(xiàn)原理
這篇文章主要介紹了淺談MyBatis通用Mapper實(shí)現(xiàn)原理,本文會(huì)先介紹通用 Mapper 的簡(jiǎn)單原理,然后使用最簡(jiǎn)單的代碼來(lái)實(shí)現(xiàn)這個(gè)過(guò)程。感興趣的小伙伴們可以參考一下2018-10-10SpringCloud的@RefreshScope 注解你了解嗎
這篇文章主要介紹了Spring Cloud @RefreshScope 原理及使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-09-09Java聊天室之實(shí)現(xiàn)聊天室服務(wù)端功能
這篇文章主要為大家詳細(xì)介紹了Java簡(jiǎn)易聊天室之實(shí)現(xiàn)聊天室服務(wù)端功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以了解一下2022-10-10MyBatis中的關(guān)聯(lián)關(guān)系配置與多表查詢的操作代碼
本文介紹了在MyBatis中配置和使用一對(duì)多和多對(duì)多關(guān)系的方法,通過(guò)合理的實(shí)體類設(shè)計(jì)、Mapper接口和XML文件的配置,我們可以方便地進(jìn)行多表查詢,并豐富了應(yīng)用程序的功能和靈活性,需要的朋友可以參考下2023-09-09Java中replace與replaceAll的區(qū)別與測(cè)試
replace和replaceAll是JAVA中常用的替換字符的方法,下面這篇文章主要給大家介紹了關(guān)于Java中replace與replaceAll的區(qū)別與測(cè)試,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09一文學(xué)會(huì)如何在SpringBoot中使用線程池執(zhí)行定時(shí)任務(wù)
在開(kāi)發(fā)現(xiàn)代應(yīng)用程序時(shí),定時(shí)任務(wù)是一項(xiàng)常見(jiàn)的需求,SpringBoot提供了一個(gè)強(qiáng)大的定時(shí)任務(wù)框架,可以輕松地執(zhí)行各種定時(shí)任務(wù),結(jié)合線程池的使用,可以更好地管理任務(wù)的執(zhí)行,提高系統(tǒng)的性能和穩(wěn)定性,本文將介紹如何在Spring Boot中使用線程池執(zhí)行定時(shí)任務(wù)2023-06-06java基于odbc連接oracle的實(shí)現(xiàn)方法
這篇文章主要介紹了java基于odbc連接oracle的實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了連接操作的具體步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-09-09Spring Data分頁(yè)與排序的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Spring Data分頁(yè)與排序的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12整理總結(jié)Java多線程程序編寫(xiě)的要點(diǎn)
這篇文章主要介紹了Java多線程程序編寫(xiě)的要點(diǎn),包括線程的狀態(tài)控制和優(yōu)先級(jí)以及線程的通信問(wèn)題等方面,非常之全面!需要的朋友可以參考下2016-01-01