五分鐘學後端技術:如何學習Java工程師必知必會的消息隊列

原創聲明

本文做者:黃小斜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

RabbitMQ 2007年發佈,是一個在AMQP(高級消息隊列協議)基礎上完成的,可複用的企業消息系統,是當前最主流的消息中間件之一。

主要特性:

  1. 可靠性: 提供了多種技術可讓你在性能和可靠性之間進行權衡。這些技術包括持久性機制、投遞確認、發佈者證明和高可用性機制;
  2. 靈活的路由: 消息在到達隊列前是經過交換機進行路由的。RabbitMQ爲典型的路由邏輯提供了多種內置交換機類型。若是你有更復雜的路由需求,能夠將這些交換機組合起來使用,你甚至能夠實現本身的交換機類型,而且當作RabbitMQ的插件來使用;
  3. 消息集羣:在相同局域網中的多個RabbitMQ服務器能夠聚合在一塊兒,做爲一個獨立的邏輯代理來使用;
  4. 隊列高可用:隊列能夠在集羣中的機器上進行鏡像,以確保在硬件問題下還保證消息安全;
  5. 多種協議的支持:支持多種消息隊列協議;
  6. 服務器端用Erlang語言編寫,支持只要是你能想到的全部編程語言;
  7. 管理界面: RabbitMQ有一個易用的用戶界面,使得用戶能夠監控和管理消息Broker的許多方面;
  8. 跟蹤機制:若是消息異常,RabbitMQ提供消息跟蹤機制,使用者能夠找出發生了什麼;
  9. 插件機制:提供了許多插件,來從多方面進行擴展,也能夠編寫本身的插件;

優勢:

  1. 因爲erlang語言的特性,mq 性能較好,高併發;
  2. 健壯、穩定、易用、跨平臺、支持多種語言、文檔齊全;
  3. 有消息確認機制和持久化機制,可靠性高;
  4. 高度可定製的路由;
  5. 管理界面較豐富,在互聯網公司也有較大規模的應用;
  6. 社區活躍度高;

缺點:

  1. 儘管結合erlang語言自己的併發優點,性能較好,可是不利於作二次開發和維護;
  2. 實現了代理架構,意味着消息在發送到客戶端以前能夠在中央節點上排隊。此特性使得RabbitMQ易於使用和部署,可是使得其運行速度較慢,由於中央節點增長了延遲,消息封裝後也比較大;
  3. 須要學習比較複雜的接口和協議,學習和維護成本較高;

ActiveMQ

ActiveMQ是由Apache出品,ActiveMQ 是一個徹底支持JMS1.1和J2EE 1.4規範的 JMS Provider實現。它很是快速,支持多種語言的客戶端和協議,並且能夠很是容易的嵌入到企業的應用環境中,並有許多高級功能。

主要特性:

  1. 服從JMS 規範:JMS 規範提供了良好的標準和保證,包括:同步或異步的消息分發,一次和僅一次的消息分發,消息接收和訂閱等等。聽從 JMS 規範的好處在於,不論使用什麼 JMS 實現提供者,這些基礎特性都是可用的;
  2. 鏈接性:ActiveMQ 提供了普遍的鏈接選項,支持的協議有:HTTP/S,IP 多播,SSL,STOMP,TCP,UDP,XMPP等等。對衆多協議的支持讓 ActiveMQ 擁有了很好的靈活性。
  3. 支持的協議種類多:OpenWire、STOMP、REST、XMPP、AMQP ;
  4. 持久化插件和安全插件:ActiveMQ 提供了多種持久化選擇。並且,ActiveMQ 的安全性也能夠徹底依據用戶需求進行自定義鑑權和受權;
  5. 支持的客戶端語言種類多:除了 Java 以外,還有:C/C++,.NET,Perl,PHP,Python,Ruby;
  6. 代理集羣:多個 ActiveMQ 代理能夠組成一個集羣來提供服務;
  7. 異常簡單的管理:ActiveMQ 是以開發者思惟被設計的。因此,它並不須要專門的管理員,由於它提供了簡單又使用的管理特性。有不少中方法能夠監控 ActiveMQ 不一樣層面的數據,包括使用在 JConsole 或者 ActiveMQ 的Web Console 中使用 JMX,經過處理 JMX 的告警消息,經過使用命令行腳本,甚至能夠經過監控各類類型的日誌。

