天天看点

消息中间件MetaQ高性能原因分析

该篇是由原作者 傅冲 提供

<code>metaq</code>是一款高性能的消息中间件,经过几年的发展,已经非常成熟稳定,历经多年双11的零点峰值压测,表现堪称完美。

<code>metaq</code>当前最新最稳定的稳本是<code>3.x</code>系统,<code>metaq 3.x</code>重新设计和实现,比之前的版本更优秀。虽然<code>metaq</code>借鉴了<code>linkedin</code> 的消息中间件<code>kafak</code>思想,但已经是青出于蓝而胜于蓝。

本文不对<code>metaq</code>做全面的介绍,只选择高性能这点来分析。

消息中间件MetaQ高性能原因分析

以上测试图片,来自消息测中间件试团队 @以夕 妹子的性能测试结果

<code>metaq</code>作为一款消息中间件,消息中间件该有的功能,<code>metaq</code>也有。本文并不全面介绍<code>metaq</code>方面方面,只是选取性能这一角度,来剖析其高性能的原因。

<code>metaq server</code>

最为核心的组件,它主要可以接收应用程序发送过来的消息并存储,然后再投递。

<code>metaq master</code>

是<code>metaq server</code>逻辑上的角色,和<code>mysql master</code>概念类型,对外提供发送消息、订阅消息以及维护着管理信息。

<code>metaq slave</code>

是<code>metaq server</code>逻辑上的角色,和<code>mysql slave</code>概念类型,对外提供订阅消息功能。

<code>metaq client</code>

主要是应用程序使用,使用<code>metaq client</code>来发送消息、订阅消息、其它控制信息。

其它无数据管理及控制信息组件

提供订阅关系管理功能,<code>metaq server</code>服务发现功能。

<code>metaq client</code> 发送消息,<code>metaq server</code>收到消息,并存储到文件系统。也就是说<code>metaq</code>会有大量<code>write</code>系统调用。

<code>metaq client</code> 订阅消息,因其是<code>pull</code>的模型。<code>metaq server</code>收到<code>pull</code>消息的请求,会从磁盘上读取出消息,然后返回给<code>metaq client</code>。这一步有大量的<code>read</code>系统调用。

从上面的功能上看,<code>metaq server</code>要支持大量的磁盘<code>io</code>操作,因为其是构建文件系统之上的消息中间件。既然使用了文件系统来存储数据,但磁盘<code>qps</code>每秒也就是几百。<code>metaq server</code>又必须高性能(如<code>metaq server</code>性能是10w级别的qps),才能在可接收的成本范围内,满足业务需求(不丢消息)。如何在<code>qps</code>只有几百的磁盘上,构建出一个高性能的<code>metaq</code>消息间件正是本文的中心。

前面介绍了<code>metaq</code>高性能的难点,那么我们如何解决这些难点。要解决这些难点,就必须找出这些难点。那么要写一个高性能的消息中间件,会有哪些会部分会对影响性能。

序列化与反序列化

从<code>metaq cleint</code>要发送消息,必须要先序列化,然后才能通过网络发送出去。 <code>metaq server</code>收到消息后,要进行反序列化,才能解析出消息内容,最后序列化存储到文件系统。

<code>metaq client</code>收到消息,首页<code>metaq server</code>必须从文件中读取消息,然后通过网络发送给<code>metaq client</code>,收到消息,进行反序列化,应用才能识别消息内容。

<code>metaq</code>核心功能,都要通过序列化与反序列化,所以其性能,对<code>metaq</code>性能有关键性的影响,其实不是对<code>metaq</code>,只要使用了序列化与反序列化,其对性能影响都很大。

<code>write</code>性能

因为<code>metaq server</code>会有大量的<code>write</code>系统调用 ,所以其性能对<code>metaq</code>性能有着重要的影响。

<code>read</code>性能

因为<code>metaq server</code>会有大量的<code>read</code>系统调用 ,所以其性能对<code>metaq</code>性能有着重要的影响。

网络框架

