本文做者:黃小斜php
轉載請務必在文章開頭註明出處和做者。java
「RabbitMQ?」「Kafka?」「RocketMQ?」...在平常學習與開發過程當中,咱們經常聽到消息隊列這個關鍵詞,可能你是熟練使用消息隊列的老手,又或者你是不懂消息隊列的新手,不論你了不瞭解消息隊列,本文都將帶你搞懂消息隊列的一些基本理論。若是你是老手,你可能從本文學到你以前未曾注意的一些關於消息隊列的重要概念,若是你是新手,相信本文將是你打開消息隊列大門的一板磚。python
根據百度百科的說法,「消息隊列」是在消息的傳輸過程當中保存消息的容器。消息隊列管理器在將消息從它的源中繼到它的目標時充當中間人。隊列的主要目的是提供路由並保證消息的傳遞;若是發送消息時接收者不可用,消息隊列會保留消息,直到能夠成功地傳遞它。`c++
我以爲使用消息隊列主要有兩點好處:git
1.經過異步處理提升系統性能(削峯、減小響應所需時間);github
2.下降系統耦合性。若是在面試的時候你被面試官問到這個問題的話,通常狀況是你在你的簡歷上涉及到消息隊列這方面的內容,這個時候推薦你結合你本身的項目來回答。web
《大型網站技術架構》第四章和第七章均有提到消息隊列對應用性能及擴展性的提高。面試
在我平時的平常工做中,用到消息隊列的場景可很多,好比,我有一個定時任務須要在A應用天天7點開始調度,那麼定時任務系統如何告訴這個A應用呢,一種辦法是直接調用A應用的RPC服務,可是,定時任務系統不可能去記錄那麼多應用的RPC服務,因此若是換成消息,就大大下降了複雜度。數據庫
還有一種常見使用消息隊列的場景,那就是把一些不須要及時處理的RPC調用改爲消息,好比最典型的電商下單,必定是實時性要求很高的,可是,有一些消息會在用戶下單後進行異步的發送,好比用戶對商品的評價,用戶的退款請求,這些請求不須要被實時地進行處理,徹底能夠異步化處理,這個時候使用消息隊列就是再好不過的選擇了,消息隊列會幫你存儲這些待處理的消息,而且等應用負載較低的時候再分發給應用處理,或者是等待應用主動向消息隊列獲取消息。apache
咱們能夠把消息隊列比做是一個存放消息的容器,當咱們須要使用消息的時候能夠取出消息供本身使用。消息隊列是分佈式系統中重要的組件,使用消息隊列主要是爲了經過異步處理提升系統性能和削峯、下降系統耦合性。目前使用較多的消息隊列有ActiveMQ,RabbitMQ,Kafka,RocketMQ。
固然,在咱們公司內部用的大多都是自研的消息隊列產品,一方面是由於須要適配金融級分佈式場景,另外一方面自研的中間件有專門的的團隊維護,出了什麼問題才能及時處理和修復。
下面咱們就一塊兒來看看這些開源的消息隊列是怎麼設計的,各有什麼優缺點呢。
RabbitMQ 2007年發佈,是一個在AMQP(高級消息隊列協議)基礎上完成的,可複用的企業消息系統,是當前最主流的消息中間件之一。
主要特性:
優勢:
缺點:
ActiveMQ是由Apache出品,ActiveMQ 是一個徹底支持JMS1.1和J2EE 1.4規範的 JMS Provider實現。它很是快速,支持多種語言的客戶端和協議,並且能夠很是容易的嵌入到企業的應用環境中,並有許多高級功能。
主要特性:
優勢:
缺點:
RocketMQ出自 阿里公司的開源產品,用 Java 語言實現,在設計時參考了 Kafka,並作出了本身的一些改進,消息可靠性上比 Kafka 更好。RocketMQ在阿里集團被普遍應用在訂單,交易,充值,流計算,消息推送,日誌流式處理,binglog分發等場景。
主要特性:
優勢:
訪問時,直接從內存讀取。
缺點:
支持的客戶端語言很少,目前是java及c++,其中c++不成熟;
RocketMQ社區關注度及成熟度也不及前二者;
沒有web管理界面,提供了一個CLI(命令行界面)管理工具帶來查詢、管理和診斷各類問題;
沒有在 mq 核心中去實現JMS等接口;
Apache Kafka是一個分佈式消息發佈訂閱系統。它最初由LinkedIn公司基於獨特的設計實現爲一個分佈式的提交日誌系統( a distributed commit log),,以後成爲Apache項目的一部分。Kafka系統快速、可擴展而且可持久化。它的分區特性,可複製和可容錯都是其不錯的特性。
主要特性:
優勢:
缺點:
這麼多的消息隊列,有一個區別很重要,那就是模式,究竟是pull好仍是push好,下面就讓咱們來一探究竟吧
Push即服務端主動發送數據給客戶端。在服務端收到消息以後當即推送給客戶端。
Push模型最大的好處就是實時性。由於服務端能夠作到只要有消息就當即推送,因此消息的消費沒有「額外」的延遲。
可是Push模式在消息中間件的場景中會面臨如下一些問題:
Pull模式能夠很好的應對以上的這些場景。
Pull模式由Consumer主動從Broker獲取消息。
這樣帶來了一些好處:
Pull模式還有一個好處是能夠聚合消息。由於Broker沒法預測寫一條消息產生的時間,因此在收到消息以後只能當即推送給Consumer,因此沒法對消息聚合後再推送給Consumer。 而Pull模式由Consumer主動來獲取消息,每一次Pull時都儘量多的獲取已近在Broker上的消息。
可是,和Push模式正好相反,Pull就面臨了實時性的問題。
由於由Consumer主動來Pull消息,因此實時性和Pull的週期相關,這裏就產生了「額外」延遲。若是爲了下降延遲來提高Pull的執行頻率,可能在沒有消息的時候產生大量的Pull請求(消息中間件是徹底解耦的,Broker和Consumer沒法預測下一條消息在何時產生);若是頻率低了,那延遲天然就大了。
另外,Pull模式狀態維護在Consumer,因此多個Consumer之間須要相互協調,這裏就須要引入ZK或者本身實現NameServer之類的服務來完成Consumer之間的協調。
有沒有一種方式,能結合Push和Pull的優點,同時變各自的缺陷呢?答案是確定的。
使用long-polling模式,Consumer主動發起請求到Broker,正常狀況下Broker響應消息給Consumer;在沒有消息或者其餘一些特殊場景下,能夠將請求阻塞在服務端延遲返回。
long-polling不是一種Push模式,而是Pull的一個變種。
那麼:
以上兩點避免了多餘的Pull請求,同時也解決Pull請求的執行頻率致使的「額外」的延遲。
注意上面有一個概念:「超時以前」。每個請求都有超時時間,Pull請求也是。「超時以前」的含義是在Consumer的「Pull」請求超時以前。
基於long-polling的模型,Broker須要保證在請求超時以前返回一個結果給Consumer,不管這個結果是讀取到了消息或者沒有可讀消息。
由於Consumer和Broker之間的時間是有誤差的,且請求從Consumer發送到Broker也是須要時間的,因此若是一個請求的超時時間是5秒,而這個請求在Broker端阻塞了5秒才返回,那麼Consumer在收到Broker響應以前就會斷定請求超時。因此Broker須要保證在Consumer斷定請求超時以前返回一個結果。
一般的作法時在Broker端能夠阻塞請求的時間老是小於long-polling請求的超時時間。好比long-polling請求的超時時間爲30秒,那麼Broker在收到請求後最遲在25s以後必定會返回一個結果。中間5s的差值來應對Broker和Consumer的始終存在誤差和網絡存在延遲的狀況。 (可見Long-Polling模式的前提是Broker和Consumer之間的時間誤差沒有「很大」)
Long-Polling還存在什麼問題嗎,還能改進嗎?
「在Broker一直有可讀消息的狀況下,long-polling就等價於執行間隔爲0的pull模式(每次收到Pull結果就發起下一次Pull請求)。」
這是上面long-polling在服務端一直有可消費消息的處理狀況。在這個狀況下,一條消息若是在long-polling請求返回時到達服務端,那麼它被Consumer消費到的延遲是:
假設Broker和Consumer之間的一次網絡開銷時間爲R毫秒, 那麼這條消息須要經歷3R才能到達Consumer 第一個R:消息已經到達Broker,可是long-polling請求已經讀完數據準備返回Consumer,從Broker到Consumer消耗了R 第二個R:Consumer收到了Broker的響應,發起下一次long-polling,這個請求到達Broker須要一個R 的時間 第三個R:Broker收到請求讀取了這條數據,那麼返回到Consumer須要一個R的時間 因此總共須要3R(不考慮讀取的開銷,只考慮網絡開銷)
另外,在這種狀況下Broker和Consumer之間一直在進行請求和響應(long-polling變成了間隔爲0的pull)。
考慮這樣一種方式,它有long-polling的優點,同時能減小在有消息可讀的狀況下由Broker主動push消息給Consumer,減小沒必要要的請求。
http://www.luyixian.cn/news_s...
https://blog.51cto.com/caczjz...