IM開發基礎知識補課(五):通俗易懂,正確理解並用好MQ訊息佇列
訊息是網際網路資訊的一種表現形式,是人利用計算機進行資訊傳遞的有效載體,比如即時通訊網壇友最熟悉的即時通訊訊息就是其具體的表現形式之一。
訊息從傳送者到接收者的典型傳遞方式有兩種:
1)一種我們可以稱為即時訊息:即訊息從一端發出後(訊息傳送者)立即就可以達到另一端(訊息接收者),這種方式的具體實現就是平時最常見的IM聊天訊息;
2)另一種稱為延遲訊息:即訊息從某端發出後,首先進入一個容器進行臨時儲存,當達到某種條件後,再由這個容器傳送給另一端。
在上述“訊息傳遞方式2)”中所指的這個容器的一種具體實現就是MQ訊息佇列服務。
MQ訊息佇列中介軟體是中大型分散式系統中重要的元件,它主要用來解決:應用解耦、非同步訊息、流量削鋒等問題,用以實現高效能、高可用、可伸縮和最終一致性架構。目前使用較多的訊息佇列有ActiveMQ、RabbitMQ、ZeroMQ、Kafka、MetaMQ、RocketMQ等。MQ訊息佇列中介軟體已被廣泛用於電商、即時通訊、社交等各種中大型分散式應用系統。
▲ 各種MQ訊息佇列,玲琅滿目
在一個典型的IM即時通訊應用中,MQ訊息佇列可以用於:
1)使用者的聊天訊息離線儲存環節:因為IM訊息的傳送屬於高吞吐場景,直接操縱DB很容易就把DB搞掛了,所以離線訊息在落地入庫前,可以先扔到MQ訊息佇列中,再由單獨部署的消費者來有節奏地儲存到DB中;
2)使用者的行為資料收集環節:因為使用者的聊天訊息和指令等,可以用於大資料分析,而且基於國家監管要求也是必須要儲存一段時間的,所以此類資料的收集同樣可以用於MQ訊息佇列,再由單獨部署的消費者儲存到DB中;
3)使用者的操作日誌收集環節:log這種資料價值不高,但關鍵時刻又非常有用,而且資料量又很大,要想儲存起來難度很高,這時就輪到Linkedin公司開源的Kafka出場了;
....
因此,對於即時通訊開發者來說,正確地理解MQ訊息佇列,對於IM或訊息推送系統的架構設計、方案選型等都大有裨益。
▲ 一個典型的訊息佇列原理圖(生產者將訊息通過佇列傳遞給消費者)
學習交流:
- 即時通訊開發交流3群: ofollow,noindex">185926912 [推薦]
- 移動端IM開發入門文章:《 新手入門一篇就夠:從零開發移動端IM 》
(本文同步釋出於: http://www.52im.net/thread-1979-1-1.html )
2、系列文章
▼ IM開發乾貨系列文章(本文是其第16篇):
《 IM訊息送達保證機制實現(一):保證線上實時訊息的可靠投遞 》
《 IM訊息送達保證機制實現(二):保證離線訊息的可靠投遞 》
《 IM單聊和群聊中的線上狀態同步應該用“推”還是“拉”? 》
《 一種Android端IM智慧心跳演算法的設計與實現探討(含樣例程式碼) 》
《 通俗易懂:基於叢集的移動端IM接入層負載均衡方案分享 》
《 IM開發基礎知識補課(一):正確理解前置HTTP SSO單點登陸介面的原理 》
《 IM開發基礎知識補課(二):如何設計大量圖片檔案的服務端儲存架構? 》
《 IM開發基礎知識補課(三):快速理解服務端資料庫讀寫分離原理及實踐建議 》
《 IM開發基礎知識補課(四):正確理解HTTP短連線中的Cookie、Session和Token 》
《 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訊息模式原理圖
P2P模式包含三個角色:
1)訊息佇列(Queue);
2)傳送者(Sender);
3)接收者(Receiver)。
每個訊息都被髮送到一個特定的佇列,接收者從佇列中獲取訊息。佇列保留著訊息,直到他們被消費或超時。
P2P訊息模式的特點:
每個訊息只有一個消費者(Consumer)(即一旦被消費,訊息就不再在訊息佇列中)傳送者和接收者之間在時間上沒有依賴性,也就是說當傳送者傳送了訊息之後,不管接收者有沒有正在執行,它不會影響到訊息被髮送到佇列接收者在成功接收訊息之後需向佇列應答成功 如果希望傳送的每個訊息都會被成功處理的話,那麼需要P2P模式。
4.2 Pub/sub模式
▲ 典型的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架構設計實踐分享(含詳細圖文) 》
《 騰訊QQ1.4億線上使用者的技術挑戰和架構演進之路PPT 》
《 微信後臺基於時間序的海量資料冷熱分級架構設計實踐 》
《 微信技術總監談架構:微信之道——大道至簡(演講全文) 》
《 如何解讀《微信技術總監談架構:微信之道——大道至簡》 》
《 快速裂變:見證微信強大後臺架構從0到1的演進歷程(一) 》
《 移動端IM中大規模群訊息的推送如何保證效率、實時性? 》
《 IM開發基礎知識補課(二):如何設計大量圖片檔案的服務端儲存架構? 》
《 IM開發基礎知識補課(三):快速理解服務端資料庫讀寫分離原理及實踐建議 》
《 IM開發基礎知識補課(四):正確理解HTTP短連線中的Cookie、Session和Token 》
《 WhatsApp技術實踐分享:32人工程團隊創造的技術神話 》
《 王者榮耀2億使用者量的背後:產品定位、技術架構、網路方案等 》
《 IM系統的MQ訊息中介軟體選型:Kafka還是RabbitMQ? 》
《 騰訊資深架構師乾貨總結:一文讀懂大型分散式系統設計的方方面面 》
《 以微博類應用場景為例,總結海量社交系統的架構設計步驟 》
《 子彈簡訊光鮮的背後:網易雲信首席架構師分享億級IM平臺的技術實踐 》
《 知乎技術分享:從單機到2000萬QPS併發的Redis高效能快取實踐之路 》
《 IM開發基礎知識補課(五):通俗易懂,正確理解並用好MQ訊息佇列 》
>> 更多同類文章 ……
附錄2:有關IM即時通訊的更多熱點問題的文章
《 移動端IM開發者必讀(一):通俗易懂,理解行動網路的“弱”和“慢” 》
《 移動端IM開發者必讀(二):史上最全移動弱網路優化方法總結 》
《 從客戶端的角度來談談移動端IM的訊息可靠性和送達機制 》
《 現代移動端網路短連線的優化手段總結:請求速度、弱網適應、安全保障 》
《 小白必讀:閒話HTTP短連線中的Session和Token 》
《 IM開發基礎知識補課:正確理解前置HTTP SSO單點登陸介面的原理 》
《 移動端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 )