消息中间件MetaQ高性能原因分【对外公布版本】

简介

前面写了关于文件系统的三篇文章,[深入浅出文件系统][文件系统之读写基础篇],[文件系统之读写高级篇],此篇算是对文件系统相关文章的一个总结。

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

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

本文不对MetaQ做全面的介绍,只选择高性能这点来分析。

性能测试对比图

以上测试图片,来自消息测中间件试团队 @以夕 妹子的性能测试结果,更多测试结果请点击
[Kafka、RabbitMQ、RocketMQ发送小消息性能对比],
[Kafka vs RocketMQ——Topic数量对单机性能的影响],
[Kafka vs RocketMQ——单机系统可靠]。

核心功能

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

功能组件

  • MetaQ Server

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

  • MetaQ Master

    MetaQ Server逻辑上的角色,和MySQL Master概念类型,对外提供发送消息、订阅消息以及维护着管理信息。

  • MetaQ Slave

    MetaQ Server逻辑上的角色,和MySQL Slave概念类型,对外提供订阅消息功能。

  • MetaQ Client

    主要是应用程序使用,使用MetaQ Client来发送消息、订阅消息、其它控制信息。

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

    提供订阅关系管理功能,MetaQ Server服务发现功能。

发送消息

MetaQ Client 发送消息,MetaQ Server收到消息,并存储到文件系统。也就是说MetaQ会有大量write系统调用。

订阅消息

MetaQ Client 订阅消息,因其是Pull的模型。MetaQ Server收到Pull消息的请求,会从磁盘上读取出消息,然后返回给MetaQ Client。这一步有大量的read系统调用。

矛盾

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

高性能

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

影响性能的关键几点

  • 序列化与反序列化

MetaQ Cleint要发送消息,必须要先序列化,然后才能通过网络发送出去。 MetaQ Server收到消息后,要进行反序列化,才能解析出消息内容,最后序列化存储到文件系统。

MetaQ Client收到消息,首页MetaQ Server必须从文件中读取消息,然后通过网络发送给MetaQ Client,收到消息,进行反序列化,应用才能识别消息内容。

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

  • write性能

因为MetaQ Server会有大量的write系统调用 ,所以其性能对MetaQ性能有着重要的影响。

  • read性能

因为MetaQ Server会有大量的read系统调用 ,所以其性能对MetaQ性能有着重要的影响。

  • 网络框架

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

如何高性能

  • 序列化与反序列化

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

我们来看下Java世界可以选择的序列化与反序列化技术

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

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

MetaQ消息元数据是通过JSON来序列化与反序列化,消息Body是交给应用自己序列化与反序列化。

虽然使用Protocol Buffers性能会更好,但带给用户带来麻烦。所以MetaQ选择使用JSON

  • IO优化

前面也已经介绍了,MetaQ Server 存大大量的IO,那么怎么优化呢?

read优化

read优化主要是使用了[文件系统之读写高级篇]里介绍的mmap文件映射技术。这样可以减少系统上下文切换和复制数据的开销。更多详情见[文件系统之读写高级篇]。

同时文件系统提供了文件预读的功能,也使的读取文件开销,特别是顺序读时,开销比较低。更多详情见[文件系统之读写高级篇]。

write优化

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

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

Group Commit技术

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

MySQL数据库能保证ACID,事务提交也使用了Group Commit来提高性能(为了保证D,数据需要持久化到文件系统)。

详细见下图

写请求1MetaQ Server时,把线程写入内核后,触发flush线程刷新数据到磁盘,以保证数据的可靠性。
然后再向MetaQ Client 响应发送消息成功。这个时间,只要文件系统和磁盘不损坏,数据是不会丢失的。

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

如何实现Group Commit,请看源码

  // Synchronization flush
        if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
            GroupCommitService service = (GroupCommitService) this.flushCommitLogService;
            if (msg.isWaitStoreMsgOK()) {
                request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes());
                service.putRequest(request);
                boolean flushOK =
                        request.waitForFlush(this.defaultMessageStore.getMessageStoreConfig()
                            .getSyncFlushTimeout());
                if (!flushOK) {
                    log.error("do groupcommit, wait for flush failed, topic: " + msg.getTopic() + " tags: "
                            + msg.getTags() + " client address: " + msg.getBornHostString());
                    putMessageResult.setPutMessageStatus(PutMessageStatus.FLUSH_DISK_TIMEOUT);
                }
            }
            else {
                service.wakeup();
            }
        }
        // Asynchronous flush
        else {
            this.flushCommitLogService.wakeup();
        }

