分布式消息隊列淺析


隊列作為一種比較抽象的數據結構,在程序世界中被廣泛的應用,而實現方式和形態也各式各樣,有使用進程內堆棧實現的,如stl庫中的queue;有基於管道、Shmem實現的,如常見的同機進程間通信模型,而隨着分布式系統應用越來越廣泛,跨機通信的場景需來需多,面臨的問題不僅是消息投遞問題,分布式系統普適性的挑戰也隨着應用場景的多樣性而越來越多。

一個優秀的分布式消息隊列,個人分析應該具備以下的能力:高吞吐、低時延(因場景而異),傳輸透明,伸縮性強,有冗災能力,一致性順序投遞,同步+異步的發送方式,完善的運維和監控工具,開源。但上面的能力有一些在設計理念上可能是相悖的,或者是應用場景不同,在最終的實現上會有所側重。以騰訊互娛內部廣泛使用的TBUS\TBUSD為例,最為看重的是一致性順序投遞以及低時延,但傳輸上做不到透明,需要使用者手工初始化隊列,了解整個網格的拓撲,另外故障后也需要手工處理。和TSFG的負責人溝通,新版本在研發中的TBUS正在解決這個問題,從而減少使用者的學習和運營成本。

消息傳輸模型

從消息傳輸模型上,大致可以抽象為以下幾種:

點對點模型(Point-to-point)

基礎模型中,只有一個發送者、一個接收者和一個分布式隊列。如下圖所示:

生產者消費者模型(Producer–consumer)

如果發送者和接收者都可以有多個部署實例,甚至不同的類型;但是共用同一個隊列,這就變成了標准的生產者消費者模型。在該模型,三個角色一般稱為生產者(Producer)、分布式隊列(Queue)、消費者(Consumer)。

發布訂閱模型(PubSub)

如果只有一類發送者,發送者將產生的消息實體按照不同的主題(Topic)分發到不同的邏輯隊列。每種主題隊列對應於一類接收者。這就變成了典型的發布訂閱模型。在該模型,三個角色一般稱為發布者(Publisher),分布式隊列(Queue),訂閱者(Subscriber)。

業界組件介紹

