导语 | 近日,云+社区技术沙龙“腾讯开源技术”圆满落幕。本次沙龙邀请了多位腾讯技术专家围绕腾讯开源与各位开发者进行探讨,深度揭秘了腾讯开源项目TencentOS tiny、TubeMQ、Kona JDK、TARS以及MedicalNet。本文是对张国成老师演讲的整理。
本文要点:
- Message Queue 的原理和特点;
- TubeMQ相关实现原理及使用介绍;
- TubeMQ后续的发展和探讨。
一、Message Queue 简介
对于Message Queue(以下简称MQ),Wiki百科上的定义指:不同进程之间或者相同进程不同线程之间的一种通讯方式,它是一种通讯方式。
那我们为什么要采用MQ呢?这是由MQ的特点来决定的。第一是因为它可以整合多个不同系统共同协作;第二是它可以解耦,进行数据传递和处理;第三是它可以做峰值的缓冲处理,我们平常接触到的像Kafka、RocketMQ、Pulsar等基本上也都有这样的特点。
那作为大数据场景下的MQ又有什么特点呢?从我个人的理解来说,就是高吞吐低延时,系统尽可能地稳定,成本尽可能地低,协议也不需要特别地复杂,特别是水平扩展能力要尽可能的高。
因为像海量数据基本上都是到百亿、千亿、万亿,比方说我们自己的生产环境可能一个月、一年的时间就会翻一番,如果没有横向的扩展能力,系统就很容易出现各种问题。
二、TubeMQ实现原理及使用介绍
1.TubeMQ的特点
那么,腾讯自研的TubeMQ又有什么样的特点呢?TubeMQ属于万亿级分布式消息中间件,专注于海量数据下的数据传输和存储,在性能、可靠性,和成本方面具有独特的优势。
针对大数据场景的应用,我们给出了一个测试方案(详细方案在腾讯TubeMQ开源docs目录里可以查询得到)。首先我们要做一个实际应用场景定义,在这个定义之下,再进行系统数据的收集整理。结果是:我们的吞吐量达到了14万的TPS,消息时延可以做到5ms以内。
可能有些人会感到好奇,因为有很多研究报告都分析过,同样的分布式发布订阅消息系统,例如Kafka这种,都能达到上百万的TPS。相比来说,我们的数据会不会显得太差?
其实这里是有一个前提的,那就是:我们是在1000个Topic中,并为每个Topic配置10个Partition的场景下达到的性能指标。对于Kafka,它或许可以达到百万级的TPS,或许时延可以降到很低,但在我们的大数据的场景下,就远远到不了这个量级。
我们的系统在线上已经稳定运行了7年的时间,系统的架构采用的是瘦客户端、偏服务器管控模型。这样的技术特点就决定了它相应的应用场景。比如实时的广告推荐、海量的数据上报、指标&监控、流处理、以及IOT场景下的数据上报等。部分数据有损是可以允许的,因为大不了重复再报一下也可以解决,但是考虑到数据的量级实在太大,所以只能牺牲部分的可靠性来换取高性能的数据接入。
2.TubeMQ的系统架构
TubeMQ系统架构是怎样的?如下图所示,它通过一个SDK与外部交互,当然直接通过我们定义的TCP协议实现对接也是可以的。值得注意的是:TubeMQ是纯Java编写的,它拥有Master HA的协调节点,采用弱zk来管理Offset。在消息存储模式上也同样进行了改进,调整了数据可靠性的方案,采用的是磁盘RAID10多副本+快速消费,而非多Broker节点多副本的方案,而且启用了元数据自管理模式等。
3.TubeMQ的研发历程
如下图所示,自有数据记录以来,我们一共经历了四个阶段,从引入到改进再到开始自研,再到现在的自我创新。从2013年6月最开始的200亿,到2019年11月的35万亿,预计2020年能达到40万亿。
总的来说,这也是我们当初要选择自研的重要的原因。因为当数据量还不大的时候,比如在10亿或者是10亿量级以下,用什么样MQ其实都是可以的。但是一旦达到百亿、百亿到千亿、千亿到万亿甚至是万亿以上数据量的时候,越来越多的制约因素就会相继出现,包括系统的稳定性、性能以及机器成本和运维成本等问题。
我们现网TubeMQ拥有1500台的机器,而1500台机器就只需要用到一个左右的人力来运维就可以了(非全职的两个人)。对于我们的Broker存储服务器,能够做到上线持续运行4个月不重启,这些都是我们在原基础之上所做的改进和创新带来的。
4.TubeMQ与其他MQ的横向比较
下图的这个表格是TubeMQ与其他MQ横向比较的数据情况,我们项目是开源的,大家如果感兴趣的话可以直接去验证一下。总的来说,TubeMQ比较适合需要高性能、低成本又容许极端情况下有数据损失的场景,是经得起实践考验的。
三、TubeMQ的存储模式与管控措施
MQ里最核心的是它的存储模式,如下图所示,右边的存储方案列表是一位叫陈大白的知乎用户给我们提供的,左边是TubeMQ的存储方案。
TubeMQ采用的是按Topic组织内存+文件的存储实例方案来实现的,数据先写到主内存,主内存写满之后切为备份内存,数据异步从备份内存刷到文件完成数据写入。它通过消费的偏移度来决定它是由主内存还是备内存还是文件消费数据,这样的话可以减轻系统的负担,提高它的存储量。
大家从右边的存储图上面可以看到:Kafka、RocketMQ和京东2019年发布JMQ,其实相差并不大。但是需要注意的是,每一种存储模式在不同的资源要求下,它的性能指标和相应的量级是不一样的。
因为我们做的是有损服务,有损的服务是怎么回事呢?就是我们在机器断电、没有存储或者没有刷盘的情况下,数据就会丢失,在磁盘RAID10都无法恢复的磁盘故障情况下数据也会丢失。除了这两种情况,其他的情况都不会丢。
因为上述两类故障随时可能发生,因而TubeMQ其实不适合用做持久的反复消费、而又需要前后数据完全一致的场景。那么,我们为什么要这么做呢?我们是不是做不到多副本呢?其实也不是的。
问题就在于成本方面的考量。我们这样做,如果横向作比较,大家知道我们能够省多少台机器吗?换算成金钱的话能省多少吗?
在这里给大家提供一个数据:2019年11月8日,开源Kafka项目的LinkIn公司发表了一篇文章,他们7万多条数据用了4000台机器,这个信息大家网上可以查到。另一个是我们国内做大数据与应用相关场景公司的例子,采用原生Kafka做大数据接入,在2018年底也达到了7、8万亿的数据量,花了1500台万兆机。
说回来,我们这种模式下需要多少台机器呢?我们现在达到35万亿的数据量用的也是1500台机器,在相同的前提下,我们对比外部MQ,使用的机器数量只有它们的1/4、1/5。换算成人民币的话是多少?一台商用机大概是10万左右,仅仅机器成本我们就可以节约到几个亿,这就是为什么要采用这个方案的原因。
跟Kafka异步节点复制方案相比,我们只需要1/4左右的机器量。当然,即便是用单复本,我们的性能也会比Kafka强很多,可以节约不少机器,相关数据可以看我们的测试报告。
TubeMQ所有的管控逻辑包括所有的API接口都是围绕着它的存储来做的,包括它的Topic的配置和流控的处理和数据的查询、API的库存等等。下图所示的是TubeMQ最核心的API接口定义,主要分为4个章节。如果只是使用的话,直接通过管控台操控就可以了,但如果你要精细化地去调控系统,就需要去了解API的定义了。
TubeMQ的管控模块Master,是基于BDB嵌入式数据库进行集群的Broker节点管理。各个Broker配置的Topic信息的数据存储,只要在标红的操作栏里操作,就会有一个状态告知操作者目前处于什么样的过程,是基于执行操作还是只读只写或者是可读写的情况。还可以通过这个页面查询。本系统在Windows上面就可以运行起来,欢迎大家去试用。
TubeMQ的认证授权设计和传统的也不太一样,因为我们把TubeMQ的认证机做了重新的定义,具体可见下图。
四、为什么选择开源?
第一,基于公司的开源政策要求:对内开源协同,对外形成技术影响力,所以我们选择了开源。第二,从我们掌握的信息来看,我们认为在这一领域开源TubeMQ,是可以对有需要的同学们产生实际价值的。第三,我们觉得开源是在打破壁垒。
在世界不同的角落,很多人都在研究这一问题,就像平行宇宙一样,大家都在各自的宇宙里面去研究和分析,相互之间没有太多的交流,我们相信肯定有人比我们做得更好,有值得我们学习的地方,所以我们把它开源出来,形成一个大家都了解、可以相互学习的状态,这样对自己也是一种促进。基于以上这三点,我们最终选择了开源。
在已经开源情况下为什么还要去贡献过给Apache呢?其实我也理解有很多做开发的同学不敢去用一些开源项目,因为有很多公司开源了一个项目,用着用着结果发现没有人维护了。
为了解决这个问题,我们希望把它捐献给一个中立的基金会,通过它已经成文的标准化流程,使项目成为一个大家可以接受的成熟项目,包括它的文档化和多种接入的情况。即便原创团队最后不接手这个项目了,后面也有人去接手它,使这个项目能够持续向前改进。
所以我们把它捐献给了基金会。为什么选择Apache呢?因为我们是专注于大数据场景的MQ,而Apache是基于大数据这个生态最为出名的社区,而我们也同样也受惠于这个生态,所以理所当然就想回馈社区,将项目捐献给Apache。前段时间TubeMQ已经成为了Apache的孵化项目。
五、TubeMQ的后续发展探讨
2020年上半年我们在开源的协同推广下,内部接入的业务数据将会越来越多,日均接入量相信很快就会过40万亿。
我们的机器也将会由以前的TS60升级成BX2,它将会带来什么样的变化呢?以往的机器是CPU 99%,磁盘IO是30~40%,根据最新的测试数据,在BX2上面变为CPU 30~40%,磁盘IO 99%。由此可见,我们需要把它磁盘的IO尽可能地降下来,或者选择其他更合适的机器,这是需要去研究的。
另外,因为我们已经开源了,后续如何培养社区也是一个比较关键的点。目前来看,我们会基于协作的机制将它开源,无论是公司内还是公司外的同学,一起贡献来把这个项目做大,我们会在自己擅长的领域把这个东西继续夯实,大家可以根据自己的需要去使用我们的项目。
同时大家在使用的过程中如果能发现有些不完善的地方,也希望能通过社区贡献出来,大家一起努力把这个项目做好。
其实我们不仅仅只有MQ,我们同样在做的还有汇聚层和采集层,在此之上还有管理层。我们的希望是把MQ这一块做稳定以后,再将整体开源出来。我们会允许这一套系统接纳不同的MQ,根据MQ不同的特点提供给外部业务使用,但对外部业务又是无感知的。
六、Q&A
Q:张老师,你刚才做了TubeMQ和Kafka的对比,还介绍了TubeMQ内部的存储结构,但是我发现它的内部存储结构和Kafka的存储没有差别,你们只多了一个备份缓存,我不知道为什么你们只是一个备份问题就可以把Kafka甩这么远?
A:Kafka是基于Partition的结构,一个Partition就会有一个文件块,而TubeMQ是基于Topic的,Partition已经是一个逻辑的概念。第二个不同是我们的内存是主备模式,你刚才已经提到了,为什么多了一个内存块就会快一些?写内存更快基本上是共识,然后把一块盘写满,写满了的切为备块异步去刷到文件,然后换块内存继续写,这样主备切换的话读写冲突就少了很多,整体就会更快一些。
我们为什么改为这样的存储结构呢?像Kafka,1000×10的时候就已经变成了随机读写,跑起来数据指标不是很好,而且也不稳定。RocketMQ是所有数据存储在一个文件,每一个Partition又构造了一个文件,这样子就带来一个问题:数据文件会有写入瓶颈,遇到流量增长时整个系统指标就上不来了。
JMQ是按Topic定义数据文件,但每个Partition定义新的文件,它比RocketMQ更宽泛一点,它数据不会集中到一个文件,它是按照Topic来的,解决了RokcetMQ的一些问题。TubeMQ是怎样呢?TubeMQ是一个Topic一个数据文件,不同的Topic有不同的文件,我们没有Partition。我们都是按Topic来定义存储单元的,一个数据文件 + 一个索引文件。大家可以去分析一下,它们是各有特点,不同的场景下的表现特征是不一样的,包括你的硬件场景,其实还是有很大差异的。
讲师介绍
张国成,腾讯高级工程师,2015年起负责TubeMQ项目研发工作,历经数据从万亿到35万亿的系统优化改造过程,在海量数据接入领域具有丰富的项目实践经验。