并发安全

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

 synchronized (this) {
            long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now();

            // Here settings are stored timestamp, in order to ensure an orderly
            // global
            msg.setStoreTimestamp(beginLockTimestamp);

            MapedFile mapedFile = this.mapedFileQueue.getLastMapedFile();
            if (null == mapedFile) {
                log.error("create maped file1 error, topic: " + msg.getTopic() + " clientAddr: "
                        + msg.getBornHostString());
                return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, null);
            }
            result = mapedFile.appendMessage(msg, this.appendMessageCallback);
            switch (result.getStatus()) {
            case PUT_OK:
                break;
            case END_OF_FILE:
                // Create a new file, re-write the message
                mapedFile = this.mapedFileQueue.getLastMapedFile();
                if (null == mapedFile) {
                    // XXX: warn and notify me
                    log.error("create maped file2 error, topic: " + msg.getTopic() + " clientAddr: "
                            + msg.getBornHostString());
                    return new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result);
                }
                result = mapedFile.appendMessage(msg, this.appendMessageCallback);
                break;
            case MESSAGE_SIZE_EXCEEDED:
                return new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, result);
            case UNKNOWN_ERROR:
                return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
            default:
                return new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result);
            }

            DispatchRequest dispatchRequest = new DispatchRequest(//
                topic,// 1
                queueId,// 2
                result.getWroteOffset(),// 3
                result.getWroteBytes(),// 4
                tagsCode,// 5
                msg.getStoreTimestamp(),// 6
                result.getLogicsOffset(),// 7
                msg.getKeys(),// 8
                /**
                 * Transaction
                 */
                msg.getSysFlag(),// 9
                msg.getPreparedTransactionOffset());// 10

            this.defaultMessageStore.putDispatchRequest(dispatchRequest);

            eclipseTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp;
        } // end of synchronized

网络性能

MetaQ的网络框架,选择了Netty4Netty4因出色的性能和易用性,成为高性能场景的不二选择。

更多详细的介绍请看我2014年为双11优化Notify性能的文章,Notify优化性能及Netty实践

后记

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

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

________________________________________该篇是由原作者 傅冲 提供

时间: 2024-10-29 16:07:31

消息中间件MetaQ高性能原因分【对外公布版本】的相关文章

淘宝网今日对外公布全年打假数据

淘宝网今日对外公布全年打假数据.数据显示,2012年,淘宝网共处理侵权商品信息8700万条,处罚会员95万余人次.2013年,淘宝网对于因为售假严重违规扣分达24分的会员,将不再对此项扣分做年度清零,24分的分值会累计带入2014年.延长售假用户的处罚时限,意味着淘宝网将对出售假货的行为给予更严格的处理,有序规范市场.2012年12月14日,淘宝网长期不遗余力的打假举措也获得了美国贸易代表办公室的高度认可,在特别301报告名单中被移除.美国贸易代表办公室在报告中称:"淘宝网已经从2012年的名单

唯品会与乐蜂双方正式对外公布达成“联姻”

唯品会战略投资乐蜂与聚美优品独立上市,背后有一个共同的资本角色--红杉资本.红杉是这三家公司的共同投资者,因此,这次"l"."v"的联姻被业内人士认为是红杉的资本运作游戏.而投资了包括阿里巴巴.京东.美团等电商的红杉资本是中国电商幕后的重要推手之一. 上周,唯品会与乐蜂双方正式对外公布达成"联姻":唯品会投资1.125亿美元现金,战略入股东方风行旗下的乐蜂网子公司75%的股份,唯品会成乐蜂网最大股东. 与乐蜂网有着共同投资人的聚美优品,当下正在赴

中国电信对外公布了其过去两年围绕混合所有制经济

在今天召开的"中国电信开放合作大会"上,中国电信对外公布了其过去两年围绕混合所有制经济,探索自身业务机制改革的最新进展及未来规划. 中国电信创新业务事业部总经理李安民表示,中国电信今年将大力向混合经济机制转型,一方面会继续将旗下各业务基地公司化,另一方面将引入社会资本,加强国资与民资的合作. 在基地公司化方面,中国电信从2009年开始在全国各地陆续成立了8大带有"天翼"品牌的业务基地,打造自主经营的移动互联网核心产品.这8大基地包括:天翼视讯基地(上海).爱游戏基地

