分布式消息隊(duì)列是是大型分布式系統(tǒng)不可缺少的中間件该押,主要解決應(yīng)用耦合疗杉、異步消息、流量削鋒等問題蚕礼。實(shí)現(xiàn)高性能烟具、高可用、可伸縮和最終一致性架構(gòu)奠蹬。
對(duì)于一個(gè)架構(gòu)師來說净赴,在大型系統(tǒng)設(shè)計(jì)中,會(huì)經(jīng)常需要面對(duì)同步和異步等架構(gòu)問題罩润,搞明白這些問題玖翅,能更好地實(shí)現(xiàn)程序并行執(zhí)行,減少等待或無效操作割以,以及充分利用計(jì)算機(jī)的性能金度!
本文將詳細(xì)講解:
1.同步架構(gòu)和異步架構(gòu)的區(qū)別
2.異步架構(gòu)的主要組成部分:消息生產(chǎn)者、消息消費(fèi)者严沥、分布式消息隊(duì)列
3.異步架構(gòu)的兩種主要模型:點(diǎn)對(duì)點(diǎn)模型和發(fā)布訂閱模型猜极。
4.消息隊(duì)列的好處
5.消息隊(duì)列相關(guān)產(chǎn)品
建議用10min通讀,搞懂分布式消息隊(duì)列的核心內(nèi)容消玄。
一跟伏、同步架構(gòu)和異步架構(gòu)的區(qū)別
1.同步調(diào)用
是指從請(qǐng)求的發(fā)起一直到最終的處理完成期間丢胚,請(qǐng)求的調(diào)用方一直在同步阻塞等待調(diào)用的處理完成。
如圖受扳,在這個(gè)例子中客戶端代碼ClientCode携龟,需要執(zhí)行發(fā)送郵件sendEmail這樣一個(gè)操作,它會(huì)調(diào)用EmailService進(jìn)行發(fā)送勘高,而EmailService會(huì)調(diào)用SmtpEmailAdapter這樣一個(gè)類來進(jìn)行處理峡蟋,而這個(gè)類會(huì)調(diào)用遠(yuǎn)程的一個(gè)服務(wù),通過SMTP和TCP協(xié)議把請(qǐng)求發(fā)送給它华望。
而遠(yuǎn)程服務(wù)器收到消息以后會(huì)對(duì)消息進(jìn)行一系列的操作蕊蝗,然后將郵件發(fā)送出去,再進(jìn)行返回赖舟。Adapter收到返回后蓬戚,再返回給EmailService,EmailService收到返回后再把返回結(jié)果返回給Clientcode宾抓。
ClientCode在sendEmail發(fā)出請(qǐng)求后子漩,就一直都阻塞在這里,等待最終調(diào)用結(jié)果的返回洞慎,是成功還是失敗痛单。因?yàn)檫@個(gè)過程是阻塞等待的,所以這個(gè)過程也就是同步調(diào)用劲腿。
2.異步調(diào)用
是指在請(qǐng)求發(fā)起的處理過程中旭绒,客戶端的代碼已經(jīng)返回了,它可以繼續(xù)進(jìn)行自己的后續(xù)操作焦人,而不需要等待調(diào)用處理完成挥吵,這就叫做異步調(diào)用。
異步調(diào)用過程花椭,同樣看剛剛發(fā)送郵件的例子忽匈,用戶Clientcode調(diào)用EmailService以后,EmailService會(huì)把這個(gè)調(diào)用請(qǐng)求發(fā)送給消息隊(duì)列矿辽,然后就立即返回了丹允。Clientcode收到返回以后繼續(xù)向下處理,不會(huì)繼續(xù)阻塞等待袋倔。實(shí)際上消息發(fā)送到Queue后雕蔽,還沒有被處理,我們看到后面的消息消費(fèi)宾娜,其實(shí)要比EmailService返回可能還要晚一點(diǎn)批狐,EmailService返回以后消息才會(huì)被消費(fèi)處理。
有一個(gè)QueueConsumer消息隊(duì)列的消費(fèi)者前塔,從消息隊(duì)列中取出這個(gè)消息嚣艇,再把這個(gè)消息發(fā)送給SmtpAdapter承冰,也就是調(diào)用SmtpAdapter,處理邏輯跟同步調(diào)用一樣食零,SmtpAdapter通過SMTP的通訊協(xié)議困乒,把消息發(fā)送給遠(yuǎn)程的一個(gè)服務(wù)器,進(jìn)行郵件發(fā)送慌洪,通過RemoteServer進(jìn)行處理顶燕,處理完了收到返回凑保,再把返回結(jié)果通知消息隊(duì)列Queue冈爹。
在這個(gè)過程中,客戶端的調(diào)用欧引,也就是應(yīng)用程序的調(diào)用频伤,和業(yè)務(wù)邏輯真正發(fā)送郵件的操作是不同步的。
二芝此、異步架構(gòu)的主要組成部分
使用異步調(diào)用架構(gòu)的主要手段憋肖,就是通過消息隊(duì)列構(gòu)建,如下是它的架構(gòu)圖婚苹。
消息的生產(chǎn)者將消息發(fā)送到消息隊(duì)列以后岸更,由消息的消費(fèi)者從消息隊(duì)列中獲取消息,然后進(jìn)行業(yè)務(wù)邏輯的處理膊升,消息的生產(chǎn)者和消費(fèi)者是異步處理的怎炊,彼此不會(huì)等待阻塞,所以叫做異步架構(gòu)廓译。
使用消息隊(duì)列構(gòu)建一個(gè)異步調(diào)用架構(gòu)评肆,你需要了解如下3種角色。
1.消息的生產(chǎn)者
是客戶端應(yīng)用程序代碼的一部分非区,用來初始化異步調(diào)用處理流程瓜挽。在基于消息隊(duì)列的處理中,生產(chǎn)者的職責(zé)非常少征绸,它要做的就是創(chuàng)建一個(gè)合法的消息久橙,并把這個(gè)消息發(fā)送到消息隊(duì)列中,由應(yīng)用開發(fā)者決定生產(chǎn)者的代碼在哪里執(zhí)行管怠,什么時(shí)候發(fā)送消息淆衷。
2.消息隊(duì)列
消息隊(duì)列是消息發(fā)送的目的地和發(fā)給消費(fèi)者的一個(gè)緩沖。消息隊(duì)列實(shí)現(xiàn)的方法有好多種排惨,可以用共享文件夾吭敢,也可以用關(guān)系數(shù)據(jù)庫(kù)或者NoSQL系統(tǒng),當(dāng)然最主要的還是使用專門的分布式消息隊(duì)列服務(wù)器來實(shí)現(xiàn)暮芭。
3.消息的消費(fèi)者
消息的消費(fèi)者從消息隊(duì)列中接受并處理消息鹿驼,消息的消費(fèi)者也是由應(yīng)用開發(fā)者實(shí)現(xiàn)的欲低,但是它是一個(gè)異步處理的組件。消息的消費(fèi)者不需要知道生產(chǎn)者存在畜晰,它只依賴消息隊(duì)列中的消息砾莱。消息的消費(fèi)者通常部署在獨(dú)立的服務(wù)器上,和消息的生產(chǎn)者完全隔離凄鼻,并且可以通過添加硬件的方式進(jìn)行伸縮腊瑟。
三、異步架構(gòu)的兩種主要模型
使用消息隊(duì)列構(gòu)建異步的調(diào)用架構(gòu)块蚌,你還需要知道兩種模型:點(diǎn)對(duì)點(diǎn)模型和發(fā)布訂閱模型闰非。
1.點(diǎn)對(duì)點(diǎn)模型
消費(fèi)者和生產(chǎn)者只需要知道消息隊(duì)列的名字,生產(chǎn)者發(fā)送消息到消息隊(duì)列中峭范,而消息隊(duì)列的另一端是多個(gè)消費(fèi)者競(jìng)爭(zhēng)消費(fèi)消息财松,每個(gè)到達(dá)消息隊(duì)列的消息只會(huì)被路由到一個(gè)消費(fèi)者中去,所以消費(fèi)者看到的是全部消息的一個(gè)子集纱控。我們看這張圖辆毡,消息的生產(chǎn)者有多個(gè),消息的消費(fèi)者也有多個(gè)甜害,多個(gè)生產(chǎn)者將消息發(fā)送到消息隊(duì)列中舶掖,而有多個(gè)消費(fèi)者去消息隊(duì)列中對(duì)消息進(jìn)行競(jìng)爭(zhēng)性的消費(fèi)。每個(gè)消息只會(huì)被一個(gè)消費(fèi)者消費(fèi)尔店,每個(gè)消費(fèi)者只會(huì)消費(fèi)消息隊(duì)列中的一部分消息眨攘。
2.發(fā)布訂閱模型
在發(fā)布訂閱模型中,消息可能被發(fā)送到不止一個(gè)消費(fèi)者闹获,生產(chǎn)者發(fā)送消息到一個(gè)主題期犬,而不是隊(duì)列中。消息被發(fā)布到主題后避诽,就會(huì)被克隆給每一個(gè)訂閱它的消費(fèi)者龟虎,每個(gè)消費(fèi)者接收一份消息復(fù)制到自己的私有隊(duì)列。消費(fèi)者可以獨(dú)立于其他消費(fèi)者使用自己訂閱的消息沙庐,消費(fèi)者之間不會(huì)競(jìng)爭(zhēng)消息鲤妥。常用的分布式消息隊(duì)列都支持發(fā)布訂閱模型,也就是說消息的發(fā)布訂閱模型是分布式消息隊(duì)列的一個(gè)功能特性拱雏。
3.兩個(gè)模型的應(yīng)用
點(diǎn)對(duì)點(diǎn)模型:主要用于一些耗時(shí)較長(zhǎng)的棉安、邏輯相對(duì)獨(dú)立的業(yè)務(wù)。
比如說我前面的講到的發(fā)送郵件這樣一個(gè)操作铸抑。因?yàn)榘l(fā)送郵件比較耗時(shí)贡耽,而且應(yīng)用程序其實(shí)也并不太關(guān)心郵件發(fā)送是否成功,發(fā)送郵件的邏輯也相對(duì)比較獨(dú)立,所以它只需要把郵件消息丟到消息隊(duì)列中就可以返回了蒲赂,而消費(fèi)者也不需要關(guān)心是哪個(gè)生產(chǎn)者去發(fā)送的郵件阱冶,它只需要把郵件消息內(nèi)容取出來以后進(jìn)行消費(fèi),通過遠(yuǎn)程服務(wù)器將郵件發(fā)送出去就可以了滥嘴。而且每個(gè)郵件只需要被發(fā)送一次木蹬。所以消息只被一個(gè)消費(fèi)者消費(fèi)就可以了。
發(fā)布訂閱模型:如新用戶注冊(cè)這樣一個(gè)消息若皱,需要使用按主題發(fā)布的方式镊叁。
比如新用戶注冊(cè),一個(gè)新用戶注冊(cè)成功以后走触,需要給用戶發(fā)送一封激活郵件晦譬,發(fā)送一條歡迎短信,還需要將用戶注冊(cè)數(shù)據(jù)寫入數(shù)據(jù)庫(kù)饺汹,甚至需要將新用戶信息發(fā)送給關(guān)聯(lián)企業(yè)的系統(tǒng)蛔添,比如淘寶新用戶信息發(fā)送給支付寶痰催,這樣允許用戶可以一次注冊(cè)就能登錄使用多個(gè)關(guān)聯(lián)產(chǎn)品兜辞。一個(gè)新用戶注冊(cè),會(huì)把注冊(cè)消息發(fā)送給一個(gè)主題夸溶,多種消費(fèi)者可以訂閱這個(gè)主題逸吵。比如發(fā)送郵件的消費(fèi)者、發(fā)送短信的消費(fèi)者缝裁、將注冊(cè)信息寫入數(shù)據(jù)庫(kù)的消費(fèi)者扫皱,跨系統(tǒng)同步消息的消費(fèi)者等。
四捷绑、消息隊(duì)列的好處
1.實(shí)現(xiàn)異步處理韩脑,提升處理性能
對(duì)一些比較耗時(shí)的操作,可以把處理過程通過消息隊(duì)列進(jìn)行異步處理粹污。這樣做可以推遲耗時(shí)操作的處理段多,使耗時(shí)操作異步化,而不必阻塞客戶端的程序壮吩,客戶端的程序在得到處理結(jié)果之前就可以繼續(xù)執(zhí)行进苍,從而提高客戶端程序的處理性能。
2.可以讓系統(tǒng)獲得更好的伸縮性
耗時(shí)的任務(wù)可以通過分布式消息隊(duì)列鸭叙,向多臺(tái)消費(fèi)者服務(wù)器并行發(fā)送消息觉啊,然后在很多臺(tái)消費(fèi)者服務(wù)器上并行處理消息,也就是說可以在多臺(tái)物理服務(wù)器上運(yùn)行消費(fèi)者沈贝。那么當(dāng)負(fù)載上升的時(shí)候杠人,可以很容易地添加更多的機(jī)器成為消費(fèi)者。
如圖中的例子,用戶上傳文件后嗡善,通過發(fā)布消息的方式市俊,通知后端的消費(fèi)者獲取數(shù)據(jù)、讀取文件滤奈,進(jìn)行異步的文件處理操作摆昧。那么當(dāng)前端發(fā)布更多文件的時(shí)候,或者處理邏輯比較復(fù)雜的時(shí)候蜒程,就可以通過添加后端的消費(fèi)者服務(wù)器绅你,提供更強(qiáng)大的處理能力。
3.可以平衡流量峰值昭躺,削峰填谷
使用消息隊(duì)列忌锯,即便是訪問流量持續(xù)的增長(zhǎng),系統(tǒng)依然可以持續(xù)地接收請(qǐng)求领炫。這種情況下偶垮,雖然生產(chǎn)者發(fā)布消息的速度比消費(fèi)者消費(fèi)消息的速度快,但是可以持續(xù)的將消息納入到消息隊(duì)列中帝洪,用消息隊(duì)列作為消息的緩沖似舵,因此短時(shí)間內(nèi),發(fā)布者不會(huì)受到消費(fèi)處理能力的影響葱峡。
從這張圖可以看到砚哗,因?yàn)橄⒌纳a(chǎn)者是直接面向用戶請(qǐng)求的,而用戶的請(qǐng)求訪問壓力是不均衡的砰奕。如淘寶每天的訪問高峰是在上午10點(diǎn)左右蛛芥,而新浪微博則可能在某個(gè)明星半夜發(fā)一條微博后突然出現(xiàn)訪問高峰。
在訪問高峰军援,用戶的并發(fā)訪問數(shù)可能超過了系統(tǒng)的處理能力仅淑,所以在高峰期就可能會(huì)導(dǎo)致系統(tǒng)負(fù)載過大,響應(yīng)速度變慢胸哥,更嚴(yán)重的可能會(huì)導(dǎo)致系統(tǒng)崩潰涯竟。這種情況下,通過消息隊(duì)列將用戶請(qǐng)求的消息納入到消息隊(duì)列中烘嘱,通過消息隊(duì)列緩沖消費(fèi)者處理消息的速度昆禽。
如圖中所示,消息的生產(chǎn)者它有高峰有低谷蝇庭,但是到了消費(fèi)者這里醉鳖,只會(huì)按照自己的最佳處理能力去消費(fèi)消息。高峰期它會(huì)把消息緩沖在消息隊(duì)列中哮内,而在低谷期它也還是使用自己最大的處理能力去獲取消息盗棵,將前面緩沖起來壮韭、來不及及時(shí)處理的消息處理掉。那么纹因,通過這種手段可以實(shí)現(xiàn)系統(tǒng)負(fù)載消峰填谷喷屋,也就是說將訪問的高峰消掉,而將訪問的低谷填平瞭恰,使系統(tǒng)處在一個(gè)最佳的處理狀態(tài)之下屯曹,不會(huì)對(duì)系統(tǒng)的負(fù)載產(chǎn)生太大的沖擊。
4.失敗隔離和自我修復(fù)
因?yàn)榘l(fā)布者不直接依賴消費(fèi)者惊畏,所以分布式消息隊(duì)列可以將消費(fèi)者系統(tǒng)產(chǎn)生的錯(cuò)誤異常與生產(chǎn)者系統(tǒng)隔離開來恶耽,生產(chǎn)者不受消費(fèi)者失敗的影響。 當(dāng)在消息消費(fèi)過程中出現(xiàn)處理邏輯失敗的時(shí)候颜启,這個(gè)錯(cuò)誤只會(huì)影響到消費(fèi)者自身偷俭,而不會(huì)傳遞給消息的生產(chǎn)者,也就是應(yīng)用程序可以按照原來的處理邏輯繼續(xù)執(zhí)行缰盏。
所以涌萤,這也就意味著在任何時(shí)候都可以對(duì)后端的服務(wù)器執(zhí)行維護(hù)和發(fā)布操作】诓拢可以重啟负溪、添加或刪除服務(wù)器,而不影響生產(chǎn)者的可用性暮的,這樣簡(jiǎn)化了部署和服務(wù)器管理的難度笙以。
5.可以使生產(chǎn)者和消費(fèi)者的代碼實(shí)現(xiàn)解耦合
也就是說可以多個(gè)生產(chǎn)者發(fā)布消息,多個(gè)消費(fèi)者處理消息冻辩,共同完成完整的業(yè)務(wù)處理邏輯,但是它們的不需要直接的交互調(diào)用拆祈,沒有代碼的依賴耦合恨闪。在傳統(tǒng)的同步調(diào)用中,調(diào)用者代碼必須要依賴被調(diào)用者的代碼放坏,也就是生產(chǎn)者代碼必須要依賴消費(fèi)者的處理邏輯代碼咙咽,代碼需要直接的耦合,而使用消息隊(duì)列淤年,這兩部分的代碼不需要進(jìn)行任何的耦合钧敞。
耦合程度越低的代碼越容易維護(hù),也越容易進(jìn)行擴(kuò)展麸粮。
比如新用戶注冊(cè)溉苛,如果用傳統(tǒng)同步調(diào)用的方式,那么發(fā)郵件弄诲、發(fā)短信愚战、寫數(shù)據(jù)庫(kù)、通知關(guān)聯(lián)系統(tǒng)這些代碼會(huì)和用戶注冊(cè)代碼直接耦合起來,整個(gè)代碼看起來就是完成用戶注冊(cè)邏輯后寂玲,后面必然跟著發(fā)郵件塔插、發(fā)短信這些代碼。如果要新增一個(gè)功能拓哟,比如將監(jiān)控用戶注冊(cè)情況想许,將注冊(cè)信息發(fā)送到業(yè)務(wù)監(jiān)控系統(tǒng),就必須要修改前面的代碼断序,至少增加一行代碼伸刃,發(fā)送注冊(cè)信息到監(jiān)控系統(tǒng),我們知道逢倍,任何代碼的修改都可能會(huì)引起bug捧颅。
而使用分布式消息隊(duì)列實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者解耦合以后,用戶注冊(cè)以后较雕,不需要調(diào)用任何后續(xù)處理代碼碉哑,只需要將注冊(cè)消息發(fā)送到分布式消息隊(duì)列就可以了。如果要增加新功能亮蒋,只需要寫個(gè)新功能的消費(fèi)者程序扣典,在分布式消息隊(duì)列中,訂閱用戶注冊(cè)主題就可以了慎玖,不需要修改原來任何一行代碼贮尖。
這種解耦的特點(diǎn)對(duì)于團(tuán)隊(duì)的工作分工也很有幫助!從消息生產(chǎn)者的視角看趁怔,它只需要構(gòu)建消息湿硝,將消息放入消息隊(duì)列中,開發(fā)就完成了润努。而從消費(fèi)者的開發(fā)視角看关斜,它只需要從消息隊(duì)列中獲取消息,然后進(jìn)行邏輯處理铺浇。它們彼此之間不進(jìn)行任何耦合痢畜。消息的生產(chǎn)者不關(guān)心放入消息隊(duì)列中下一步會(huì)發(fā)生什么,而消費(fèi)者也不需要知道消息從哪里來鳍侣。這兩部分程序的開發(fā)者也可以不關(guān)心彼此的工作進(jìn)展丁稀,他們開發(fā)的代碼也不需要集成在一起,只要約定好消息格式倚聚,就可以各自開發(fā)了线衫。
常用的消息隊(duì)列產(chǎn)品
目前業(yè)界常用的消息隊(duì)列產(chǎn)品,有這么幾種:RabbitMQ 秉沼、ActiveMQ桶雀、RocketMQ 矿酵、Kafka
RabbitMQ 的主要特點(diǎn)是性能好,社區(qū)活躍矗积,但是RabbitMQ用Erlang開發(fā)全肮,我們的應(yīng)用很少用Erlang,所以不便于二次開發(fā)和維護(hù)棘捣。
ActiveMQ 影響比較廣泛辜腺,可以跨平臺(tái),使用Java開發(fā)乍恐,對(duì)Java比較友好评疗。
RocketMQ 是阿里推出的一個(gè)開源產(chǎn)品,也是使用Java開發(fā)茵烈,性能比較好百匆,可靠性也比較高。
Kafka 是LinkedIn出品的呜投,專門針對(duì)分布式場(chǎng)景進(jìn)行了優(yōu)化加匈,因此分布式的伸縮性會(huì)比較好。
目前看來仑荐,Kafka因?yàn)樽畛踉O(shè)計(jì)時(shí)就是針對(duì)互聯(lián)網(wǎng)的分布式雕拼、高可用應(yīng)用場(chǎng)景而設(shè)計(jì),并且在大數(shù)據(jù)領(lǐng)域得到廣泛支持粘招,資料文檔更加完善啥寇,因此在互聯(lián)網(wǎng)企業(yè)得到更多的應(yīng)用。
這里跟大家分享一個(gè)進(jìn)行技術(shù)產(chǎn)品選型的小技巧洒扎,供你在進(jìn)行技術(shù)決策的時(shí)候參考辑甜。當(dāng)在幾個(gè)相似的技術(shù)產(chǎn)品中進(jìn)行選型決策,如果拿不定主意逊笆,感覺都差不多的時(shí)候栈戳,一個(gè)辦法就是利用搜索引擎搜索一下這些產(chǎn)品的名字,搜索結(jié)果最多的產(chǎn)品难裆,代表了這個(gè)產(chǎn)品是最熱門,文檔資料最多的镊掖、遇到問題有更大概率可以通過搜索引擎找到答案的乃戈,最有發(fā)展前景、不會(huì)半途而廢沒人維護(hù)的亩进。
利用這個(gè)技巧症虑,我們看一下消息隊(duì)列MQ的產(chǎn)品選型,Kafka在百度中的搜索結(jié)果數(shù)量是其他三個(gè)MQ產(chǎn)品的搜索結(jié)果數(shù)量之和归薛,那么如果你拿不定主意谍憔,選擇Kafka至少不會(huì)是一個(gè)糟糕的選擇匪蝙。
以上內(nèi)容摘取自拉勾
《阿里前輩的架構(gòu)經(jīng)》 第03講(分布式消息隊(duì)列)?點(diǎn)擊查看更多
主講人:李智慧,前阿里巴巴技術(shù)專家
添加拉勾職場(chǎng)導(dǎo)師微信 lagouandy 习贫,可領(lǐng)取技術(shù)相關(guān)100+電子書