做者:neoremind
出處:http://neoremind.com/2018/03/...網絡
消息隊列做爲服務/應用之間的通訊中間件,能夠起到業務耦合、廣播消息、保證最終一致性以及錯峯流控(克服短板瓶頸)等做用。本文不打算詳細深刻講解消息隊列,而是體系化的梳理消息隊列可能涉及的技術點,起到提綱挈領的做用,構造一個宏觀的概念,使用思惟導圖梳理。架構
再介紹以前,先簡短比較下RPC和消息隊列。RPC大多屬於請求-應答模式,也包括愈來愈多響應式範式,對於須要點對點交互、強事務保證和延遲敏感的服務/應用之間的通訊,RPC是優於消息隊列的。那麼消息隊列(下文也簡稱MQ,即Message Queueu)能夠看作是一種異步RPC,把一次RPC變爲兩次,進行內容轉存,再在合適的時機投遞出去。消息隊列中間件每每是一個分佈式系統,內部組件間的通訊仍然會用到RPC。異步
目前開源界用的比較多的選型包括,ActiveMQ、RabbitMQ、Kafka、阿里巴巴的Notify、MetaQ、RocketMQ。下文的技術點梳理也是學習借鑑了這些開源組件,而後萃取出一些通用技術點。分佈式
關於消息隊列的體系化認知,見下方的思惟導圖。ide
通常分爲producer,broker,consumer三者。工具
詳細參考《體系化認識RPC》(http://www.infoq.com/cn/artic...)。性能
主要考慮MQ的延遲和吞吐。學習
高性能投遞方面,分爲producer和broker考慮。producer能夠同步變異步、單條變批量保證發送端高性能,批量發送的觸發條件能夠分爲buffer滿或者時間窗口到了。broker能夠進行多topic劃分,再多分區/queue來進行分治(Divide and Conquer)策略,加大並行度,分散投遞壓力。另外broker對於須要持久化的消息,能夠使用順序IO,page cache,異步刷盤等技術提升性能,可是異步刷盤在掉電的狀況下,可能會丟失數據,能夠結合下面的高可用方案,在數據嚴格不丟和高性能吞吐之間作折中。大數據
高性能消費,即consumer和broker通訊,進行推、拉消息。使用consumer group水平擴展消費能力,須要按照業務場景使用分區有序或者無序消費。零拷貝技術節省broker端用戶態到內核態的數據拷貝,直接從page cache發送到網絡,從而最大化發送性能。consumer批量pull,broker批量push。broker端還能夠作消息過濾,可經過tag或者插件實現。spa
主要針對broker而言。
集羣高可用,producer經過broker投遞消息,因此必然有且僅有一個broker主負責「寫」,選主策略分爲自動選主和非主動選擇,自動選主使用分佈一致性組件完成,例如Kafka使用zookeeper,非自動選主,例如RocketMQ依賴多個無狀態的name server。
數據高可用,針對broker持久化積壓消息場景。可藉助分佈式存儲完成,可是每每性能上是個短板,因此大多數主流產品都進行本地IO順序寫,進行主從備份,多副本拷貝保證可用性,例如RocketMQ分爲同步雙寫和異步複製,前者像HDFS同樣,寫完多個副本再返回producer成功,有必定性能損失,但不大,後者最大化性能,可是當主掛的時候,數據有丟失風險。
一樣,MQ集羣也須要考慮跨機房高可用(非「異地多活」),broker的寫高可用,要考慮最小化MTTR,同時不阻塞consumer消費。
採用分治(Divide and Conquer)策略,加大投遞和消費的並行度,多個topic、多個分區/queue、多個副本、多個slave或者鏡像。
producer、consumer和broker通訊的協議,包括AMQP、STOMP、MQTT、HTTP、OpenWire(ActiveMQ)、XMPP、自定義等等。
AMQP是比較全面和複雜的一個協議,包括協議自己以及模型(broker、exchange、routing key等概念),目前RabbitMQ是AMQP消息隊列最有名的開源實現,有很是多語言已經支持基於AMQP協議與消息隊列通訊,同時還能夠經過插件支持STOMP、MQTT等協議接入。Kafka、RocketMQ均使用自定義的協議。
包括三種
1) 點對點,也就是P2P,FIFO的隊列,能夠看作單播。
2) Topic模式,Pub/Sub發佈訂閱。
3) fanout廣播模式。
持久化消息,若是存儲在本地磁盤,能夠使用同步刷盤和異步刷盤兩種策略。磁盤不能無限堆積,會有清理策略,例如Kafka、RocketMQ都按照時間、數據量進行retention。
非持久化,僅放在內存,消費者處理完可選擇刪除掉。
對於producer,從API和I/O層面可以使用同步、異步,對於吞吐層面可以使用單條、批量。fire-and-forget模式,相似UDP,儘管發送便可。針對可能發生的錯誤,例如鏈接broker失敗,RPC超時、發佈消息失敗、發佈後無響應,可選擇忽略或者重發,因此每每重複投遞的狀況不可避免。
對於broker,若是要保證數據100%不丟,是可能的,可是須要犧牲下性能和吞吐,使用同步多寫、多副本策略+同步刷盤持久化消息,能夠嚴格保證不丟。另外,broker對於寫入消息的payload,也會作完整性校驗,例如CRC等。
消費次數,包括at most once、at least once、exactly once,其中前兩個比較好作到,最後的exactly once須要streaming consumer系統和broker端協做完成,例如storm的trident和flink。
推拉模式,push or pull。推模式最小化投遞延遲,可是沒有考慮consumer的承載能力,拉通常是輪詢接收broker的數據,按照consumer本身的能力消費。
消費記錄點,通常每一個消息都有一個offset、ID或者時間戳,consumer能夠按照這個offset來進行定點消費以及消息重放。
消息確認,consumer消費完成ACK回調broker或者集羣高可用中間件(zk)通知消費進度。
錯誤處理,對於消費失敗的狀況,能夠回覆NACK,要求重發/requeue消息,當錯誤超多必定閾值時候,放到死信隊列中。
消息重複消費,這和消費次數有關係,consumer在某些時候須要作到冪等性,保證重複消費不會引發業務異常。
順序消息,有序的話,分爲分區有序或者全局有序,前者能夠按照某個業務ID取模,在發送端發到不一樣的分區/queue便可,後者每每須要單個隊列才能夠知足。無序消費則可最大化吞吐。
定時消息,事務消息,例如RocketMQ均支持。
目前RocketMQ支持消息根據msgId查詢。
客戶端語言的豐富性,與其餘系統的集成度,例如Kafka和大數據技術棧融合很緊密,Spark、Storm、Flink、Kylin都有對應的connector。
分佈式系統的管理是提升生產效率的必備保障,一個好的系統,若是周邊工具不完善,對於使用者會很不友好,推廣也會有困難。
對於消息隊列,能夠從topic管理、broker管理、集羣管理、權限/配額管理、多租戶、客戶端工具、監控、報警、控制檯Console UI來全方位進行治理。