一、MQ介紹
1.什么是MQ?為什么要用MQ?
1.1MQ定義
MessageQueue,消息隊列。 隊列,是一種FIFO 先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。消息由生產(chǎn)者發(fā)送到MQ進(jìn)行排隊,然后按原來的順序交由消息的消費者進(jìn)行處理。
例如:QQ和微信就是典型的MQ。
1.2MQ的作用主要有以下三個方面
1)異步
例子:快遞員發(fā)快遞,直接到客戶家效率會很低。引入菜鳥驛站后,快遞員只需要把快遞放到菜鳥驛站,就可以繼續(xù)發(fā)其他快遞去了??蛻粼侔醋约旱臅r間安排去菜鳥驛站取快遞。
作用:異步能提高系統(tǒng)的響應(yīng)速度、吞吐量。
2)解耦
例子:《Thinking in JAVA》很經(jīng)典,但是都是英文,我們看不懂,所以需要編輯社,將文章翻譯成其他語言,這樣就可以完成英語與其他語言的交流。
作用:
a)服務(wù)之間進(jìn)行解耦,才可以減少服務(wù)之間的影響。提高系統(tǒng)整體的穩(wěn)定性以及可擴(kuò)展性。
b)另外,解耦后可以實現(xiàn)數(shù)據(jù)分發(fā)。生產(chǎn)者發(fā)送一個消息后,可以由一個或者多個消費者進(jìn)行消費,并且消費者的增加或者減少對生產(chǎn)者沒有影響。
3)削峰
例子:長江每年都會漲水,但是下游出水口的速度是基本穩(wěn)定的,所以會漲水。引入三峽大壩后,可以把水儲存起來,下游慢慢排水。
作用:以穩(wěn)定的系統(tǒng)資源應(yīng)對突發(fā)的流量沖擊。
2.MQ的優(yōu)缺點
?上面MQ的作用也就是使用MQ的優(yōu)點。 但是引入MQ也是有他的缺點的:
a)系統(tǒng)可用性降低
系統(tǒng)引入的外部依賴增多,系統(tǒng)的穩(wěn)定性就會變差。一旦MQ宕機(jī),對業(yè)務(wù)會產(chǎn)生影響。這就需要考慮如何保證MQ的高可用。
b)系統(tǒng)復(fù)雜度提高
引入MQ后系統(tǒng)的復(fù)雜度會大大提高。以前服務(wù)之間可以進(jìn)行同步的服務(wù)調(diào)用,引入MQ后,會變?yōu)楫惒秸{(diào)用,數(shù)據(jù)的鏈路就會變得更復(fù)雜。并且還會帶來其他一些問題。比如:如何保證消費不會丟失?不會被重復(fù)調(diào)用?怎么保證消息的順序性等問題。
c)消息一致性問題
A系統(tǒng)處理完業(yè)務(wù),通過MQ發(fā)送消息給B、C系統(tǒng)進(jìn)行后續(xù)的業(yè)務(wù)處理。如果B系統(tǒng)處理成功,C系統(tǒng)處理失敗怎么辦?這就需要考慮如何保證消息數(shù)據(jù)處理的一致性。
3.幾大MQ產(chǎn)品特點比較
常用的MQ產(chǎn)品包括Kafka、RabbitMQ和RocketMQ。我們對這三個產(chǎn)品做下簡單的比較,重點需要理解他們的適用場景。

