RocketMQ 1.学习资料 2.面试题 3.知识点

前言

这篇文章分三方面来介绍RocketMQ
1.学习资料
2.面试题
3.知识点

学习资料

官方

官方文档
rocketmq.apache.org/docs/simple…

官方github
github.com/apache/rock…

网络资料

浅谈消息队列及常见的消息中间件
juejin.im/post/5b41fe…


源码分析
RocketMQ源码分析之消息发送
zhuanlan.zhihu.com/p/58026650
zhuanlan.zhihu.com/p/58728454

github.com/YunaiV/Blog…

blog.csdn.net/u013160932/…

blog.csdn.net/prestigedin…


消息中间件—RocketMQ消息消费(一)
www.jianshu.com/p/f071d5069…

m635674608.iteye.com/category/32…

github.com/javahongxi/…


自己实现消息中间件
zhuanlan.zhihu.com/p/27693508


www.infoq.cn/article/201…

yq.aliyun.com/articles/69…

blog.csdn.net/a417930422/…


阿里中间件
jm.taobao.org/tags/Rocket…

Apache RocketMQ背后的设计思路与最佳实践 jm.taobao.org/2017/03/09/…

大厂最佳实践

微众银行-消息服务平台建设实践 mp.weixin.qq.com/s/sqBtQNZ6F…

滴滴出行基于 RocketMQ 构建企业级消息队列服务的实践 www.infoq.cn/article/dXJ…

书籍

1.入门
《RocketMQ实战与原理解析》 book.douban.com/subject/302…

2.实现原理
《RocketMQ技术内幕:RocketMQ架构设计与实现原理》 book.douban.com/subject/304…

面试题

如何解决高性能读写数据的问题?

内存和文件映射,减少数据的复制。


参考
blog.csdn.net/fcbayernmun…

如何解决数据重复发送的问题?

由于网络本身的原因,发送成功但是没有收到成功响应,于是认为失败就再发送一次,这是不可避免的。那怎么办?万一有问题,消费者幂等。


同步是如何收到响应的?异步是如何收到响应的? 同步和异步指的是API调用。和消息发送的成功响应是两回事,具体实现是,有另外的监听线程。


参考
blog.csdn.net/gwd11549783…

如何解决数据重复消费的问题?

重复消费和重复发送是同一个问题,因为数据重复发送,才导致重复消费。

如何确保数据按顺序消费的问题?

一般是topic的某几个数据,要确保顺序消费,要做到两点:
1.顺序写入同一个消息队列
2.同一时间,只能有一个线程消费同一个消息队列,也就是说,要对同一个消息队列进行同步处理

具体怎么做?
写的时候,判断orderId,如果是同一个orderId,那么写入同一个消息队列,而不是随机选择。
读的时候,同步处理即可。

消息重试?

如果发送的时候,失败,没有收到成功响应,那么重试。具体是,一段时间内,定时重试几次。

高可用?

集群 + 负载均衡

两种消费模式?

1.集群
2.广播

常用是集群,集群就是一个消费者组对主题的同一个数据只消费一次。 广播是,同一个组的每个消费者都会消费一次。

同步异步?

指的是发送数据的时候,是同步,还是异步。

最终一致性?

消息中间件要解决的问题就是非实时数据的最终一致性问题。

批量发送数据?批量发送数据如何解决一致性?

事务?

什么是事务消息? 事务消息就是,1.发送成功2.其他操作成功3.第二次发送commit。即二次提交,二次提交的原因是因为有其他操作,其实就是类似于分布式事务,有两个sql操作在不同的机器,逻辑基本上是一样的。


支付的分布式事务是这样的1.第一个操作成功2.第二个操作成功3.提交事务。但是具体实现的时候是,分布式事务是分布式事务,消息是消息,这两个是分开的,这里说的分开指的是没有把消息放在分布式事务里,也就是说分布式事务的组成不包含消息,是否发送消息只是一个简单的if else判读,分布式事务成功commit就发送消息,否则不发送消息。

支付的消息是广播,就是同一个消费者组都会消费这个消息,主要就是商家和代理商系统。


最新版本才支持事务消息。由于事务消息功能支持的不好,因为与高性能相矛盾,好像又撤了。