因为发送消息,订阅消息都必须经过网络,如果网络组件性能不好,对<code>metaq</code>性能有着关键的影响。

要解决序列化与反序列化性能问题,我们就必须寻种各种序列化与反序列化技术性能对比,从而选出一个高性能的序列化与反序列化技术来作为<code>metaq</code>。

我们来看下<code>java</code>世界可以选择的序列化与反序列化技术

消息中间件MetaQ高性能原因分析
消息中间件MetaQ高性能原因分析

从图中性能数据,可以看出,个人认为<code>google</code>出品的<code>protocol buffers</code>应该是最佳选择,不管软件的质量、社区活跃、软件的后续发展上来说,都是不错的选择。

但<code>metaq</code>并没有选择<code>protocol buffers</code>作为其序列化与反序列化的技术,一个原因是<code>protocol buffers</code>居然在小版之间本都不兼容,<code>2.3</code>和<code>2.5</code>的版本都不兼容。这会带来一个严重的问题,如果<code>metaq</code>选择<code>2.3</code>的版本,应用程序选择了<code>2.5</code>,都会导致冲突,反之亦然。

<code>metaq</code>消息元数据是通过<code>json</code>来序列化与反序列化,消息<code>body</code>是交给应用自己序列化与反序列化。

虽然使用<code>protocol buffers</code>性能会更好,但带给用户带来麻烦。所以<code>metaq</code>选择使用<code>json</code>。

<code>io</code>优化

前面也已经介绍了,<code>metaq server</code> 存大大量的<code>io</code>,那么怎么优化呢?

<code>read</code>优化主要是使用了<code>mmap</code>文件映射技术。这样可以减少系统上下文切换和复制数据的开销。。

同时文件系统提供了文件预读的功能,也使的读取文件开销,特别是顺序读时,开销比较低。

前面也介绍了,<code>write</code>可能存在并发问题,那么<code>metaq</code>是如何解决的?

<code>metaq</code>消息只保留在一个物理文件上,所有的消息都会写一个物理文件,每个物理文件都是固定大小,超过设置的阀值后,自动创建新的一个文件。当磁盘快满时,会自动删除老的文件。

<code>group commit</code>也就是组提交,组提交是指可以多次分写请求只要通过一次刷新数据,就可以实现这些请求的数据都已刷新到磁盘上。

<code>mysql</code>数据库能保证<code>acid</code>,事务提交也使用了<code>group commit</code>来提高性能(为了保证<code>d</code>,数据需要持久化到文件系统)。

详细见下图

消息中间件MetaQ高性能原因分析

当<code>写请求1</code>到<code>metaq server</code>时,把线程写入内核后,触发<code>flush</code>线程刷新数据到磁盘,以保证数据的可靠性。

然后再向<code>metaq client</code> 响应发送消息成功。这个时间,只要文件系统和磁盘不损坏,数据是不会丢失的。

正在<code>flush线程</code>要准备刷新数据时,<code>写请求2</code>,<code>写请求3</code>,<code>写请求4</code>也到<code>metaq server</code>且写入数据,这样因<code>写请求1</code>写数据,触发的<code>flush</code>顺便也把<code>写请求2</code>,<code>写请求3</code>,<code>写请求4</code>的数据也刷新到磁盘。这样减少了刷新磁盘的次数,性能自然就高了,同时也保证的数据的可靠性。

如何实现<code>group commit</code>,请看源码

<code>write</code>如何保证并发安全,在写数据前,需要抢占一个锁,因为这只是把数据写到文件系统缓存中,所以持有锁的时间非常短,对性能友好。请看代码

<code>metaq</code>的网络框架,选择了<code>netty4</code>。<code>netty4</code>因出色的性能和易用性,成为高性能场景的不二选择。

<code>metaq</code>高性能的秘密,我们从其功能结构,从功能的作用,一个个解释了可能影响性能的点,及怎么解决这些问题,提高性能。

虽然一个个点看起来简单,但要实现一个稳定、高性能的消息系统,还是不容易的。