官網(wǎng):https://www.rabbitmq.com/getstarted.html
RabbitMQ實(shí)現(xiàn)了AMQP協(xié)議
角色:
Broker:RabbitMQ服務(wù)器(負(fù)責(zé)消息的存儲(chǔ)轉(zhuǎn)發(fā)),默認(rèn)端口5672
Connection:TCP長連接溶推,生產(chǎn)者或消費(fèi)者和Broker之間建立的通道
Channel:
如果所有的生產(chǎn)者發(fā)送消息和消費(fèi)者接收消息徊件,都直接創(chuàng)建和釋放TCP長連接的話, 對(duì)于Broker來說肯定會(huì)造成很大的性能損耗,也會(huì)浪費(fèi)時(shí)間蒜危。
所以在AMQP里面引入了 Channel的概念虱痕,它是一個(gè)虛擬的連接。我們把它翻譯 成通道辐赞,或者消息信道部翘。這樣我們就可以在保持的TCP長連接里面去創(chuàng)建和釋放
Channel,大大了減少了資源消耗。
不同的Channel是相互隔離的,每個(gè)Channel都有自己的編號(hào)响委。對(duì)于每個(gè)客戶端線
程來說新思,Channel就沒必要共享了窖梁,各自用自己的Channel。
另外一個(gè)需要注意的是,Channel是RabbitMQ原生API里面的最重要的編程接口,
也就是說我們定義交換機(jī)夹囚、隊(duì)列纵刘、綁定關(guān)系,發(fā)送消息荸哟,消費(fèi)消息假哎,調(diào)用的都是Channel 接口上的方法。
Queue:
在Broker上有一個(gè)對(duì)象用來存儲(chǔ)消息敲茄,在RabbitMQ里面這個(gè)對(duì)象叫做Queue位谋。
實(shí)際上RabbitMQ是用數(shù)據(jù)庫來存儲(chǔ)消息的,這個(gè)數(shù)據(jù)庫跟RabbitMQ 一樣是用Erlang 開發(fā)的,名字叫Mnesia堰燎。我們可以在磁盤上找到Mnesia的存儲(chǔ)路徑掏父。
Consumer
消息到底是Broker推送給消費(fèi)者的?還是消費(fèi)者主動(dòng)獲取的秆剪?消費(fèi)者消費(fèi)消息有兩
種模式赊淑。
一種是Pull模式,對(duì)應(yīng)的方法是basicGet消息存放在服務(wù)端仅讽,只有消費(fèi)者主動(dòng)獲
取才能拿到消息陶缺。如果每隔一段時(shí)間獲取一次消息,消息的實(shí)時(shí)性會(huì)降低洁灵。但是好處是 可以根據(jù)自己的消費(fèi)能力決定獲取消息的頻率饱岸。
另一種是Push模式,對(duì)應(yīng)的方法是basicConsume只要生產(chǎn)者發(fā)消息到服務(wù)器, 就馬上推送給消費(fèi)者徽千,消息保存在客戶端苫费,實(shí)時(shí)性很高,如果消費(fèi)不過來有可能會(huì)造成 消息積壓双抽。Spring AMQP是push方式百框,通過事件機(jī)制對(duì)隊(duì)列進(jìn)行監(jiān)聽,只要有消息到 達(dá)隊(duì)列牍汹,就會(huì)觸發(fā)消費(fèi)消息的方法铐维。
RabbitMQ 中 pull 和 push 都有實(shí)現(xiàn)。kafka 和 RocketMQ 只有 pull慎菲。
由于隊(duì)列有FIFO的特性嫁蛇,只有確定前一條消息被消費(fèi)者接收之后,Broker才會(huì)把 這條消息從數(shù)據(jù)庫刪除露该,繼續(xù)投遞下一條消息棠众。
一個(gè)消費(fèi)者是可以監(jiān)聽多個(gè)隊(duì)列的,一個(gè)隊(duì)列也可以被多個(gè)消費(fèi)者監(jiān)聽。
但是在生產(chǎn)環(huán)境中闸拿,我們一般是建議一個(gè)消費(fèi)者只處理一個(gè)隊(duì)列的消息空盼。如果需要 提升處理消息的能力,可以增加多個(gè)消費(fèi)者新荤。這個(gè)時(shí)候消息會(huì)在多個(gè)消費(fèi)者之間輪詢揽趾。
Exchange
現(xiàn)在我們來思考一個(gè)問題,如果要把一條消息發(fā)送給多個(gè)隊(duì)列苛骨,被多個(gè)消費(fèi)者消費(fèi),
應(yīng)該怎么做篱瞎?生產(chǎn)者是不是必須要調(diào)用多次basicPublish的方法,依次發(fā)送給多個(gè)隊(duì) 列痒芝?就像消息推送的這種場景俐筋,有成干上萬個(gè)隊(duì)列的時(shí)候,對(duì)生產(chǎn)者來說壓力太大了严衬。
有沒有更好的辦法呢澄者?其實(shí),RabbitMQ已經(jīng)為我們考慮到了這一點(diǎn)请琳,它設(shè)計(jì)了一 個(gè)幫我們路由消息的組件粱挡,叫做Exchange。
也就是說俄精,不管有多少個(gè)隊(duì)列需要接收消息询筏,我都只需要發(fā)送到Exchange就OK 了,由它幫我來分發(fā)竖慧。Exchange是不會(huì)存儲(chǔ)消息的嫌套,它只做一件事情,根據(jù)規(guī)則分發(fā)消 息圾旨。
那么灌危,Exchange和這些需要接收消息的隊(duì)列必須建立一個(gè)綁定關(guān)系,并且為每個(gè)隊(duì)
列指定一個(gè)特殊的標(biāo)識(shí)碳胳。
Exchange和隊(duì)列是多對(duì)多的綁定關(guān)系,也就說沫勿,一個(gè)交換機(jī)的消息一個(gè)路由給多個(gè) 隊(duì)列挨约,一個(gè)隊(duì)列也可以接收來自多個(gè)交換機(jī)的消息。
綁定關(guān)系建立好之后产雹,生產(chǎn)者發(fā)送消息到Exchange,也會(huì)攜帶一個(gè)特殊的標(biāo)識(shí)诫惭。當(dāng) 這個(gè)標(biāo)識(shí)跟綁定的標(biāo)識(shí)匹配的時(shí)候,消息就會(huì)發(fā)給一個(gè)或者多個(gè)符合規(guī)則的隊(duì)列蔓挖。
Vhost
我們每個(gè)需要實(shí)現(xiàn)基于RabbitMQ的異步通信的系統(tǒng)夕土,都需要在Broker上創(chuàng)建自己要用到的交換機(jī)、隊(duì)列和它們的綁定關(guān)系。如果某個(gè)業(yè)務(wù)系統(tǒng)不想跟別人混用一個(gè) Broker,怎么辦怨绣?再采購一臺(tái)硬件服務(wù)器單獨(dú)安裝一個(gè)RabbitMQ服務(wù)角溃?這種方式成本太高了。在同一個(gè)硬件服務(wù)器上安裝多個(gè)RabbitMQ的服務(wù)呢篮撑?比如再運(yùn)行一個(gè)5673 的端口减细?
沒有必要這樣做,因?yàn)镽abbitMQ也考慮到了這一點(diǎn)赢笨,設(shè)計(jì)了虛擬主機(jī)VHOST未蝌。
VHOST除了可以提高硬件資源的利用率之外,還可以實(shí)現(xiàn)資源的隔離和權(quán)限的控
制茧妒。它的作用類似于編程語言中的namespace和package,不同的VHOST中可以有 同名的Exchange和Queue,它們是完全透明的萧吠。
這個(gè)時(shí)候,我們可以為不同的業(yè)務(wù)系統(tǒng)創(chuàng)建專屬于他們自己的VHOST,然后再為他
們創(chuàng)建專屬的用戶桐筏,給用戶分配對(duì)應(yīng)的VHOST的權(quán)限纸型。比如給風(fēng)控系統(tǒng)的用戶分配風(fēng)控 系統(tǒng)的VHOST的權(quán)限,這個(gè)用戶可以訪問里面的交換機(jī)和隊(duì)列九昧。給超級(jí)管理員分配所有
VHOST的權(quán)限绊袋。
我們安裝RabbitMQ的時(shí)候會(huì)自帶一個(gè)默認(rèn)的VHOST,名字是”/“ 。
我們說到RabbitMQ引入Exchange是為了實(shí)現(xiàn)消息的靈活路由铸鹰,至U底有哪些路由 方式癌别?
- Direct 直連
一個(gè)隊(duì)列與直連類型的交換機(jī)綁定,需指定一個(gè)明確的綁定鍵(binding key)蹋笼。
生產(chǎn)者發(fā)送消息時(shí)會(huì)攜帶一個(gè)路由鍵(routing key)展姐。 - Topic 主題
"#"代表0個(gè)或者多個(gè)單詞
*代表不多不少一個(gè)單詞
單詞(word)指的是用英文的點(diǎn)〃.“隔開的字符。例如a.bcdef是3個(gè)單詞剖毯。 - Fanout 廣播
RabbitMQ進(jìn)階知識(shí)
1圾笨、 怎么實(shí)現(xiàn)訂單延遲關(guān)閉?
RabbitMQ本身不支持延遲投遞,總的來說有2種實(shí)現(xiàn)方案:
1逊谋、先存儲(chǔ)到數(shù)據(jù)庫擂达,用定時(shí)任務(wù)掃描
2、利用 RabbitMQ 的死信隊(duì)列 (Dead Letter Queue)實(shí)現(xiàn)(隊(duì)列在創(chuàng)建的時(shí)候可以指定一個(gè)死信交換機(jī)胶滋,當(dāng)消息過期(過期時(shí)間設(shè)置為訂單關(guān)閉的延時(shí)時(shí)間)未消費(fèi)會(huì)發(fā)送到死信交換機(jī)板鬓,消費(fèi)者監(jiān)聽死信交換機(jī)消費(fèi)過期消息)
使用死信隊(duì)列實(shí)現(xiàn)延時(shí)消息的缺點(diǎn):
1) 如果統(tǒng)一用隊(duì)列來設(shè)置消息的TTL,當(dāng)梯度非常多的情況下,比如1分鐘究恤,2 分鐘俭令,5分鐘,10分鐘部宿,20分鐘抄腔,30分鐘 .....需要?jiǎng)?chuàng)建很多交換機(jī)和隊(duì)列來路由消息。
2) 如果單獨(dú)設(shè)置消息的TTL,則可能會(huì)造成隊(duì)列中的消息阻塞一一前一條消息沒 有出隊(duì)(沒有被消費(fèi)),后面的消息無法投遞(比如第一條消息過期TTL是30min,第 二條消息TTL是10mino 10分鐘后赫蛇,即使第二條消息應(yīng)該投遞了绵患,但是由于第一條消息 還未出隊(duì),所以無法投遞)棍掐。
3) 可能存在一定的時(shí)間誤差藏雏。
在RabbitMQ 3.5.7及以后的版本提供了一個(gè)插件 (rabbitmq-delayed-message-exchange)來實(shí)現(xiàn)延時(shí)隊(duì)列功能(Linux 和 Windows 都可用)。同時(shí)插件依賴Erlang/OPT18.0及以上作煌。
除了消息過期掘殴,還有什么情況消息會(huì)變成死信?
- 消息被消費(fèi)者拒絕并且未設(shè)置重回隊(duì)列:(NACK || Reject) && requeue== false
- 隊(duì)列達(dá)到最大長度粟誓,超過了 Max length (消息數(shù))或者M(jìn)ax length bytes (字 節(jié)數(shù))奏寨,最先入隊(duì)的消息會(huì)被發(fā)送到DLX。