消息队列与RocketMq核心机制详解

一、 消息队列

1.1 队列与消息队列

简单的来说,消息队列就是基于“先进先出的一种数据结构”,在开发中经常会用到。但是在实际生产中,期望这个队列是高可用的(消除单节点故障)、高性能的(应对大流量冲击)、消息可靠性(持久化、认证)。所以对消息队列的要求就很高了。经常会用到RabbitMq、ActivityMq、RocketMQ等消息中间件提供消息队列的能力。

普通队列

1.2 消息队列的作用

应用解耦

图1-2 消息队列的解耦功能

从可用性的角度来看,多个错误率底的子系统强耦合在一起,得到的是一个高错误率的整体系统。如图1-2,如果因为子系统的故障或者升级等原因造成暂时不可用,那么都会造成系统整体的故障,影响用户的使用体验。这时候如果变成基于消息队列的方式后,子系统之间使用队列通信,即使暂时的不可用,消息会缓存在队列中,等待系统修复后再进行相关逻辑

作为缓冲消除流量峰值

流量峰值:就像早高峰和晚高峰一样,在互联网的某些业务场景下,比如双11或者是春节抢火车票,短时间内会有大量请求进来,造成一个流量高峰。

削峰填谷

比如说一次秒杀活动,12:00:00 这一秒有1w个请求,但是12:00:00~12:10:00内平均每秒请求只有1000,如果有消息队列作为缓冲,系统只需要能支撑1000tps下不崩溃就可以,不需要加到1w tps不崩溃。

消息分发功能

在大数据时代,数据对很多公司来说就像金矿,公司需要依赖对数据的分析,进行用户画像 、精准推送、流程优化等各种操作, 并且对处理的实时性要求越来越高 数据是不断产生的,各个分析团队、算法团队都要依赖这些数据来进行工作,这个时候有个可持久化的消息队列就非常重要 数据的产生方只需要把各自的数据写人一个消息队列即可 数据使用方根据各自需求订阅感兴趣的数据,不同数据团队所订阅的数据可以重复也可以不重复,互不干扰,也不必和数据产生方关联


消息队列的分发功能

二、 RocketMq消息队列

2.1 概述

RocketMQ是一个消息中间件。rocketMq基于高可用分布式集群技术,提供消息发布订阅、消息轨迹查询、定时(延时)消息、持久化消息、资源统计、监控报警等一系列消息云服务,是目前较为优秀的消息队列中间件

2.2 架构及术语

RocketMQ各角色关系

术语说明:

角色 说明
Producer 生产者,用于将消息发送到RocketMQ,生产者本身既可以是生成消息,也可以对外提供接口,由外部来调用接口,再由生产者将受到的消息发送给MQ。
Consumer 消费者,从Broker拉取消息进行消费。从应用角度来说有两类消费者:
Broker RocketMQ服务器,也是整个服务的核心,它实现了消息的存储、拉取功能。每个broker上提供对指定topic的服务。理解了broker的原理以及它和其他服务交互的过程,也就命令消息中间件的原理,其实都大同小异。它具有2中角色 : Master:能写、能读。 Slave:只能读,不能写
Topic 消息的主题,由用于定义并在服务端配置,消费者可以按照主题进行订阅,也就是消息分类,通常一个应用一个Topic
Message 在生产者、消费者、服务器之间传递的消息,一个message必须属于一个Topic,以流传递,可以是文本消息也可以是媒体流消息
Nameserver 一个无状态的名称服务,可以集群部署,可以理解为注册中心,nameServer集群之间不互相通信
Tag 用于对消息进行过滤,理解文件message的子主题,同一业务不同目的的message可以用相同的topic但是可以用不同的tag来区分

结合上表格的术语说明可以丰富一下刚才的角色图


各角色的工作职责

1.3 消息消费者

pull与push消费方式

在RocketMQ中消息消费者分为两种,PUSH 和 PULL,大多数场景使用的是 PUSH 模式,这两种模式分别对应的是 DefaultMQPushConsumer 类和 DefaultMQPullConsumer 类。PUSH 模式实际上在内部还是使用的 PULL 方式实现的,通过 PULL 不断地轮询 Broker 获取消息,当不存在新消息时,Broker 会挂起 PULL 请求(5秒后再check),直到有新消息产生才取消挂起,返回新消息。
在 push 方式中这是通过“长轮询”方式达到 Push 效果的方法,长轮询方式既有 Pull 的优点,又兼具 Push 方式的实时性。可以在 DefaultMQPushConsumer
源码中看到有大量的 PullRequest 语句。

流量控制

pushConsumer会通过processqueue来判断获取但未被消费但消息个数,消息总大小,offset但跨度,任何一个值超过设定大小就会隔一段时间再拉去消息,从而达到控制流量但目的。