看下業界,開源的分布式消息隊列有很多種,側重的維度也略有不同,包括支持的消息模型也有一些差異,如果按是否有獨立進程來看,可以分為兩個大類:

  1. Broker

    Broker類的分布式消息隊列,是指有獨立部署進行的分布式服務,即發送者把消息發布到Broker進程,再由Broker進程推(或者是拉)給訂閱者。

    • RabbitMq

      RabbitMQ是使用Erlang編寫的一個開源的消息隊列,本身支持很多的協議:AMQP,XMPP, SMTP, STOMP,也正因如此,它非常重量級,更適合於企業級的開發。同時實現了Broker構架,這意味着消息在發送給客戶端時先在中心隊列排隊。對路由,負載均衡或者數據持久化都有很好的支持。

    • RocketMq

      RocketMq是由阿里研發團隊開發的分布式隊列,側重在消息的順序投遞、高吞吐量、可靠性,在阿里內部大量使用,多次在雲棲社區中被提及是“淘寶雙11”的保障。目前已捐贈給Apache軟件基金會。

    • Nats

      Ruby-Nats作者開發,Derek Collison自稱做了20多年的MQ,並經歷過TIBOC、Rendezvous、EMC公司. 目前由Apcera公司維護,提供源碼、二進制文件以及Docker鏡像,用戶有愛立信、HTC、百度、西門子、Vmware.Nats用Golang編寫,Nats的設計思念中消息的成功投遞不做保證,需要發送者自己維護,因此Nats在應用場景上還是比較有局限性。

    • Nats-streaming

      目前由Apcera公司維護,也采用Golang編寫,在保證吞吐量和時延的基礎上,解決了Nats消息投遞一致性的問題。之前和Apcera的Community Manager有過接觸,Apcera目前只有5位工程師在進行開發維護,所以Nats-streaming目前支持的客戶端API還比較少,只有Go、Java、Nodejs、C#,CAPI支持可能要到2017年中。

    • Kafka

      Kafka是Apache下的一個子項目,是一個高性能跨語言分布式發布/訂閱消息隊列系統,而Jafka是在Kafka之上孵化而來的,即Kafka的一個升級版。具有以下特性:快速持久化,可以在O(1)的系統開銷下進行消息持久化;高吞吐,在一台普通的服務器上既可以達到10W/s的吞吐速率;完全的分布式系統,Broker、Producer、Consumer都原生自動支持分布式,自動實現負載均衡;支持Hadoop數據並行加載,對於像Hadoop的一樣的日志數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。Kafka通過Hadoop的並行加載機制統一了在線和離線的消息處理。Apache Kafka相對於ActiveMQ是一個非常輕量級的消息系統,除了性能非常好之外,還是一個工作良好的分布式系統。

    • ActiveMq

      ActiveMQ是Apache下的一個子項目。 類似於ZeroMQ,它能夠以代理人和點對點的技術實現隊列。同時類似於RabbitMQ,它少量代碼就可以高效地實現高級應用場景。

  2. Brokerless

    Brokerless類的消息隊列,主要采用api的方式,編譯到應用程序中,在應用程序間進行點對點的通信。

    • ZeroMq

      ZeroMQ號稱最快的消息隊列系統,尤其針對大吞吐量的需求場景。ZeroMQ能夠實現RabbitMQ不擅長的高級/復雜的隊列,但是開發人員需要自己組合多種技術框架,技術上的復雜度是對這MQ能夠應用成功的挑戰。ZeroMQ具有一個獨特的非中間件的模式,你不需要安裝和運行一個消息服務器或中間件,因為你的應用程序將扮演這個服務器角色。你只需要簡單的引用ZeroMQ程序庫,可以使用NuGet安裝,然后你就可以愉快的在應用程序之間發送消息了。但是ZeroMQ僅提供非持久性的隊列,也就是說如果宕機,數據將會丟失。其中,Twitter的Storm 0.9.0以前的版本中默認使用ZeroMQ作為數據流的傳輸(Storm從0.9版本開始同時支持ZeroMQ和Netty作為傳輸模塊)。

    • NanoMq

      在技術選型時,我們一般從三個維度上去考量,吞吐量、時延、可靠性,不同的業務場景對兩個維度的技術指標會有比較大的差異。 比如阿里的rocketmq,因為面臨秒級的高並發場景,因此會十分看中吞吐量和消息的可靠性(不丟、順序投遞),而時延基本在100ms的級別,再比如kafka,最高的設計初衷也是做為分布式日志系統,因為看中的也是高吞吐量和可靠性。但對於游戲業務,實時音視頻業務,不太會面臨瞬間的訪問高峰,而對低時延、時延穩定性會更加看中,一般認為消息投遞應該在1-4ms以內。

Nats/Kafka測試

既然業界有如此豐富的組件,是否可以找到一種比TBUS更優的同時也適合游戲服務器的組件呢?帶着這個問題,作者對Kafka、Nats、Nats-streaming進行了測試,主要關注時延、吞吐量、消息安全性這三個維度上。測試方法如下:搭建了兩台機器,發送者和接收者在同一台物理機,broker部署在另一台機,兩台機器ping時延在0.8ms左右。 測試結果如下:

  • Kafka

得益於JAVA的跨平台能力,Kafka不需要安裝,可直接運行。 因為Kafka借助zookeeper進行節點的故障探測與路由管理,因些需要先啟動zookeeper。
測試版本為kafka_2.10-0.10.0.1 ,測試的時候碰到一些小問題,啟動不成功,后面看了下,kafka啟動不成功,常見的有兩個問題,一是內存不夠,因為java虛擬機運行時需要配置內存大小,如果內存不夠可以調整下運行腳本。 二是jmx服務,注釋掉bin/kafka-run-class.sh 下的這幾行就ok。