優勢:

  1. 跨平臺(JAVA編寫與平臺無關有,ActiveMQ幾乎能夠運行在任何的JVM上)
  2. 能夠用JDBC:能夠將數據持久化到數據庫。雖然使用JDBC會下降ActiveMQ的性能,可是數據庫一直都是開發人員最熟悉的存儲介質。將消息存到數據庫,看得見摸得着。並且公司有專門的DBA去對數據庫進行調優,主從分離;
  3. 支持JMS :支持JMS的統一接口;
  4. 支持自動重連;
  5. 有安全機制:支持基於shiro,jaas等多種安全配置機制,能夠對Queue/Topic進行認證和受權。
  6. 監控完善:擁有完善的監控,包括Web Console,JMX,Shell命令行,Jolokia的REST API;
  7. 界面友善:提供的Web Console能夠知足大部分狀況,還有不少第三方的組件可使用,如hawtio;

缺點:

  1. 區活躍度不及RabbitMQ高;
  2. 根據其餘用戶反饋,會出莫名其妙的問題,會丟失消息;
  3. 目前重心放到activemq6.0產品-apollo,對5.x的維護較少;
  4. 不適合用於上千個隊列的應用場景;

RocketMQ

RocketMQ出自 阿里公司的開源產品,用 Java 語言實現,在設計時參考了 Kafka,並作出了本身的一些改進,消息可靠性上比 Kafka 更好。RocketMQ在阿里集團被普遍應用在訂單,交易,充值,流計算,消息推送,日誌流式處理,binglog分發等場景。

主要特性:

  1. 是一個隊列模型的消息中間件,具備高性能、高可靠、高實時、分佈式特色;
  2. Producer、Consumer、隊列均可以分佈式;
  3. Producer向一些隊列輪流發送消息,隊列集合稱爲Topic,Consumer若是作廣播消費,則一個consumer實例消費這個Topic對應的全部隊列,若是作集羣消費,則多個Consumer實例平均消費這個topic對應的隊列集合;
  4. 可以保證嚴格的消息順序;
  5. 提供豐富的消息拉取模式;
  6. 高效的訂閱者水平擴展能力;
  7. 實時的消息訂閱機制;
  8. 億級消息堆積能力;
  9. 較少的依賴;

優勢:

  1. 單機支持 1 萬以上持久化隊列
  2. RocketMQ 的全部消息都是持久化的,先寫入系統 PAGECACHE,而後刷盤,能夠保證內存與磁盤都有一份數據,

訪問時,直接從內存讀取。

  1. 模型簡單,接口易用(JMS 的接口不少場合並不太實用);
  2. 性能很是好,能夠大量堆積消息在broker中;
  3. 支持多種消費,包括集羣消費、廣播消費等。
  4. 各個環節分佈式擴展設計,主從HA;
  5. 開發度較活躍,版本更新很快。

缺點:

支持的客戶端語言很少,目前是java及c++,其中c++不成熟;

RocketMQ社區關注度及成熟度也不及前二者;

沒有web管理界面,提供了一個CLI(命令行界面)管理工具帶來查詢、管理和診斷各類問題;

沒有在 mq 核心中去實現JMS等接口;

Kafka

Apache Kafka是一個分佈式消息發佈訂閱系統。它最初由LinkedIn公司基於獨特的設計實現爲一個分佈式的提交日誌系統( a distributed commit log),,以後成爲Apache項目的一部分。Kafka系統快速、可擴展而且可持久化。它的分區特性,可複製和可容錯都是其不錯的特性。