關(guān)于RabbitMQ的功能特性,可以在官網(wǎng)(?https://www.rabbitmq.com/?)上看到,包含 Asynchronous Message(異步消息)、Developer Experience(開發(fā)體驗)、Distributed Deployment(分布式部署)、Enterprise & Cloud Ready(企業(yè)云部署)、Tools & Plugins(工具和插件)、Management & Monitoring(管理和監(jiān)控)六大部分。所以其中的功能是相當(dāng)豐富的,而我們肯定只能關(guān)注重點的部分內(nèi)容,所以還是要經(jīng)常到官網(wǎng)上去看看的。
二、Rabbitmq安裝
1.實驗環(huán)境
準(zhǔn)備了三臺虛擬機(jī) 192.168.232.128~130,預(yù)備搭建三臺機(jī)器的集群。
三臺機(jī)器均預(yù)裝CentOS7 操作系統(tǒng)。分別配置機(jī)器名 worker1,worker2,worker3。然后需要關(guān)閉防火墻(或者找到RabbitMQ的業(yè)務(wù)端口全部打開。 5672(amqp端口);15672(http Api端口);25672(集群通信端口))。
2.版本選擇
RabbitMQ版本,通常與他的大的功能是有關(guān)系的。3.8.x版本主要是圍繞Quorum Queue功能,而3.9.x版本主要是圍繞Streams功能。目前還有3.10.x版本,還在rc階段。我們這次選擇3.9.15版本。
RabbitMQ是基于Erlang語言開發(fā),所以安裝前需要安裝Erlang語言環(huán)境。需要注意下的是RabbitMQ與ErLang是有版本對應(yīng)關(guān)系的。3.9.15版本的RabbitMQ只支持23.2以上到24.3版本的Erlang。
Docker hub上也已經(jīng)有官方上傳的鏡像。
3.安裝Erlang語言包
這個語言包,在windows下的安裝比較簡單,是一個可執(zhí)行程序,直接圖形化安裝就行了。
Linux上的安裝稍微復(fù)雜,需要有非常多的依賴包。簡單起見,可以下載rabbitmq提供的zero dependency版本。 下載地址:https://github.com/rabbitmq/erlang-rpm/releases
下載完成后,可以嘗試使用下面的指令安裝:

這樣Erlang語言包就安裝完成了。 安裝完后可以使用 erl -version 指令檢測下erlang是否安裝成功。

4.安裝RabbitMQ
RabbitMQ的安裝方式有很多,我們采用RPM安裝包的方式。安裝包可以到github倉庫中下載發(fā)布包。下載地址:https://github.com/rabbitmq/rabbitmq-server/releases
然后使用 rpm -Uvh 指令安裝RabbitMQ的rpm包時,會報錯,需要安裝一個socat。
而這個socat我也在網(wǎng)上下載到了rpm安裝包。 socat-1.7.3.2-1.1.el7.x86_64.rpm ,但是安裝時,卻提示需要tcp_wrappers依賴。

這時,當(dāng)然可以按他的提示去安裝依賴包。 但是我就沒有這么做了。 直接用yum安裝這個socat依賴。在使用yum時,可以做一個小配置,將yum源配置成阿里的yum源,這樣速度會比較快。

socat安裝完成后,就可以安裝RabbitMQ了。

安裝完成后,可以查看下他的安裝情況。

其他常用的啟停操作:
rabbitmq-server -deched --后臺啟動服務(wù)
rabbitmqctl start_app --啟動服務(wù)
rabbitmqctl stop_app --關(guān)閉服務(wù)
這樣RabbitMQ服務(wù)就啟動完成了。 之后可以配置下打開他的Web管理頁面:

可以看到,這時需要重啟RabbitMQ服務(wù)才能生效。重啟后,就可以訪問Web控制臺了。?
訪問端口:192.168.232.129:15672。
這時,可以使用默認(rèn)的guest/guest用戶登錄。 但是注意下,默認(rèn)情況下,只允許在localhost本地登錄,遠(yuǎn)程訪問是無法登錄的。這時,可以創(chuàng)建一個管理員賬戶來登錄。

?這樣就可以用admin/admin用戶登錄Web控制臺了。
三、RabbitMQ集群搭建
1.集群模式
在RabbitMQ中,一個節(jié)點的服務(wù)其實也是作為一個集群來處理的,在web控制臺的admin-> cluster 中可以看到集群的名字,并且可以在頁面上修改。而多節(jié)點的集群有兩種方式:
1)默認(rèn)的普通集群模式
這種模式使用Erlang語言天生具備的集群方式搭建。這種集群模式下,集群的各個節(jié)點之間只會有相同的元數(shù)據(jù),即隊列結(jié)構(gòu),而消息不會進(jìn)行冗余,只存在一個節(jié)點中。消費時,如果消費的不是存有數(shù)據(jù)的節(jié)點, RabbitMQ會臨時在節(jié)點之間進(jìn)行數(shù)據(jù)傳輸,將消息從存有數(shù)據(jù)的節(jié)點傳輸?shù)较M的節(jié)點。
很顯然,這種集群模式的消息可靠性不是很高。因為如果其中有個節(jié)點服務(wù)宕機(jī)了,那這個節(jié)點上的數(shù)據(jù)就無法消費了,需要等到這個節(jié)點服務(wù)恢復(fù)后才能消費,而這時,消費者端已經(jīng)消費過的消息就有可能給不了服務(wù)端正確應(yīng)答,服務(wù)起來后,就會再次消費這些消息,造成這部分消息重復(fù)消費。 另外,如果消息沒有做持久化,重啟就消息就會丟失。
并且,這種集群模式也不支持高可用,即當(dāng)某一個節(jié)點服務(wù)掛了后,需要手動重啟服務(wù),才能保證這一部分消息能正常消費。
所以這種集群模式只適合一些對消息安全性不是很高的場景。而在使用這種模式時,消費者應(yīng)該盡量的連接上每一個節(jié)點,減少消息在集群中的傳輸。
2)鏡像模式
這種模式是在普通集群模式基礎(chǔ)上的一種增強(qiáng)方案,這也就是RabbitMQ的官方HA高可用方案。需要在搭建了普通集群之后再補(bǔ)充搭建。其本質(zhì)區(qū)別在于,這種模式會在鏡像節(jié)點中間主動進(jìn)行消息同步,而不是在客戶端拉取消息時臨時同步。
并且在集群內(nèi)部有一個算法會選舉產(chǎn)生master和slave,當(dāng)一個master掛了后,也會自動選出一個來。從而給整個集群提供高可用能力。
這種模式的消息可靠性更高,因為每個節(jié)點上都存著全量的消息。而他的弊端也是明顯的,集群內(nèi)部的網(wǎng)絡(luò)帶寬會被這種同步通訊大量的消耗,進(jìn)而降低整個集群的性能。這種模式下,隊列數(shù)量最好不要過多。
2.搭建普通集群
1:需要同步集群節(jié)點中的cookie。
默認(rèn)會在 /var/lib/rabbitmq/目錄下生成一個.erlang.cookie。 里面有一個字符串。我們要做的就是保證集群中三個節(jié)點的這個cookie字符串一致。
我們實驗中將worker1和worker3加入到worker2的RabbitMQ集群中,所以將worker2的.erlang.cookie文件分發(fā)到worker1和worker3。
2:將worker1的服務(wù)加入到worker2的集群中。
首先需要保證worker1上的rabbitmq服務(wù)是正常啟動的。 然后執(zhí)行以下指令:

- -ram 表示以Ram節(jié)點加入集群。RabbitMQ的集群節(jié)點分為disk和ram。disk節(jié)點會將元數(shù)據(jù)保存到硬盤當(dāng)中,而ram節(jié)點只是在內(nèi)存中保存元數(shù)據(jù)。
a)由于ram節(jié)點減少了很多與硬盤的交互,所以,ram節(jié)點的元數(shù)據(jù)使用性能會比較高。但是,同時,這也意味著元數(shù)據(jù)的安全性是不如disk節(jié)點的。在我們這個集群中,worker1和worker3都以ram節(jié)點的身份加入到worker2集群里,因此,是存在單點故障的。如果worker2節(jié)點服務(wù)崩潰,那么元數(shù)據(jù)就有可能丟失。在企業(yè)進(jìn)行部署時,性能與安全性需要自己進(jìn)行平衡。
b)這里說的元數(shù)據(jù)僅僅只包含交換機(jī)、隊列等的定義,而不包含具體的消息。因此,ram節(jié)點的性能提升,僅僅體現(xiàn)在對元數(shù)據(jù)進(jìn)行管理時,比如修改隊列queue,交換機(jī)exchange,虛擬機(jī)vhosts等時,與消息的生產(chǎn)和消費速度無關(guān)。
c)如果一個集群中,全部都是ram節(jié)點,那么元數(shù)據(jù)就有可能丟失。這會造成集群停止之后就啟動不起來了。RabbitMQ會盡量阻止創(chuàng)建一個全是ram節(jié)點的集群,但是并不能徹底阻止。所以,綜合考慮,官方其實并不建議使用ram節(jié)點,更推薦保證集群中節(jié)點的資源投入,使用disk節(jié)點。
然后同樣把worer3上的rabbitmq加入到worker2的集群中。
加入完成后,可以在worker2的Web管理界面上看到集群的節(jié)點情況:

也可以用后臺指令查看集群狀態(tài): rabbitmqctl cluster_status
3.搭建鏡像集群
以上就完成了普通集群的搭建。 再此基礎(chǔ)上,可以繼續(xù)搭建鏡像集群。
通常在生產(chǎn)環(huán)境中,為了減少RabbitMQ集群之間的數(shù)據(jù)傳輸,在配置鏡像策略時,會針對固定的虛擬主機(jī)virtual host來配置。
RabbitMQ中的vritual host可以類比為MySQL中的庫,針對每個虛擬主機(jī),可以配置不同的權(quán)限、策略等。并且不同虛擬主機(jī)之間的數(shù)據(jù)是相互隔離的。
我們首先創(chuàng)建一個/mirror的虛擬主機(jī),然后再添加給對應(yīng)的鏡像策略:

同樣,這些配置的策略也可以在Web控制臺操作。另外也提供了HTTP API來進(jìn)行這些操作。

這些參數(shù)需要大致了解下。其中,pattern是隊列的匹配規(guī)則, ^表示全部匹配。 ^ ha \ 這樣的配置表示以ha開頭。通常就用虛擬主機(jī)來區(qū)分就夠了,這個隊列匹配規(guī)則就配置成全匹配。
然后幾個關(guān)鍵的參數(shù):
HA mode: 可選值 all , exactly, nodes。生產(chǎn)上通常為了保證高可用,就配all。
a)all : 隊列鏡像到集群中的所有節(jié)點。當(dāng)新節(jié)點加入集群時,隊列也會被鏡像到這個節(jié)點。
b)exactly : 需要搭配一個數(shù)字類型的參數(shù)(ha-params)。隊列鏡像到集群中指定數(shù)量的節(jié)點。如果集群內(nèi)節(jié)點數(shù)少于這個數(shù)字,則隊列鏡像到集群內(nèi)的所有節(jié)點。如果集群內(nèi)節(jié)點少于這個數(shù),當(dāng)一個包含鏡像的節(jié)點停止服務(wù)后,新的鏡像就不會去另外找節(jié)點進(jìn)行鏡像備份了。
c)nodes: 需要搭配一個字符串類型的參數(shù)。將隊列鏡像到指定的節(jié)點上。如果指定的隊列不在集群中,不會報錯。當(dāng)聲明隊列時,如果指定的所有鏡像節(jié)點都不在線,那隊列會被創(chuàng)建在發(fā)起聲明的客戶端節(jié)點上。
還有其他很多參數(shù),可以后面慢慢再了解。
通常鏡像模式的集群已經(jīng)足夠滿足大部分的生產(chǎn)場景了。雖然他對系統(tǒng)資源消耗比較高,但是在生產(chǎn)環(huán)境中,系統(tǒng)的資源都是會做預(yù)留的,所以正常的使用是沒有問題的。但是在做業(yè)務(wù)集成時,還是需要注意隊列數(shù)量不宜過多,并且盡量不要讓RabbitMQ產(chǎn)生大量的消息堆積。
這樣搭建起來的RabbitMQ已經(jīng)具備了集群特性,往任何一個節(jié)點上發(fā)送消息,消息都會及時同步到各個節(jié)點中。而在實際企業(yè)部署時,往往會以RabbitMQ的鏡像隊列作為基礎(chǔ),再增加一些運維手段,進(jìn)一步提高集群的安全性和實用性。
例如,增加keepalived保證每個RabbitMQ的穩(wěn)定性,當(dāng)某一個節(jié)點上的RabbitMQ服務(wù)崩潰時,可以及時重新啟動起來。另外,也可以增加HA-proxy來做前端的負(fù)載均衡,通過HA-proxy增加一個前端轉(zhuǎn)發(fā)的虛擬節(jié)點,應(yīng)用可以像使用一個單點服務(wù)一樣使用一個RabbitMQ集群。這些運維方案我們就不做過多介紹了,有興趣可以自己了解下。
四、RabbitMQ基礎(chǔ)使用
RabbitMQ搭建完成后,可以在Web控制臺上選擇Exchange或者Queue來發(fā)送消息了,我們可以簡單體驗下,也可以留到下一部分編程模型時再深入體驗。
例如,先在Admin菜單,配置admin用戶可以操作/mirror虛擬機(jī)。

然后,創(chuàng)建一個經(jīng)典隊列。

創(chuàng)建完成后,選擇這個test1隊列,就可以在頁面上直接發(fā)送消息以及消費消息了。

在整體使用過程中你會發(fā)現(xiàn),對于隊列,有Classic、Quorum、Stream三種類型,其中,Classic和Quorum兩種類型,使用上幾乎是沒有什么區(qū)別的。但是Stream隊列就無法直接消費消息了。這種區(qū)別也會帶到后面的使用過程中。
然后,RabbitMQ的各種管理功能,整理上還是非常簡單的,幾乎所有的配置都可以在Web管理頁面上直觀的看到,并直接完成操作。同時,這些管理功能,也都可以通過后端的命令行工具進(jìn)行。在進(jìn)行體驗的過程中,也可以自行嘗試了解后端配置的各種指令。每個指令都有help幫助文檔,大家可以自行嘗試了解。