如何打造高可靠高性能的消息队列(ZZMQ)

背景

互联网公司使用最频繁的服务调用组件是RPC框架,RPC同步调用有些场景并不是很适用,而这些场景刚好是一个可靠MQ的适用场景。

我们看看RPC调用的场景。服务A调用如图所示服务。在正常情况下,一般都不会有问题。但是在以下情况,服务A调用会遇到问题。

问题一:如果有流量高峰,服务B响应超时,会发生什么情况?

整个RPC调用链路都会受到影响,甚至发生雪崩。

问题二:服务A逻辑复杂,逻辑耦合严重,怎么做拆分?

把一些调用链路中可以异步调用的逻辑调整为消费MQ消息。

问题三:RPC调用,jar依赖问题?服务B升级,调用B的相关服务是否需要升级?

RPC服务需要依赖生成的接口描述jar,服务接口升级一般很难做到向前兼容,所以相关调用方也需要升级。

MQ是以消息为载体的可靠异步调用的框架,能很好的应对上面三个问题。流量削峰,MQ是天然支持的,因为MQ有可靠存储,可以落地。解耦合,交给MQ也很合适。因为MQ的接入方处理的是消息,做到向前兼容也是比较容易的。

使用MQ之后,服务A,B通过MQ做到松耦合,也能很好的应对流量高峰。

MQ很好很强大,是RPC的有效补充,那问题来了怎么实现一个可靠MQ?本文结合二手交易平台的特点,详细介绍转转ZZMQ架构设计实践。

转转消息队列(ZZMQ)架构设计

设计ZZMQ,我们考虑的重点是:可靠,高性能,运维友好,接入方便,可以支持大量堆积,有效缓冲业务高峰,针对这些需求,我们做了一些设计上的考虑和取舍,最终形成了如下的架构方案。

RegisterCenter 作为注册中心,负责路由服务,无状态,每个NameNode都是对等的,NameNode 可以任意水平扩展。NameNode 与broker和Client都建立了长连接。Broker 内是主从两台机器,slave从master拉取消息Log和消费Offset,做HA。Broker也可以方便地水平扩展,加入新机器,更新topic的路由信息,client会定时更新路由信息。Consumer 和Producer 都需要到注册中心注册,同时拉取Topic的路由信息。Management Protal 是用来管理集群,维护Topic的相关联系人信息。

存储设计

一个高性能ZZMQ的瓶颈和难点就是存储系统,存储系统关乎到性能,数据可靠存储实现是否简单,数据备份控制,消息状态的表示。

目前最常见的是以Kafka为代表的Log-append-file,文件顺序写,追求吞吐量,消息消费状态通过offset来控制,还有以各种存储引擎实现的,比如leveldb(activemq),rocksdb等。