# JMX settings
#if [ -z "$KAFKA_JMX_OPTS" ]; then
#  KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false  -Dcom.sun.management.jmxremote.ssl=false "
#fi

# JMX port to use
#if [  $JMX_PORT ]; then
#  KAFKA_JMX_OPTS="$KAFKA_JMX_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT "
#fi

因為Kafka本周對消息是持久化的,可靠性不需要測試,因此主要關注時延和吞吐量,測試結果顯示吞吐量能達到65M/s, 很顯然是進行過了合包,但時延的表現不是很理想,無送包有多小,消息發送后,Consumer收到的時延的最小極限在32ms,調整了很多參數,都無然進行一步優化。
路由模式上,Kafka只支持發布\訂閱模型,即一個消息只能被一個訪閱者收到,在這一點Nats更豐富一些。 整理的測試結果來看,Kafka做為一個分布式日志\流水\經營分析系統,還是很不錯的,難怪很多2B的系統以及電商金融類的產品都在使用。

  • Nats/Nats-streaming

正如上面介紹的,Nats是由原Ruby-Nats作者Derek Collison設計開發,目前由Apcera維護,由golang語言編寫,研發團隊只有5個人,受限於團隊的規模,因此在API的支持上有一定的局限性。感興趣的同學可以看下源碼,協程的功能划分十分清晰,一些用法也很巧妙。 Nats和Nats-streaming最大的區別在於,Nats異步模式需要發送者自己處理消息丟失的問題,即不保證消息的“100%投遞成功”,也不做消息暫存, 而Nats-streaming解決了這個問題。
在吞吐量、時延測試上,二者表現也十分優異。 作者實測結果顯示,100W條消息投遞時耗時在3-4s,1秒可以投遞30-40w條消息,1k條消息投遞耗時在1毫秒,完全可以滿足像多人游戲等對時延比較挑剔的場景。

從路由模式上,Nats的支持非常豐富,支持以下三種:

Publish Subscribe


發布訂閱模式,一對多,一個消息多個訂閱者都可以收到,類似廣播的場景。支持同步和異步調用。

Request Reply

發送應答模式,Nats支持一對一和一對多的發送應答模式,可以手工指定有幾個訂閱者可以收到。發送應答模式采用同步調用。

Queue

隊列模式,一個消息發布后,只有一個訪閱者會收到,支持同步和異步調用。這個模式對於一些無狀態處理服務十分有用,比如數據倉庫的無狀態接入層,接入層可部署多台物理機組成一個集群,每個物理機無狀態,采用這個方式即達到了冗災能力,同時也可以保證每一個消息只會被處理一次。

從測試結果來看,Nats-streaming在安全性、時延、吞吐量上都可以達到一個比較好的水平,唯一不足的是API對各語言支持的還不夠,CAPI可能要到2017年才能release.

多組件對比測試

分布式消息隊列種類很多,沒有精力一一測試,在網上找了一個比較權威的測試結果跟大家分享下。

測試包量和發布速率如下所示,每次測試持續時間在30S以上。

256B requests at 3,000 requests/sec (768 KB/s)
1KB requests at 3,000 requests/sec (3 MB/s)
5KB requests at 2,000 requests/sec (10 MB/s)
1KB requests at 20,000 requests/sec (20.48 MB/s)
1MB requests at 100 requests/sec (100 MB/s)

時延統計為一次發布並收到回包的整體耗時,在不同的包量和發布量的分布如下:

Nats VS Redis

Kafka VS RabbitMQ

參考文獻:

https://kafka.apache.org/

http://nats.io/documentation/

http://bravenewgeek.com/tag/message-queues/

http://bravenewgeek.com/dissecting-message-queues/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM