分布式消息队列浅析

队列做为一种比较抽象的数据结构,在程序世界中被普遍的应用,而实现方式和形态也各式各样,有使用进程内堆栈实现的,如stl库中的queue;有基于管道、Shmem实现的,如常见的同机进程间通讯模型,而随着分布式系统应用愈来愈普遍,跨机通讯的场景需来需多,面临的问题不只是消息投递问题,分布式系统普适性的挑战也随着应用场景的多样性而愈来愈多。java

一个优秀的分布式消息队列,我的分析应该具有如下的能力:高吞吐、低时延(因场景而异),传输透明,伸缩性强,有冗灾能力,一致性顺序投递,同步+异步的发送方式,完善的运维和监控工具,开源。但上面的能力有一些在设计理念上多是相悖的,或者是应用场景不一样,在最终的实现上会有所侧重。以腾讯互娱内部普遍使用的TBUS\TBUSD为例,最为看重的是一致性顺序投递以及低时延,但传输上作不到透明,须要使用者手工初始化队列,了解整个网格的拓扑,另外故障后也须要手工处理。和TSFG的负责人沟通,新版本在研发中的TBUS正在解决这个问题,从而减小使用者的学习和运营成本。golang

消息传输模型

从消息传输模型上,大体能够抽象为如下几种:apache

点对点模型(Point-to-point)api

基础模型中,只有一个发送者、一个接收者和一个分布式队列。以下图所示:安全

生产者消费者模型(Producer–consumer)服务器

若是发送者和接收者均可以有多个部署实例,甚至不一样的类型;可是共用同一个队列,这就变成了标准的生产者消费者模型。在该模型,三个角色通常称为生产者(Producer)、分布式队列(Queue)、消费者(Consumer)。markdown

发布订阅模型(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/

相关文章
相关标签/搜索