索引?

消息队列就是对物理数据的索引。

注册中心?

自己实现的注册中心。

消息丢失?

三个方面
1.写
2.存储
3.读


确认机制
1.生产者写丢失
为什么会丢失?怎么解决丢失问题? 由于网络原因,丢失。怎么解决?没有收到成功响应,重发。

2.消费者读丢失
有确认成功机制。


写丢失?
1.同步
如果是同步,返回写成功。
2.异步 如果是异步,如何知道是否写成功?也是返回,只不过异步的话,就是通过回调机制。


参考
www.jianshu.com/p/3213d8c29…

刷盘

由于消息存储这一块使用了文件内存映射机制,所以持久化的时候有两种机制:
1.同步
2.异步

同步,就是立即把内存数据写到映射文件。
异步,就是不是立即写。

消费消息确认

消费者消费消息的时候,和broker之间有确认成功机制,即消费者消费成功,就写消费成功到broker,确保每条消息至少被消费一次。

如果由于消费者本身消费异常,导致失败,那么重复消费。具体是,使用retry机制,就是把数据丢到retry主题队列,retry主题名字由retry主题名字组成。

如果由于网络原因,导致确认失败,那么消费者将会重复消费。怎么解决?消费者幂等处理重复消息。

注:这里的所谓的确认成功,具体实现是,如果消费者成功,就返回成功给broker;如果不成功,就返回reconsume给broker。
和tcp的三次握手连接成功机制不一样。


参考
blog.csdn.net/gwd11549783…

复制

就是从复制主的数据。
复制很简单,其实就是两个系统之间的通信,怎么通信?就是tcp套接字连接


主挂了,从晋升为主,这个是怎么实现的?
不管是主还是从,其实都是broker,broker启动的时候都注册到注册中心。
生产者写的时候,流程是:生产者——注册中心:获取broker集合,负载均衡选择一个——broker。broker和注册中心保持通信,broker挂了,注册中心删除该broker,所以生产者写数据到从即可。


如何保证主从复制时数据的一致性问题?
既然是复制,就有时间延迟,这是不可避免的。只能尽量优化复制效率。

知识点

一些概念

1.主题
相当于是数据集合

2.生产者
作用是写数据

3.消费者
作用是读数据

4.消息队列
对应的类是MessageQueue,包含字段:1,哪一个机器 2,哪一个逻辑文件 3,哪一个物理文件。这样就可以唯一定位到对应的数据。

注:
物理数据文件是主题文件,按实际数据结构存储实际数据,目录是commitlog目录/主题名字/XXX。

逻辑数据文件是消息队列文件,按逻辑数据结构存放逻辑数据(映射到实际文件的实际数据),目录是consumequeue/queueid/XXX。

5.注册中心
作用是协调broker。

RocketMQ自己实现。没有使用Zookeeper,因为引入第三方中间件,会带来不必要的依赖和不稳定的麻烦,越简单越好够用就好。

注:RocketMQ服务器叫broker,broker的角色分主和从。

6.集群
比如二主二从二注册中心

7.主备
比如一个机器上一主一从

一个主题为什么要弄成多个消息队列?

把一个物理的主题变成逻辑的消息队列集合(包含n个消息队列),目的是间接地实现主题的分布式存储,间接的意思是指不是直接把主题分布式的存储在多个机器,而是多个消息队列分布在多个机器。
1.存放数据的物理主题文件只有一个
2.逻辑消息队列有多个,分布式存储在不同的机器。

每个消息队列包含了哪一个机器字段。所以,如果有的消息队列不是存储在本地机器,也可以通过消息队列的哪一个机器字段找到物理主题的那个机器。


多个消息队列文件的数据是一样的吗?
不一样。因为生产者每次写数据到逻辑文件的时候,是随机选择一个逻辑文件,所以每个数据只存放在被选中的那个逻辑文件。


生产者如何写数据?
流程
生产者——随机选择一个逻辑数据文件——物理数据文件

详细步骤
1.生产者
2.逻辑数据文件 随机选择一个逻辑数据文件(消息队列),即从消息队列集合随机选择一个消息队列
3.物理数据文件 写数据到broker