主要特性:

  1. 快速持久化,能夠在O(1)的系統開銷下進行消息持久化;
  2. 高吞吐,在一臺普通的服務器上既能夠達到10W/s的吞吐速率;
  3. .徹底的分佈式系統,Broker、Producer、Consumer都原生自動支持分佈式,自動實現負載均衡;
  4. 支持同步和異步複製兩種HA;
  5. 支持數據批量發送和拉取;
  6. zero-copy:減小IO操做步驟;
  7. 數據遷移、擴容對用戶透明;
  8. 無需停機便可擴展機器;
  9. 其餘特性:嚴格的消息順序、豐富的消息拉取模型、高效訂閱者水平擴展、實時的消息訂閱、億級的消息堆積能力、按期刪除機制;

優勢:

  1. 客戶端語言豐富,支持java、.net、php、ruby、python、go等多種語言;
  2. 性能卓越,單機寫入TPS約在百萬條/秒,消息大小10個字節;
  3. 提供徹底分佈式架構, 並有replica機制, 擁有較高的可用性和可靠性, 理論上支持消息無限堆積;
  4. 支持批量操做;
  5. 消費者採用Pull方式獲取消息, 消息有序, 經過控制可以保證全部消息被消費且僅被消費一次;
  6. 有優秀的第三方Kafka Web管理界面Kafka-Manager;
  7. 在日誌領域比較成熟,被多家公司和多個開源項目使用;

缺點:

  1. Kafka單機超過64個隊列/分區,Load會發生明顯的飆高現象,隊列越多,load越高,發送消息響應時間變長
  2. 使用短輪詢方式,實時性取決於輪詢間隔時間;
  3. 消費失敗不支持重試;
  4. 支持消息順序,可是一臺代理宕機後,就會產生消息亂序;
  5. 社區更新較慢;

image

消息隊列的pull和push

這麼多的消息隊列,有一個區別很重要,那就是模式,究竟是pull好仍是push好,下面就讓咱們來一探究竟吧

Push

Push即服務端主動發送數據給客戶端。在服務端收到消息以後當即推送給客戶端。

Push模型最大的好處就是實時性。由於服務端能夠作到只要有消息就當即推送,因此消息的消費沒有「額外」的延遲。

可是Push模式在消息中間件的場景中會面臨如下一些問題:

  • 在Broker端須要維護Consumer的狀態,不利於Broker去支持大量的Consumer的場景
  • Consumer的消費速度是不一致的,由Broker進行推送難以處理不一樣的Consumer的情況
  • Broker難以處理Consumer沒法消費消息的狀況(Broker沒法肯定Consumer的故障是短暫的仍是永久的)
  • 大量的推送消息會加劇Consumer的負載或者沖垮Consumer

Pull模式能夠很好的應對以上的這些場景。

Pull

Pull模式由Consumer主動從Broker獲取消息。

這樣帶來了一些好處:

  • Broker再也不須要維護Consumer的狀態(每一次pull都包含了其實偏移量等必要的信息)
  • 狀態維護在Consumer,因此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

使用long-polling模式,Consumer主動發起請求到Broker,正常狀況下Broker響應消息給Consumer;在沒有消息或者其餘一些特殊場景下,能夠將請求阻塞在服務端延遲返回。

long-polling不是一種Push模式,而是Pull的一個變種。

那麼:

  • 在Broker一直有可讀消息的狀況下,long-polling就等價於執行間隔爲0的pull模式(每次收到Pull結果就發起下一次Pull請求)。
  • 在Broker沒有可讀消息的狀況下,請求阻塞在了Broker,在產生下一條消息或者請求「超時以前」響應請求給Consumer。

以上兩點避免了多餘的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還存在什麼問題嗎,還能改進嗎?

Dynamic Push/Pull

「在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)。

img

考慮這樣一種方式,它有long-polling的優點,同時能減小在有消息可讀的狀況下由Broker主動push消息給Consumer,減小沒必要要的請求。

參考文章

http://www.luyixian.cn/news_s...

https://blog.51cto.com/caczjz...

https://www.jianshu.com/p/251...

https://www.jianshu.com/p/36a...

相關文章
相關標籤/搜索