在RocketMQ里限流是自动控制的,满足下面条件会触发流量限制

  1. 当前的ProcessQueue正在处理的消息数量>1000

  2. 队列中最大最小偏移量(Offset)差距>2000

限流的做法是放弃本次拉取消息的动作,并且这个队列的下一次拉取任务将在50毫秒后才加入到拉取任务队列。

2.4 消息生产者

消息生产者是用来向Broker发送消息,默认使用DefaultMQProducer类,发送消息主要有五个步骤

  1. 设置生产者的集群(GroupName)

  2. 设置实例名(InstanceName),同一个集群下的生产者实例名不能一样

  3. 设置发送失败重试次数

  4. 设置NameServer地址

  5. 组装消息并发送

消息的发送方式有三种,同步、异步、和ignore

同步发送消息,要等消息发送成功才返回成功,成功的定义:消息是否存储到磁盘,消息是否同步到Slave,消息是否在Slave上被写入磁盘。需要结合具体配置的刷盘策略、主从策略来定。

异步发送消息, 消息发送不返回结果,结果通过设置指定的类回调处理。

忽略结果,消息发送不返回结果,也不处理 回调,不管消息发送成不成功只管发送。这种模式用在不关心消息是否发送成功,消息发送成功与否对业务没有影响或者影响很小的情况

根据实际的业务场景、性能需求选择合适的消息发送模式。

此外RocketMq支持消息延迟,在生产者发送消息时设置,消息延迟只支持固定的时间模板

(1 s/5s/1 Os/30s/I m/2m/3m/4m/5m/6m/7m /8m /9m /10m/20m/30m/12h)

时间模板可以在启动RocketMQ Broker的时候配置

2.5 消息存储(Broker)

消息存储是RocketMq的核心,分布式队列对消息可靠性有高的要求,所以数据要通过磁盘去存储,对于高性能磁盘来说,磁盘存储的顺序写速度可达600MB/s,随机写速度大概只有100Kb/s。在RocketMq里面,处理磁盘存储的思路是:尽量保证顺序写。

相应的策略是:RocketMq使用两个存储文件相互配合,一个是ConsumerQueue,另一个是CommitLog,其中CommitLog是真正的物理存储文件,而consumerQueue作为一个中间的结构只存偏移信息,下图下方为一条消息的consumerQueue的格式,由偏移量、大小、Tag的Hashcode组成的。实际情况中大部分的consumerQueue都可以直接读入内存,所以速度很快。一个topic对应多个consumerQueue。

消息存储

从上图可以看到,ConsumeQueue 中一条消息的格式为:
1.开头为CommitLog文件的Offset,通过Offset可以快速定位到消息的开头
2.第二个部分为消息的size,消息在CommitLog文件中开始和借宿的位置分别为Offset,Offset+size
3.Tag HashCode为上面属于介绍说到的属于Topic的一个子主题,用于区分同一个Topic下不同类型的消息,这里只存Tag的一个HashCode

MappedByteBuffer:“零拷贝”技术**

一般来说一台服务器把磁盘的内容发送给客户端,需要先read(file,tmp_buf,len)然后write(socket,tmp_buf,len),这两部操作包含:从磁盘复制数据到内核态内存,然后从内核态内存复制到用户态,从用户态内存复制到网络驱动的内核态内存,最后复制到网卡中进行传输,而使用Java 7中MappedByteBuffer,可以省去向用户态的内存复制,提高速度。

总的来说,RocketMq的存储机制使用了顺序写和随机读,顺序写使用CommitLog +ConsumerQueue实现,提升了写入效率。虽然读取是随机的,但是可以利用操作系统的pagecache机制,可以批量的从磁盘读取,作为cache存到内存中,加速后续的读取速度。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • RocketMQ 本文内容:描述RocketMQ的概念与术语,最下方解释各种MQ之间的区别与选型 RcoketMQ...
    严重思想跑偏患者阅读 3,547评论 0 1
  •   今年的一个周末,去参加了一场rocketMq的meet up分享,由此对rocketMq产生了极大的兴趣,ro...
    左小星阅读 15,769评论 7 28
  • 核心组件(4个组件+消息存储结构) 客户端消费模式 1. MQ的使用场景 昨天在写完之后,有些读者在评论中提出:到...
    楼亭樵客阅读 1,004评论 0 3
  • 消息中间件需要解决哪些问题? Publish/Subscribe 发布订阅是消息中间件的最基本功能,也是相对于传统...
    壹点零阅读 1,599评论 0 7
  • 本来想将broker和client分开写。但是他们的每个功能都是共同协作完成的,写broker的时候,难免会涉及到...
    msrpp阅读 3,488评论 1 7