消费者如何读数据?
流程
消费者——遍历逻辑数据文件——物理数据文件

详细步骤
1.消费者
2.遍历逻辑数据文件集合(即消息队列集合)
遍历消息队列集合,多次请求broker。其中必有一个消息队列,可以找到物理数据文件。
3.物理数据文件
从broker读数据

什么是消息队列id?

唯一标识消息队列。
目录是consumequeue/queueid/XXX。

物理数据结构和逻辑数据结构
集群

架构图

注意:
1.broker和注册中心互相通信
2.注册中心之间不通信
注册中心如果挂了,不会通知broker;
当broker再次请求时,获取注册中心集合时才知道注册中心挂了。

架构图

集群


通信流程


消息队列流程图


类图


物理数据
主要字段
1.偏移量
2.大小

偏移量是加上大小


逻辑数据
主要字段
1.偏移量
2.大小

偏移量是自增1

逻辑数据所在磁盘位置

数据内容


索引

多个消费者如何消费数据?broker如何保存每个消费者的进度?

1.逻辑数据偏移量
2.物理数据偏移量

笔记

注册中心不需要持久化数据,数据都是在内存。数据就是ip。

IP的作用就是用来路由服务器的。

所谓路由就是负载均衡选择一个服务器。


所有实时的数据都不需要持久化,非实时的数据才需要持久化。

比如,消息中间件的数据,写和读是不在同一个时刻,当然不能太慢,而是说允许几十秒到几分钟的延迟。所以中间件的数据需要持久化。

RPC都是实时调用,不存在什么持久化。

注册中心的数据,都在内存,实时调用,实时通信,不存在持久化。


负载均衡算法就3种 1.余数 2.一致性Hash环 3.槽

余数,就是用Hash除以机器数量,得到一个余数。但是有个问题,不够均衡。

一致性Hash环,主要是为了解决增加一个节点或减少一个节点带来的问题。最终实现,某个节点的加或减,影响的数据有限。但是有个问题,节点少的情况下,每个节点的数据多,那么增或减一个节点,影响的数据比较多。所以,解决了均衡的问题,但是没有解决节点少这种情况的均衡问题。

槽,主要是解决节点少不均衡的问题,那我们换个思路,就是增大节点的数量,具体做的时候不能增大实际节点的数量,那么就只能增大虚拟节点的数量,这个虚拟节点算是一个中介,流程是:数据-中介节点-实际节点。


服务器和注册中心具体是怎么通信的?其实就是调用方法,和别的没有任何区别,什么心跳啊 心跳包啊,都一个意思,不管叫什么名字,都是调用方法,输入数据和输出数据。


1.余数 增加一个节点或减少一个节点,影响数据太多。 2.一致性Hash 增加一个数据或减少一个节点,影响数据比较少。 但是,如果节点比较少,那么还是影响的数据比较多。 3.中介槽 即便是节点比较少,影响的数据也比较少。 通过引入中间节点的办法。中间节点1万个,这样单个节点影响的数据比较少。


所谓长连接,就是你不断开,我也不断开。 TCP套接字,有个close方法,主要不主动调用关闭方法,套接字就一直处于连接状态,这就是长连接。


zookeeper注册中心是有把数据持久化到磁盘的,比如,RPC dubbo和zookeeper搭配使用。

为什么呢?因为RPC的数据是非实时,RPC的数据就是IP-port-类-方法。生产者写数据,消费者稍后才调用方法。


所谓幂等,就是在处理数据的时候先验证之,如果已经处理过,比如,和数据库的数据比较,如果已经存在,那么就直接返回,不做处理,这样就相当于解决了数据的重复处理的问题。

支付场景也有这种需求。

消息中间件也有这种需求,解决办法的思路是一样的,就是先校验之,如果数据库里已经有了数据就直接返回不做处理。


支付幂等场景?支付是如何解决幂等的?


消息中间件生产者写数据重复?为什么会重复?


消息中间件消费者如何解决读重复数据的问题?消费者幂等。

幂等的前提是,当前数据在数据库存在,也就是说,必须有一个ID字段相同,才能知道是幂等的。

支付的幂等是,扫码的时候,order和orderId已经生成。后面点击提交按钮多次提交或者点击支付按钮多次支付,在后头始终是一个order和orderId。


下游商家来了一个订单数据,支付公司生成自己的订单数据,缓存数据库里有ID/order,ID什么时候产生的? 对于扫码支付而言,不是下游商家,而是微信客户端。下游商家只是二维码url的一个字段-商家号。根本就没有什么下游商家。


不是order的ID,是UUID,这个UUID在服务器和客户端,具体的流程是这样的,我们只需要关注两个步骤: 1.点击提交按钮,提交订单 2.点击支付按钮,支付订单

第一步,服务器生成UUID,缓存数据库保存UUID/order。

以上是错的。

两个重要步骤是1.扫码2.点击支付按钮。二维码是服务器的一个请求url,包含了商家号字段。


扫码支付的流程


消息中间件 生产者发写数据,为什么会重复? 断网是不可避免的,这个是由网络的硬件物理特性决定的,只能尽量避免减少损失。那玩游戏是怎么处理这种实时的对网络性能要求极高的情况? 断网。写了两次,因为也没有对方的回应,所以就再写了一次。


生产者写的时候 重复?主要是重复什么呢?就是两条一模一样的数据被写入了物理数据文件。

那么消费的时候,要解决重复消费的问题,怎么解决?幂等。由于两个数据的ID字段一样,所以,处理过了就不再处理。


网络波动是什么? 就是断网了。临时断网了。


网络不可达是什么? 断网了


网络波动、跳Ping、丢包、掉线 cs.xunyou.com/html/39/1/4…


什么是顺序消费? 就是有的数据,因为业务上的要求,必须要按顺序消费,比如,订单1.未支付2.已支付。

顺序消费分类?从数据量来划分1.所有数据,即所有主题的所有数据2.某个主题的数据3.某个主题的包含同一个字段值的某几个数据,比如,order主题里面只有包含了相同orderID的数据才需要顺序被消费,不同的order是没有必要按顺序来消费的。

一般不会所有topic,而是topic的某几个数据。 同一个消息队列加锁,同一时间只有一个消费者能够消费该消费队列的数据。


如何解决顺序消费?
如果是所有topic,那么所有topic 即topic集合的数据结构应该是队列,先进先出,这样就保证了所有topic的顺序。如果是topic下面的数据,那么同一个消息队列的数据同步,同一时间只能有一个线程消费。


网络的稳定性非常重要,不然打游戏都打不了。 1.宏观 2.微观

宏观就是不能断网。

微观就是网络硬件本身的特性决定的,不能做到100%,总有一个丢包率,总有一个网络抖动的时刻,但是基本上影响不大。


生产者和消费者分别是如何选择消息队列的?负载均衡。 首先,生产者是负载均衡,之所以弄一个中间的东西出来也就是消息队列出来,目的也是为了分布式。 消费者是根据什么知道数据是在哪一个消费队列? 肯定是消息的某个唯一标志和消息队列唯一标志关联了起来。


集群-消息队列的数量和节点broker的数量 几种情况
1.少
2.一样
3.多

节点的数量少,一个节点多个消息队列。
一样,一个节点一个消息队列。
节点多,有的节点就没有消息队列,即没有流量请求。如果是后面增加的节点,那么就是后面增加的节点都没有流量请求。

高级知识点

多中心多活、灰度发布、熔断机制、消息存活期、流量的权重、消息去重、惊群效应问题的解决、背压模式、消息服务治理、MQTT消息服务

搭建环境

步骤
1.先启动注册中心
2.再启动服务器

先启动注册中心,再启动服务器broker,broker就注册到了注册中心,生产者写数据的时候,就从注册中心获取broker集合,负载均衡选择一个broker,往里面写数据即可。 启动完成注册中心和broker之后,

消息中间件的设计目标

高可用
数据必须至少被发送一次

基于确认成功机制,如果不成功,重试。


数据重复发送
如果因为网络原因,导致数据重复,只能在消费端做幂等处理。

数据必须至少被消费一次

基于确认成功机制,如果不成功,重试。


数据重复消费
如果因为网络原因,导致消费两次,也是一样在消费端做幂等处理。

几大核心模块

1.注册中心
2.broker
3.生产者
4.消费者

注册中心先启动,然后再启动broker,broker注册到注册中心。
生产者从注册中心获取broker集合,负载均衡选择一个broker,写数据到broker。
消费者根据主题名字 + 消费者组名字,找到是哪一个消息队列(逻辑数据),从而找到物理数据。

画一个图

逻辑数据

磁盘上的逻辑数据

消息队列目录

{
	"offsetTable":{
		"TopicTest@orderG2":{0:31,1:32,2:33,3:32
		}, //主题名字@组名字:{消息队列编号:偏移量}
		"%RETRY%orderG@orderG":{0:0
		},
		"TopicTest@orderG":{0:31,1:32,2:33,3:32
		},
		"%RETRY%orderG2@orderG2":{0:0
		}
	}
}
复制代码

注:偏移量是在逻辑数据文件里的偏移量,根据偏移量,可以找到哪一个逻辑数据。


数据结构
包含主要字段
1.物理偏移量
2.大小

物理偏移量定位到哪一个数据的起始位置,大小定位结束位置。


消费者是如何定位到哪一个消息队列的?
消息/消息队列存储在注册中心。

物理数据

注册中心

所有的节点,包括broker和生产者/消费者,都要和注册中心通信,各自的通信想要获取的数据也不同。但有一点,注册中心的数据一样,所以解决了单点挂了的问题。

索引

数据结构
是hashmap,不是树。

与kafka的区别

编程语言
RocketMQ编程语言是java。


注册中心
RocketMQ是自己开发。
kafka是zookeeper。

主从

目前主从,如果主挂了,从只能读。没有实现自动晋升从为主。如果要实现自动晋升,需要开发者自己实现。

文件内存映射

作用是节省数据复制的次数,从而提高数据的读写速度。

具体是怎么节约的?细节?直接把磁盘文件和用户进程缓冲区内存建立联系,数据不经过内核的缓冲区,减少了一个环节的数据复制,即内核进程缓冲器和用户进程缓冲区二者之间的内存的数据复制。


参考
blog.csdn.net/fcbayernmun…

单机数据量

万级别

架构

RocketMQ的整体架构设计
下图为大家清晰地展示了RocketMQ的几个组件,分别是nameserver、broker、producer和consumer。nameserver主要负责对于源数据的管理,包括了对于Topic和路由信息的管理,broker在启动的时候会去向nameserver注册并且定时发送心跳,producer在启动的时候会到nameserver上去拉取Topic所属的broker具体地址,然后向具体的broker发送消息。

注意,源数据,包括:
1.路由
2.topic 由此二者可以使得生产者和消费者找到哪一个消息队列和哪一个broker的物理数据。


RocketMQ的技术概览 请配图同时对RocketMQ的架构进行说明;请对消息的产生、传输和消费流程进行说明,最好也可以配图。

在我们看来,它最大的创新点在于能够通过精巧的横向、纵向扩展,不断满足与日俱增的海量消息在高吞吐、高可靠、低延迟方面的要求。

目前RocketMQ主要由NameServer、Broker、Producer以及Consumer四部分构成,如下图所示。

所有的集群都具有水平扩展能力,无单点障碍。

NameServer以轻量级的方式提供服务发现和路由功能,每个NameServer存有全量的路由信息,提供对等的读写服务,支持快速扩缩容。

Broker负责消息存储,以Topic为纬度支持轻量级的队列,单机可以支撑上万队列规模,支持消息推拉模型,具备多副本容错机制(2副本或3副本)、强大的削峰填谷以及上亿级消息堆积能力,同时可严格保证消息的有序性。除此之外,Broker还提供了同城异地容灾能力,丰富的Metrics统计以及告警机制。这些都是传统消息系统无法比拟的。

Producer由用户进行分布式部署,消息由Producer通过多种负载均衡模式发送到Broker集群,发送低延时,支持快速失败。

Consumer也由用户部署,支持PUSH和PULL两种消费模式,支持集群消费和广播消息,提供实时的消息订阅机制,满足大多数消费场景。

写数据和读数据的几个重要问题

从发送端和接收端来看有如下图所示的几个比较重要的部分。

