1、引言
消息是互聯網信息的一種表現形式,是人利用計算機進行信息傳遞的有效載體,比如即時通訊網壇友最熟悉的即時通訊消息就是其具體的表現形式之一。
消息從發送者到接收者的典型傳遞方式有兩種:
1)一種我們可以稱為即時消息:即消息從一端發出后(消息發送者)立即就可以達到另一端(消息接收者),這種方式的具體實現就是平時最常見的IM聊天消息;
2)另一種稱為延遲消息:即消息從某端發出后,首先進入一個容器進行臨時存儲,當達到某種條件后,再由這個容器發送給另一端。
在上述“消息傳遞方式2)”中所指的這個容器的一種具體實現就是MQ消息隊列服務。
MQ消息隊列中間件是中大型分布式系統中重要的組件,它主要用來解決:應用解耦、異步消息、流量削鋒等問題,用以實現高性能、高可用、可伸縮和最終一致性架構。目前使用較多的消息隊列有ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ等。MQ消息隊列中間件已被廣泛用于電商、即時通訊、社交等各種中大型分布式應用系統。
在一個典型的IM即時通訊應用中,MQ消息隊列可以用于:
1)用戶的聊天消息離線存儲環節:因為IM消息的發送屬于高吞吐場景,直接操縱DB很容易就把DB搞掛了,所以離線消息在落地入庫前,可以先扔到MQ消息隊列中,再由單獨部署的消費者來有節奏地存儲到DB中;
2)用戶的行為數據收集環節:因為用戶的聊天消息和指令等,可以用于大數據分析,而且基于國家監管要求也是必須要存儲一段時間的,所以此類數據的收集同樣可以用于MQ消息隊列,再由單獨部署的消費者存儲到DB中;
3)用戶的操作日志收集環節:log這種數據價值不高,但關鍵時刻又非常有用,而且數據量又很大,要想存儲起來難度很高,這時就輪到Linkedin公司開源的Kafka出場了;
....
因此,對于即時通訊開發者來說,正確地理解MQ消息隊列,對于IM或消息推送系統的架構設計、方案選型等都大有裨益。
▲ 一個典型的消息隊列原理圖(生產者將消息通過隊列傳遞給消費者)
學習交流:
- 即時通訊開發交流3群:185926912 [推薦]
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
(本文同步發布于:http://www.52im.net/thread-1979-1-1.html)
2、系列文章
▼ IM開發干貨系列文章(本文是其第16篇):
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《如何保證IM實時消息的“時序性”與“一致性”?》
《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)》
《移動端IM登錄時拉取數據如何作到省流量?》
《通俗易懂:基于集群的移動端IM接入層負載均衡方案分享》
《淺談移動端IM的多點登陸和消息漫游原理》
《IM開發基礎知識補課(一):正確理解前置HTTP SSO單點登陸接口的原理》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
《IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《IM群聊消息的已讀回執功能該怎么實現?》
《IM群聊消息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》(本文)
如果您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。
3、MQ消息隊列的典型應用場景
MQ消息隊列目前在中大型分布式系統實際應用中常用的使用場景主要有:異步處理、應用解耦、流量削鋒和消息通訊四個場景。
3.1 應用場景1:異步處理
場景說明:一個典型的IM即時通訊系統中,用戶注冊成功后可能需要發送注冊郵件和注冊通知短信。
傳統的做法有兩種:
1)串行的方式:即將注冊信息寫入數據庫成功后、發送注冊郵件、再發送注冊短信。以上三個任務全部完成后,返回給客戶端;
2)并行方式:即將注冊信息寫入數據庫成功后,發送注冊郵件的同時,發送注冊短信。以上三個任務完成后,返回給客戶端。與串行的差別是,并行的方式可以提高處理的時間。
假設三個業務節點每個使用50毫秒,不考慮網絡等其他開銷,則串行方式的時間是150毫秒,并行的時間可能是100毫秒。
因為CPU在單位時間內處理的請求數是一定的,假設CPU1秒內吞吐量是100次。則串行方式1秒內CPU可處理的請求量是7次(1000/150)。并行方式處理的請求量是10次(1000/100)。
小結:如以上案例描述,傳統的方式系統的性能(并發量,吞吐量,響應時間)會有瓶頸。
如何解決這個問題呢?答案是:引入消息隊列,將不是必須的業務邏輯,異步處理。
改造后的架構如下:
按照以上約定,用戶的響應時間相當于是注冊信息寫入數據庫的時間,也就是50毫秒。注冊郵件,發送短信寫入消息隊列后,直接返回,因此寫入消息隊列的速度很快,基本可以忽略,因此用戶的響應時間可能是50毫秒。因此架構改變后,系統的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了兩倍。
3.2 應用場景2:應用解耦
場景說明:一個典型的電商購物系統中,用戶下訂單后,訂單系統需要通知庫存系統。
傳統的做法是:訂單系統調用庫存系統的接口。如下圖所示:
傳統模式的缺點:假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗,訂單系統與庫存系統耦合。
如何解決以上問題呢?答案是:引入應用消息隊列后的方案。如下圖:
如上圖所示,大致的原理是:
訂單系統:用戶下單后,訂單系統完成持久化處理,將消息寫入消息隊列,返回用戶訂單下單成功;
庫存系統:訂閱下單的消息,采用拉/推的方式,獲取下單信息,庫存系統根據下單信息,進行庫存操作。
好處就是:假如在下單時庫存系統不能正常使用,也不影響正常下單,因為下單后,訂單系統寫入消息隊列就不再關心其他的后續操作了。實現訂單系統與庫存系統的應用解耦。
3.3 應用場景3:流量削鋒
流量削鋒也是消息隊列中的常用場景,一般在電商秒殺等大型活動(比如雙11)、團購搶單活動中使用廣泛。
應用場景:秒殺活動,一般會因為流量過大,導致流量暴增,應用掛掉。為解決這個問題,一般需要在應用前端加入消息隊列。
在這種場景下加入消息隊列服務的好處:
1)可以控制活動的人數;
2)可以緩解短時間內高流量壓垮應用。
用戶的請求,服務器接收后,首先寫入消息隊列。假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面。秒殺業務根據消息隊列中的請求信息,再做后續處理。
3.4 應用場景4:日志處理
日志處理是指將消息隊列用在日志處理中,比如Linkedin這種大型職業社交應用架構中Kafka的應用(Kafka就是Linkedin開發并開源的),解決大量日志傳輸的問題。
使用Kafka后的架構簡化如下:
上圖所示的架構原理就是:
日志采集客戶端:負責日志數據采集,定時寫入Kafka隊列;
Kafka消息隊列:負責日志數據的接收,存儲和轉發;
日志處理應用:訂閱并消費kafka隊列中的日志數據。
3.5 應用場景5:即時消息通訊
即時消息通訊是指,消息隊列一般都內置了高效的通信機制,因此也可以用在純的即時消息通訊場景。比如實現點對點消息隊列或者IM聊天室等(但Jack Jiang認為,在中大型IM系統中,MQ并不適合這么用,具體的討論請見:《請教可以使用MQ消息隊列中間件做即時通訊系統嗎?》)。
點對點通訊:客戶端A和客戶端B使用同一隊列,進行消息通訊;
聊天室通訊:客戶端A,客戶端B,客戶端N訂閱同一主題,進行消息發布和接收。實現類似聊天室效果。
以上實際是消息隊列的兩種消息模式,點對點或發布訂閱模式。模型為示意圖,供參考。
4、MQ消息隊列的常見消息模式
常見的MQ消息隊列消息模式有:
1)P2P模式;
2)Pub/sub模式(也就是常說的“發布/訂閱”模式);
3)推(Push)模式和拉(Pull)模式。
下面將逐個介紹這幾種常消息模式。
4.1 P2P模式
P2P模式包含三個角色:
1)消息隊列(Queue);
2)發送者(Sender);
3)接收者(Receiver)。
每個消息都被發送到一個特定的隊列,接收者從隊列中獲取消息。隊列保留著消息,直到他們被消費或超時。
P2P消息模式的特點:
每個消息只有一個消費者(Consumer)(即一旦被消費,消息就不再在消息隊列中)發送者和接收者之間在時間上沒有依賴性,也就是說當發送者發送了消息之后,不管接收者有沒有正在運行,它不會影響到消息被發送到隊列接收者在成功接收消息之后需向隊列應答成功 如果希望發送的每個消息都會被成功處理的話,那么需要P2P模式。
4.2 Pub/sub模式
如上圖所示,此消息模式包含三個角色:
1)主題(Topic);
2)發布者(Publisher);
3)訂閱者(Subscriber)。
多個發布者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者。
Pub/Sub的特點:
每個消息可以有多個消費者發布者和訂閱者之間有時間上的依賴性。針對某個主題(Topic)的訂閱者,它必須創建一個訂閱者之后,才能消費發布者的消息。為了消費消息,訂閱者必須保持運行的狀態。為了緩和這樣嚴格的時間相關性,有些MQ消息隊列(比如RabbitMQ)允許訂閱者創建一個可持久化的訂閱。這樣,即使訂閱者沒有被激活(運行),它也能接收到發布者的消息。如果希望發送的消息可以不被做任何處理、或者只被一個消息者處理、或者可以被多個消費者處理的話,那么可以采用Pub/Sub模型。
4.3 推模式和拉模式
推(push)模式是一種基于C/S機制、由服務器主動將信息送到客戶器的技術。
在Push模式應用中,服務器把信息送給客戶器之前,并沒有明顯的客戶請求。push事務由服務器發起。push模式可以讓信息主動、快速地尋找用戶/客戶器,信息的主動性和實時性比較好。但精確性較差,可能推送的信息并不一定滿足客戶的需求。
Push模式不能保證能把信息送到客戶器,因為推模式采用了廣播機制,如果客戶器正好聯網并且和服務器在同一個頻道上,推送模式才是有效的。
Push模式無法跟蹤狀態,采用了開環控制模式,沒有用戶反饋信息。在實際應用中,由客戶器向服務器發送一個申請,并把自己的地址(如IP、port)告知服務器,然后服務器就源源不斷地把信息推送到指定地址。在多媒體信息廣播中也采用了推模式。
拉(Pull)模式與推(Push)模式相反,是由客戶器主動發起的事務。服務器把自己所擁有的信息放在指定地址(如IP、port),客戶器向指定地址發送請求,把自己需要的資源“拉”回來。不僅可以準確獲取自己需要的資源,還可以及時把客戶端的狀態反饋給服務器。
5、主流的MQ消息隊列技術選型對比
一份主流MQ技術對比清單:
另外,即時通訊網整理的另一篇《IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?》,可以詳細了解一下Kafka和RabbitMQ的對比。
5.1 Kafka
Kafka是Linkedin開源的MQ系統(現已是Apache下的一個子項目),它是一個高性能跨語言分布式發布/訂閱消息隊列系統,主要特點是基于Pull的模式來處理消息消費,追求高吞吐量,一開始的目的就是用于日志收集和傳輸,0.8開始支持復制,不支持事務,適合產生大量數據的互聯網服務的數據收集業務。
Kafka還具有以下特性:
1)快速持久化,可以在O(1)的系統開銷下進行消息持久化;
2)高吞吐,在一臺普通的服務器上既可以達到10W/s的吞吐速率;
3)完全的分布式系統,Broker、Producer、Consumer都原生自動支持分布式,自動實現負載均衡;
4)支持Hadoop數據并行加載,對于像Hadoop的一樣的日志數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。Kafka通過Hadoop的并行加載機制統一了在線和離線的消息處理。
Apache Kafka相對于ActiveMQ是一個非常輕量級的消息系統,除了性能非常好之外,還是一個工作良好的分布式系統。
5.2 RabbitMQ
RabbitMQ是使用Erlang語言開發的開源消息隊列系統,基于AMQP協議來實現。AMQP的主要特征是面向消息、隊列、路由(包括點對點和發布/訂閱)、可靠性、安全。AMQP協議更多用在企業系統內,對數據一致性、穩定性和可靠性要求很高的場景,對性能和吞吐量的要求還在其次。
RabbitMQ本身支持很多的協議:AMQP,XMPP, SMTP, STOMP,也正因如此,它非常重量級,更適合于企業級的開發。同時實現了Broker構架,這意味著消息在發送給客戶端時先在中心隊列排隊。對路由,負載均衡或者數據持久化都有很好的支持。
5.3 RocketMQ
RocketMQ是阿里開源的消息中間件,它是純Java開發,具有高吞吐量、高可用性、適合大規模分布式系統應用的特點。RocketMQ思路起源于Kafka,但并不是Kafka的一個Copy,它對消息的可靠傳輸及事務性做了優化,目前在阿里集團被廣泛應用于交易、充值、流計算、消息推送、日志流式處理、binglog分發等場景。
5.4 ZeroMQ
ZeroMQ只是一個網絡編程的Pattern庫,將常見的網絡請求形式(分組管理,鏈接管理,發布訂閱等)模式化、組件化,簡而言之socket之上、MQ之下。對于MQ來說,網絡傳輸只是它的一部分,更多需要處理的是消息存儲、路由、Broker服務發現和查找、事務、消費模式(ack、重投等)、集群服務等。
ZeroMQ是號稱最快的消息隊列系統,尤其針對大吞吐量的需求場景。ZeroMQ能夠實現RabbitMQ不擅長的高級/復雜的隊列,但是開發人員需要自己組合多種技術框架,技術上的復雜度是對這MQ能夠應用成功的挑戰。ZeroMQ具有一個獨特的非中間件的模式,你不需要安裝和運行一個消息服務器或中間件,因為你的應用程序將扮演這個服務器角色。你只需要簡單的引用ZeroMQ程序庫,可以使用NuGet安裝,然后你就可以愉快的在應用程序之間發送消息了。但是ZeroMQ僅提供非持久性的隊列,也就是說如果宕機,數據將會丟失。其中,Twitter的Storm 0.9.0以前的版本中默認使用ZeroMQ作為數據流的傳輸(Storm從0.9版本開始同時支持ZeroMQ和Netty作為傳輸模塊)。
5.5 小結
RabbitMQ/Kafka/ZeroMQ 都能提供消息隊列服務,但有很大的區別。
在面向服務架構中通過消息代理(比如 RabbitMQ / Kafka等),使用生產者-消費者模式在服務間進行異步通信是一種比較好的思想。
因為服務間依賴由強耦合變成了松耦合。消息代理都會提供持久化機制,在消費者負載高或者掉線的情況下會把消息保存起來,不會丟失。就是說生產者和消費者不需要同時在線,這是傳統的請求-應答模式比較難做到的,需要一個中間件來專門做這件事。其次消息代理可以根據消息本身做簡單的路由策略,消費者可以根據這個來做負載均衡,業務分離等。
缺點也有,就是需要額外搭建消息代理集群(但優點是大于缺點的 ) 。
ZeroMQ 和 RabbitMQ/Kafka 不同,它只是一個異步消息庫,在套接字的基礎上提供了類似于消息代理的機制。使用 ZeroMQ 的話,需要對自己的業務代碼進行改造,不利于服務解耦。
RabbitMQ 支持 AMQP(二進制),STOMP(文本),MQTT(二進制),HTTP(里面包裝其他協議)等協議。Kafka 使用自己的協議。
Kafka 自身服務和消費者都需要依賴 Zookeeper。
RabbitMQ 在有大量消息堆積的情況下性能會下降,Kafka不會。畢竟AMQP設計的初衷不是用來持久化海量消息的,而Kafka一開始是用來處理海量日志的。
總的來說,RabbitMQ 和 Kafka 都是十分優秀的分布式的消息代理服務,只要合理部署,基本上可以滿足生產條件下的任何需求。
關于這兩種MQ的比較,網上的資料并不多,最權威的的是kafka的提交者寫一篇文章:http://www.quora.com/What-are-the-differences-between-Apache-Kafka-and-RabbitMQ
這篇文間里面提到的要點:
1) RabbitMq比kafka成熟,在可用性上,穩定性上,可靠性上,RabbitMq超過kafka;
2) Kafka設計的初衷就是處理日志的,可以看做是一個日志系統,針對性很強,所以它并沒有具備一個成熟MQ應該具備的特性;
3) Kafka的性能(吞吐量、tps)比RabbitMq要強,這篇文章的作者認為,兩者在這方面沒有可比性;
4)總的來說,目前RocketMq、Kafka、RabbitMq在各家公司都有使用,具體看技術團隊的熟悉程度及使用場景了。
附錄1:有關IM即時通訊架構設計的文章
《淺談IM系統的架構設計》
《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
《一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》
《一套原創分布式即時通訊(IM)系統理論架構方案》
《從零到卓越:京東客服即時通訊系統的技術架構演進歷程》
《蘑菇街即時通訊/IM服務器開發之架構選擇》
《騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT》
《微信后臺基于時間序的海量數據冷熱分級架構設計實踐》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《17年的實踐:騰訊海量產品的技術方法論》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《現代IM系統中聊天消息的同步和存儲方案探討》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
《IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《WhatsApp技術實踐分享:32人工程團隊創造的技術神話》
《微信朋友圈千億訪問量背后的技術挑戰和實踐總結》
《王者榮耀2億用戶量的背后:產品定位、技術架構、網絡方案等》
《IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?》
《騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面》
《以微博類應用場景為例,總結海量社交系統的架構設計步驟》
《快速理解高性能HTTP服務端的負載均衡技術原理》
《子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐》
《知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
>> 更多同類文章 ……
附錄2:有關IM即時通訊的更多熱點問題的文章
《移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”》
《移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結》
《從客戶端的角度來談談移動端IM的消息可靠性和送達機制》
《現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障》
《騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路》
《小白必讀:閑話HTTP短連接中的Session和Token》
《IM開發基礎知識補課:正確理解前置HTTP SSO單點登陸接口的原理》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《移動端IM開發需要面對的技術問題》
《開發IM是自己設計協議用字節流好還是字符流好?》
《請問有人知道語音留言聊天的主流實現方式嗎?》
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《如何保證IM實時消息的“時序性”與“一致性”?》
《一個低成本確保IM消息時序的方法探討》
《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《談談移動端 IM 開發中登錄請求的優化》
《移動端IM登錄時拉取數據如何作到省流量?》
《淺談移動端IM的多點登陸和消息漫游原理》
《完全自已開發的IM該如何設計“失敗重試”機制?》
《通俗易懂:基于集群的移動端IM接入層負載均衡方案分享》
《微信對網絡影響的技術試驗及分析(論文全文)》
《即時通訊系統的原理、技術和應用(技術論文)》
《開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
《基于社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(圖片壓縮篇)》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(音視頻技術篇)》
《為什么說即時通訊社交APP創業就是一個坑?》
《字符編碼那點事:快速理解ASCII、Unicode、GBK和UTF-8》
《全面掌握移動端主流圖片格式的特點、性能、調優等》
《最火移動端跨平臺方案盤點:React Native、weex、Flutter》
《子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
>> 更多同類文章 ……
(本文同步發布于:http://www.52im.net/thread-1979-1-1.html)