从这张图(http://queue.acm.org/detail.cfm?id=1563874),我们能看到磁盘顺序读写的性能甚至超过了内存随机访问的性能,能达到50多M/s。并且,相对来说,Log-append-file简单一些。最后选择了类似kafka的log-append-file。

Kafkatopic分成partition,顺序读写,partition有一个小的问题,单机不能支撑大量的topic,单机能支持几百分区,随着分区数量的增加,性能有所下降。而我们的场景的是:业务大部分topic的qps并不像kafka处理的日志文件那么高,希望能单机支撑更多的topic数量。

针对这一点,我们做了一些调整,topic消息本身不再分区,统一存储,使用更加轻量的Consumer Queue实现partition的功能。Consumer Queue存储的只是位置信息,更加轻量,能做到支持更多的topic。

消息写入pageCache,再Dispatch到对应的Consumer Queue中去,Consumer Queue只有消息Log的offset信息,以及大小和其他的一些元数据,占用很小的空间。这样设计,经过验证,单机能够支持几千队列。

这样的设计也带来了一个副作用,因为消息本身是统一存储,topic数量多的时候,消息的读取是随机读,性能有些许下降,目前,我们通过优化linuxpageCache和IO调度,开启系统预读,性能相对顺序读没有太大下降。

ZZMQ消息订阅模型

Consumergroup A 有两个消费者实例,B也有两个消费者实例,同时订阅了Topic-A,他们之间的消费进度是独立的。Group处理相同topic,消费逻辑一致的一个整体,包含不同的消费实例。

ConsumerGroup基本上是跟kafka的Group一致,由不同的ConsumerGroup来区分不同的消费组,维护Group对应的消费状态,能很方便的实现。

网络

基于netty实现了网络层的协议。Netty对nio,和reactor做了很好的封装,netty4.x 对direct buffer的利用,以及高效的buffer分配和回收算法,netty也解决了TCP协议的半包问题,使用方不需要自己组TCP包。用netty实现网络层协议,网络层的可靠由netty来做,减少了复杂度。

Push 还是Pull

这个设计的考虑主要是两个方面:慢消费,以及消费延迟。主动Push能做到低的消费延迟,但是对于慢消费,不能很好的应对,主动Push需要感知消费者的速率,不至于push 太快,把消费者压垮了。Pull模式,由消费者控制拉取的速度,能很好的应对慢消费的问题,但是,Pull模式对消费延迟不敏感,拉取的频率不好控制,处理不好有可能造成CPU使用率飙升。参照kafka的pull,实现了longpull。Consumer 与Broker建立长连接,Consumer发起拉取请求,如果有消息,Broker 返回消息,如果没有消息,broker hold住这个请求一段时间,等有消息再notify这个请求,返回给客户端。基于long pull,消费延迟能降到跟push差不多,同时又能由Consumer 控制拉取速度。

重复消息和顺序消息

消息重复在一个复杂网络条件下很难避免的,如果由MQ本身来做去重,代价太大,所以我们要求接入MQ的逻辑做好幂等(状态机或者版本号的一些机制)。顺序消息,跟kafka一样,Consumer queue跟partition类似,一个Queue内部,消费是有序的。如果要做到完全有序,只能一个Producer,一个Consumer。

高可用

Broker组由主从两台机器构成,可以配置的策略有同步双写和异步复制。默认情况,采用的异步复制,由slave去拉取master上面的消息Log和offset。Broker 接入了内部的监控系统,每分钟上报topic的消费情况和broker状态,能做到分钟级别发现异常消费。消息写入PageCache之后,默认是异步刷盘。也可以配置为同步刷盘,只有刷盘成功才会返回。

Broker的默认配置异步刷盘,Master-Slave异步复制。Client消费消费消息的可靠是通过Consume queue的offset来保证的,Client会定时上报已经消费成功的Offset信息。如果Client被异常kill掉,没有确认的消息会被Client 重新拉取,消费。

一条消息的生命过程

Proudcer通过与NameNode的路由找到topic的broker地址,producer发消息,netty序列化,通过TCP传输到对应的broker,broker写log的pageCache,返回。Broker Dispatch消息到对应的Consumer queue,broker唤醒刷盘线程,broker唤醒slave同步线程,slave拉取消息。

NotifyConsumer 拉取请求,经过netty序列化,通过tcp到达Consumer,Consumer消费成功,Client上报消费成功offset。

Broker持久化Offset,slave同步offset。三天之后,broker删除消息。

总结

本文主要介绍了转转ZZMQ在架构、存储系统、保证消息可靠存储,可靠消费,以及netty的使用等经验,分享出来,与大家一起探讨,欢迎拍砖指正。

原文发布于微信公众号 - 架构之美(beautyArch)

原文发表时间:2016-12-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏写代码的海盗

Docker学习总结之跨主机进行link 方案一:  方案二:

Docker学习总结之跨主机进行link Docker的功能非常强大,但要想驾驭好Docker却不是一件很容易的事情。下面就介绍一种日常工作中会遇到的一个us...

2746
来自专栏MessageQueue

如何在MQ中实现支持任意延迟的消息?

定时消息与延迟消息在代码配置上存在一些差异,但是最终达到的效果相同:消息在发送到 MQ 服务端后并不会立马投递,而是根据消息中的属性延迟固定时间后才投递给消费者...

3744
来自专栏匠心独运的博客

消息中间件—RocketMQ消息存储(一)一、MQ消息队列的一般存储方式二、RocketMQ消息存储整体架构三、RocketMQ文件存储模型层次结构四、总结

文章摘要:MQ分布式消息队列大致流程在于消息的一发一收一存,本篇将为大家主要介绍下RocketMQ存储部分的架构 消息存储是MQ消息队列中最为复杂和最为重要的...

3912
来自专栏企鹅号快讯

R基础入门(1)——搭建运行环境

R和Python都是最近比较火的开源数据分析与挖掘语言。两者各有千秋,都可以胜任基本的数据分析、挖掘、可视化以及大数据集的处理。我们先了解R,然后是Python...

2545
来自专栏社区的朋友们

Kafka 设计原理

跟 RabbitMQ 、RocketMQ 等目前流行的开源消息中间件相比,Kakfa 具有高吞吐、低延迟等特点,在大数据、日志收集等应用场景下被广泛使用。

2.5K2
来自专栏地方网络工作室的专栏

Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(十三)集成 UEditor 百度富文本编辑器

Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(十三)集成 UEditor 百度富文本编辑器 前情回顾 通过前面系统的学...

2888
来自专栏Ribbon

在vSphere中为不同服务器配置IPMI功能

在本示例中,一台DELL R730 XD服务器安装了ESXi 6.0,ESXi 的IP地址是192.168.100.11,这台DELL服务器iDRAC控制台的I...

2441
来自专栏互联网技术栈

kafka0.9.0 新特性(对比0.8)

0.9.0相比0.8.2,引入了一个新的Consumer API,这个API不再使用high level和low level的基于zookeeper的clien...

833
来自专栏杨建荣的学习笔记

关于switchover的流程和补充(r9笔记第4天)

对于Oracle Data Guard中的Switchover一般是计划内的操作,自己其实也处理了不少的故障,也算是轻门熟路。复杂的事情简单做,简单的事情重复做...

2645
来自专栏Java工程师日常干货

RocketMQ实战(一)What is RocketMQ?初步理解Producer/Consumer Group install RocketMQ

阿里巴巴有2大核心的分布式技术,一个是OceanBase,另一个就是RocketMQ。在实际项目中已经领教过RocketMQ的强大,本人计划写一个RocketM...

943

扫码关注云+社区