天猫对外公布了2015年的服务新标准

摘要: 昨日下午,天猫对外公布了2015年的服务新标准.这次发布的2015年服务细则中,加大了对消费体验差.不诚信商家的处罚力度,同时对垂直细分领域的的赔偿条款甚至超过了多个 B2C 电商 昨日下午,天猫对外公布了2015年的服务新标准.这次发布的2015年服务细则中,加大了对消费体验差.不诚信商家的处罚力度,同时对垂直细分领域的的赔偿条款甚至超过了多个 B2C 电商公司.这或许意味着阿里巴巴IPO后,天猫已成为这家公司的核心业务,并且不计成本加大了扶持力度. 先来看看阿里都做了哪些调整: 1.

项目名称- 一个对外公布的接口已经固定了:项目A怎么访问我的上下文为B的目录

问题描述 一个对外公布的接口已经固定了:项目A怎么访问我的上下文为B的目录 如题所示:http://host:port/A/能访问项目名为B的目录吗,(例如:http://host:port/B/).tomcat能设置映射吗?tomcat能否让一个项目名为A的url访问项目名为B的项目呢?项目要求:接口已经固定了,要访问我的项目.注:我能想到的就是建2个项目,然后A再请求B.但是太麻烦了.能否把A映射到B呢? 解决方案 直接A项目里放B项目的内容就好反正你要的效果不就是 访问A项目和直接访问B项

新浪阿里首次对外公布阶段性进展

摘要: 新浪阿里发布微博淘宝版 新浪科技讯 8月1日上午消息, 新浪 旗下新浪微博今日联手阿里巴巴集团旗下淘宝网发布"微博淘宝版"等一系列产品功能. 距离合作宣布仅仅3个月,双方首新浪阿里发布微博淘宝版 新浪科技讯 8月1日上午消息,新浪旗下新浪微博今日联手阿里巴巴集团旗下淘宝网发布"微博淘宝版"等一系列产品功能. 距离合作宣布仅仅3个月,双方首次对外公布阶段性进展.基于用户账户互通.数据交换,两大平台将同时为数亿用户及广大卖家提供全新的服务体验,加速社交购物2.0

国家发展和改革委员会对外公布了国家电子商务示范城市

昨日,国家发展和改革委员会对外公布了国家电子商务示范城市(下称"示范城市")电子商务试点专项工作的具体内容,今后,国家将依托北京.天津.上海.昆明等21个示范城市组织开展电子商务试点工作,以推动电子商务的有关政策在局部地区取得突破性进展. 根据通知,示范城市将在网络(电子)发票.电子商务企业公共信息服务.电子商务支付基础平台.跨境贸易电子商务服务.电子商务标准和交易产品追溯服务等6个重点领域展开试点.在电子商务标准和交易产品追溯服务方面,针对当前电子商务标准体系不健全,交易市场假冒伪劣

温州“移动门”被低调处理:结果未对外公布

工信部通报运营商不规范竞争增多 IT时报记者 钱立富 用大量的天翼手机同时通话,造成中国电信网络拥堵,以至相应区域的天翼用户无法正常拨打手机--温州"移动门"事件曾引起多方关注,央视也曾对此进行过报道.一个多月时间过去了,事件逐渐平息但下文却未见报道. 记者日前获悉,相关部门对此事件已低调处理完毕,具体结果没有对外公布.不过有消息称,当地移动公司有两名相关责任人被处理. 浙江通管局称已妥善处理 9月中旬,记者曾采访浙江通管局办公室负责宣传的叶主任,当时他表示,通管局正在对温州移动门事件

美国国土安全部被迫对外公布了一份词汇表

近日,在民间组织的持续压力下,美国国土安全部被迫对外公布了一份词汇表,其中披露了他们在监督社交网站及其它在线媒体时所涉及的敏感词. 据美国<福布斯>杂志网站报道,美国国土安全部之所以如此,是因为来自美国电子隐私信息中心的持续压力,该中心于2011年4月向美国国土安全部提交了一项有关信息自由的申请,请求查看该部门媒体监控计划的详细记录.但当时遭到了拒绝.随后,电子隐私信息中心又于去年年底提起上诉,直到众议院就该诉讼举行听证会,美国国土安全部才被迫公开了这一列表. 该词汇表总共分为8大类别,分别为