首先对于发送端而言,需要重点关注是三个部分,一个就是发送失败了怎么办,这里就是send backoff也就是所谓的一种补偿。那么发送失败了,怎样进行补偿呢?比如现在的机制是三次补偿,需要设置每次间隔多少秒,这是就一种很好的补偿机制。还有就是send reliability,也就是发送的可靠性。第三块就是send oneway(即单向发送),也就是在一些大数据场景下不需要响应的情况下,可以采用send oneway的方式来迅速地提高吞吐量。

而对于消费者端而言,有几块也需要重点关注。一个是重复消费问题,一般情况下推荐大家通过存储介质探重。而如果消费失败了或者消费速度太慢怎么办,有什么样的方式可以解决呢?一种方案就是通过并发去解决,可以通过提高并发的通道实现。第三块就是consume batch的问题,在RocketMQ里面默认的batch是32,如果大家觉得自己消息引擎吞吐量比较低就可以提高batch,但是如果不对于服务端的限制进行调整,batch最多也就只能达到32。


总结
1.补偿
补偿机制,就是未成功,就retry。
2.单向发送
就是只管写数据,不需要返回。
3.存储介质探重
就是判断数据库是否存在,如果存在,做幂等处理。所谓幂等,就是数据存在,那么直接返回,什么都不做。

调优

接下来分享一下阿里巴巴内部对于RocketMQ服务端性能调优的几点经验总结。

这几点经验其实都与I/O相关,首先如果要进行I/O操作,那么就无法避免文件句柄的调优,这个是达到一个高吞吐必备的系统调优。
其次CPU的亲和特性,包括线程亲和CPU,内存亲和CPU,网卡亲和CPU,这里面非常有学问。
第三部分就是如何突破内存锁的限制,无锁设计是我们所推崇的。
第四部分就是NUMA架构Disable问题。这里简单提一下NUMA架构,它主要是为了解决CPU访问内存的响应性能问题。因为由于所有CPU都是通过一个内存控制器来读取内存,随着CPU核不断发展,其响应时间上的性能问题越来越明显。
于是,NUMA架构出现了。NUMA会为每个CPU核绑定一部分内存,每次在CPU中运行的线程会优先地去访问该CPU所对应的内存,而当内存不足的时候,会优先把CPU相对应的内存释放掉。NUM这种默认的内存分配算法对于存储类产品而言是存在问题的,所以建议大家对于这一部分进行Disable,当然业界也有很多其他的解决方案,比如打开NUMA中的交叉分配方案。

而通过很多的线上观察,我们发现在线程处理来自外部的请求的时候,对于内存的访问其实都是随机的,所以交叉分配方案可以使得线程对于内存的访问进行打散,具有一定程度上的提升。还有一部分就是PageCache flush调优,因为MQ的存储其实是一个文件系统,那么就避免不了与PageCache打交道,而PageCache里面就有很多可以进行调优的,包括脏页回写,换页机制等,这些都是进行调优的重点。

最后一部分就是对于磁盘的I/O调度的调优,大家都知道在磁盘的I/O调度方面,Linux操作系统默认为我们提供了四种方式,而通过线上的观察以及实验建议大家将默认的调度方式变为Deadline方式,也就是最后期限调度算法,这样就可以有效地避免I/O写请求饿死情况的出现。

消息中间件的两大作用

2.2 低延迟探索之路
RocketMQ作为一款消息引擎,最大的作用是异步解耦和削峰填谷。一方面,分布式应用会利用RocketMQ来进行异步解耦,应用程序可以自如地扩容和缩容。另一方面,当洪峰数据来临时,大量的消息需要堆积到RocketMQ中,后端程序可以根据自己的消费速度来进行数据的读取。所以保证RocketMQ写消息链路的低延迟至关重要。

在今年双11期间,天猫发布了红包火山的新玩法。该游戏对延迟非常敏感,只能容忍50ms内的延迟,在压测初期RocketMQ写消息出现了大量50~500ms的延迟,导致了在红包喷发的高峰出现大量的失败,严重影响前端业务。下图为压测红包集群在压测时写消息延迟热力图统计。

转载于:https://juejin.im/post/5cd980f2f265da035c6be685

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值