爲了得到更良好的閱讀體驗,建議您前往我的獨立域名博客觀看:傳送門
)java
當我試圖用一則通俗的比喻來講明這個概念的時候,我想到一個有意思的比喻:若是把隊列抽象成一個集合體,那麼消息隊列也就是一堆消息的集合。按照這個思路我想到了「雜誌」。這不就是一堆消息的集合嗎,關心這些消息的人都能經過「購買」來得到這些消息,而我能夠經過不一樣種類的「雜誌」或許到不一樣的消息。而且若是我做爲出版方,我能夠提供全部出版過的「雜誌」,也能夠選擇讓讀者只能購買近期的。git
假設咱們作了一個會議室預約系統,咱們的一個設備壞了。咱們須要通知預約這個會議室的全部人,因而咱們須要發郵件,僞代碼以下:github
@Service public class EquipmentServiceImpl implements EquipmentService { @Autowired private EmailService emailService; @Autowired private EquipmentRepository equipmentRepository; public void setEquipmentBroken(Long id) { Equipment equipment = equipmentRepository.findById(id); equipment.setStatus(Equipment.StatusEnum.BROKEN); emailService.sendEmail(); } }
問題來了,若是咱們後來發現設備壞了而且須要更改可用庫存的數量,這時候咱們是否是要在這裏加入 InventoryService
庫存服務的代碼呢?後來若是經理說設備壞了應該通知他纔對啊,因此咱們要不要加入 emailService.sendEmailTo(Manager)
這樣的代碼呢?redis
隨着咱們業務模塊接入愈來愈多,咱們的代碼與其餘模塊愈來愈耦合,修改代碼的難度也指數級的增長,因此咱們引入「消息隊列」,把「設備壞了」這樣的消息發送到隊列中,其餘關心這條消息的業務就會獲得這樣的「通知」,而後就會去作對應的事,這樣各個模塊之間就解耦了。僞代碼看上去以下:數據庫
public void setEquipmentBroken(Long id) { Equipment equipment = equipmentRepository.findById(id); equipment.broken(); eventBus.publish(new EquipmentBrokenEvent(equipment.id)); }
接着上面的例子,假設咱們已經把「發送郵件」、「修改庫存」以及「通知經理」的代碼都寫入了咱們的 Service 代碼中,它們分別耗時:30ms、50ms、80ms,而且咱們得知,本來最主要的功能實際上是「發送郵件」,但咱們完成主要的功能以後卻等待了更多的額外時間,這顯示是不合理的。服務器
因此咱們爲了提升用戶體驗&提升吞吐量,咱們其實能夠引入「消息隊列」來進行異步的操做。微信
假設咱們的服務器最多能支持每秒 1000 個請求,而咱們公司在節日要搞促銷,爲了不服務器掛掉咱們額外申請了兩臺服務器作了負載均衡,因而咱們如今的機器最理想的狀況可以支持每秒 3000 個請求,但奈何活動太火爆了,每秒來的請求有大概 4000 個,這些多出來的請求就可能致使服務器給直接掛掉了。架構
因此咱們就引入了一個「消息隊列」,讓消息不直接到達服務器,而是先讓「消息隊列」保存這些數據,而後讓下面的服務器每一次都取各自能處理的請求數再去處理,這樣當請求數超過服務器最大負載時,就不至於把服務器搞掛了。負載均衡
基於上面的描述,咱們大概能想到「消息隊列」的侷限性,例如當「生產者」須要「從消費者得到反饋」時,就會出現必定的問題。例如我以前嘗試着使用「事件驅動」的方式編碼時,我想要把 Service 的一些主邏輯給轉移到關注該事件的監聽器上時,發現有點問題,我本來的意圖是想讓一部分代碼解耦,但做爲主邏輯的一部分我須要保證它們準確的執行,當我使用「消息」的方式傳遞出去時,我沒法獲得消費者的反饋,因此最終我仍是把主邏輯給遷回來了,算是一次失敗的嘗試吧。框架
經過上述的問題你也看到了,「消息隊列」適用於異步處理,而且是那些不指望從消費者獲得反饋的處理。就好像一開始說到的設備壞了的問題,我只須要通知設備壞了,至於以後須要作什麼事,關心的人天然會去作相應的處理。
上面提到的異步處理,跟日誌系統彷佛搭配起來也很好。特別是當你須要把日誌發往單獨的數據平臺的時候,「消息隊列」尤其有用,咱們再也不須要在業務代碼裏面侵入咱們的各類打點or日誌,只須要簡單的發佈一條消息,再去關注作處理就行了。
基於上面的例子你應該也能感覺一二了。
這也是「消息隊列」常見的場景,經過引入「消息隊列」,咱們一來能夠控制請求的人數,二來也能夠緩解短期內高流量的壓力。
消息通信是指,消息隊列通常都內置了高效的通訊機制,所以也能夠用在純的消息通信。好比實現點對點消息隊列,或者聊天室等。
咱們在討論市面上常見的「消息隊列」中間件以前,咱們先來考慮本身造一個怎麼樣?若是是你本身來設計,你會怎麼作?乍一想,彷佛每一個語言都會有本身實現的「隊列」,往隊列裏塞數據,再從隊列裏面挨個取就好了?
可是一細想好像事情並不簡單。做爲一個「消息隊列」,你首先要保證數據不能給人家弄丟了吧?存內存?萬一斷電了怎麼辦?寫磁盤?消息量超過系統寫磁盤速率上限了怎麼辦?備份又該怎麼作呢?
好,假設我一整搗鼓,保證了個人數據不會丟失了,下一個問題,生產者怎麼往「消息隊列」裏面塞數據?個人意思是,生產者可能不止一個,把全量的消息放在一個隊列彷佛不太合適,我須要給這些消息分個類吧?新來了一個分類的消息我怎麼動態的擴容呢?消費者又如何消費這些數據呢?多個消費者之間又如何進行協調呢?
好吧..總之問題挺多的..並不像表面那麼簡單。
RabbitMQ 是使用 Erlang 編寫的一個開源的消息隊列,自己支持不少的協議:AMQP,XMPP, SMTP, STOMP,也正因如此,它很是重量級,更適合於企業級的開發。同時實現了 Broker 構架,這意味着消息在發送給客戶端時先在中心隊列排隊。對路由,負 載均衡或者數據持久化都有很好的支持。
Redis 也能用來作「消息隊列」。Redis 是一個基於 Key-Value 對的 NoSQL 數據庫,開發維護很活躍。雖然它是一個 Key-Value 數據庫存儲系統,但它自己支持 MQ 功能, 因此徹底能夠當作一個輕量級的隊列服務來使用。對於 RabbitMQ 和 Redis 的入隊和出隊操做,各執行 100 萬次,每 10 萬次記錄一次執行時間。測試 數據分爲 128 Bytes、512 Bytes、1 K和 10 K四個不一樣大小的數據。實驗代表:入隊時,當數據比較小時 Redis 的性能要高於 RabbitMQ,而若是數據大小超過了 10 K,Redis 則慢的沒法忍受;出隊時,不管數據大小,Redis 都表現出很是好的性能,而 RabbitMQ 的出隊性能則遠低於Redis。
Kafka 是 Apache 下的一個子項目,是一個高性能跨語言分佈式 Publish/Subscribe 消息隊列系統,而 Jafka 是在 Kafka 之上孵化而來的,即 Kafka 的一個升級版。
具備如下特性:
Kafka 經過 Hadoop 的並行加載機制來統一了在線和離線的消息處理。Apache Kafka 相對於 ActiveMQ 是一個很是輕量級的消息系統,除了性能很是好以外,仍是一個工做良好的分佈式系統。
ZeroMQ 號稱最快的消息隊列系統,尤爲針對大吞吐量的需求場景。ZeroMQ 可以實現 RabbitMQ 不擅長的高級 / 複雜的隊列,可是開發人員須要本身組合多種技術框架,技術上的複雜度是對這 MQ 可以應用成功的挑戰。ZeroMQ 具備一個獨特的非中間件的模式,你不須要安裝和運行一個消息服務器或中間件,由於你的應用程序將扮演這個服務器角色。你只須要簡單的引用 ZeroMQ 程序庫,可使用 NuGet 安裝,而後你就能夠愉快的在應用程序之間發送消息了。可是 ZeroMQ 僅提供非持久性的隊列,也就是說若是宕機,數據將會丟失。其中,Twitter 的 Storm 0.9.0 之前的版本中默認使用 ZeroMQ 做爲數據流的傳輸(Storm 從 0.9 版本開始同時支持 ZeroMQ 和 Netty 做爲傳輸模塊)。
ActiveMQ 是 Apache 下的一個子項目。 相似於 ZeroMQ,它可以以代理人和點對點的技術實現隊列。同時相似於 RabbitMQ,它少許代碼就能夠高效地實現高級應用場景。
按照慣例黏一個尾巴:
歡迎轉載,轉載請註明出處!
獨立域名博客:wmyskxz.com
簡書ID:@我沒有三顆心臟
github:wmyskxz 歡迎關注公衆微信號:wmyskxz 分享本身的學習 & 學習資料 & 生活 想要交流的朋友也能夠加qq羣:3382693