【中间件】RocketMQ问答合集

1.为什么要引入MQ(好处)

异步、解耦、削峰

MQ有一个非常好的优势,就是可以把原本必须串行执行的业务并发进行,比如用户注册后,进行注册邮箱验证、注册手机号验证、增加积分等操作,使用消息队列可以在注册后就返回成功,把这些操作都分给不同消息队列,实现“以空间换时间”。

2.MQ缺点

系统可用性降低

任何集群的可用性都是<1的,因此引入MQ需要尽可能保证其可靠性

系统复杂度增高、学习成本增大

消息一致性问题

系统A处理完业务后发消息给B、C,发给B的消息失败了,发给C的消息成功了,该怎样处理?(rocketMQ支持事务消息,完美解决分布式系统中消息不一致问题)

3.MQ对比

RocketMQ的生产者不仅作为客户端来发送消息,它有生产者组的概念,在事务消息的时候,消费者可以查看消息的生产者,如果此时生产者崩溃,可以在本组其他生产者那里查询以提交或者回溯消费。

3.消息堆积可能的原因和解决方案

(RocketMQ建议整个APP的所有消息放在同一个Topic下,以Tag等做区分,可见RocketMQ对自己消息堆积处理的自信,放在同一个Topic下可能是利于更灵活的调度消费队列)

在以下场景我们需要介入处理消息堆积问题:

  • 业务系统上下游能力不匹配造成的持续堆积,且无法自行恢复。
  • 业务系统对消息的消费实时性要求较高,即使是短暂的堆积造成的消息延迟也无法接受。

正常情况下,系统要保障消费能力>生产能力,就不会产生积压,消息堆积的主要瓶颈在于本地客户端的消费能力,即消费耗时和消费并发度,几乎不会在MQ。

1.突发流量高峰导致大量消息进入,下游来不及处理

        解决:增加消费者、消费队列、增加并发度(调整单节点的线程数到上限,扩容节点)

                增加下游配套消费设施,防止出现新的性能瓶颈

                适当做一些消费降级,将一些不太重要但是产生大量消息的功能暂时关闭、对消息写入进行限流、流量控制,一些时效性可以降低的消息整合为批量消息

2.消费端存在大量IO:如大量读写mysql、redis,调用外部http接口,有时候瓶颈在mysql读写能力

        解决:找到下游瓶颈并提升,比如减少BD交互次数、增加mysql处理能力、把一些可以异步的业务写成异步、批量消费消息、跳过丢弃非重要消息、优化每条消息消费过程,提高消费并行度、消费失败不要立刻重试,需要分析原因,对整个队列延迟消费或者对失败消息延迟消费

3.消费端业务代码存在无限循环、递归,存在无法消费的消息,消费不掉就无限次重试

        解决:查看消息轨迹、日志,确保不存在消费错误、无限重试的消息,确保不是由于业务代码死循环带来的消费卡壳,增加报警信息

4.记录消费日志,对性能有一点点影响,消息量不是特别特别大时推荐开启,对排查消息堆积延迟原因非常友好

参考:

阿里云:https://help.aliyun.com/document_detail/193875.html

                https://help.aliyun.com/document_detail/193952.html

CSDN:https://blog.csdn.net/weixin_30507481/article/details/101116372

B站:https://www.bilibili.com/video/BV1ab4y1o77r?p=22&spm_id_from=pageDriver

消息堆积会影响读取速度吗

页缓存(PageCache)是OS对文件的缓存,用于加速对文件的读写。一般来说,程序对文件进行顺序读写的速度几乎接近于内存的读写速度,主要原因就是由于OS使用PageCache机制对读写访问操作进行了性能优化,将一部分的内存用作PageCache。对于数据的写入,OS会先写入至Cache内,随后通过异步的方式由pdflush内核线程将Cache内的数据刷盘至物理磁盘上。对于数据的读取,如果一次读取文件时出现未命中PageCache的情况,OS从物理磁盘上访问读取文件的同时,会顺序对其他相邻块的数据文件进行预读取。

在RocketMQ中,ConsumeQueue逻辑消费队列存储的数据较少,并且是顺序读取,在page cache机制的预读取作用下,Consume Queue文件的读性能几乎接近读内存,即使在有消息堆积情况下也不会影响性能。而对于CommitLog消息存储的日志数据文件来说,读取消息内容时候会产生较多的随机访问读取,严重影响性能。如果选择合适的系统IO调度算法,比如设置调度算法为“Deadline”(此时块存储采用SSD的话),随机读的性能也会有所提升。

4.RocketMQ如何保障消息可靠性(不丢失)

影响消息可靠性的几种情况:
1) Broker非正常关闭
2) Broker异常Crash
3) OS Crash
4) 机器掉电,但是能立即恢复供电情况
5) 机器无法开机(可能是cpu、主板、内存等关键设备损坏)
6) 磁盘设备损坏

1)、2)、3)、4) 四种情况都属于硬件资源可立即恢复情况,RocketMQ在这四种情况下能保证消息不丢,或者丢失少量数据(依赖刷盘方式是同步还是异步)。

5)、6)属于单点故障,且无法恢复,一旦发生,在此单点上的消息全部丢失。RocketMQ在这两种情况下,通过异步复制,可保证99%的消息不丢,但是仍然会有极少量的消息可能丢失。通过同步双写技术可以完全避免单点,同步双写势必会影响性能,而且目前版本主备不能自动切换,适合对消息可靠性要求极高的场合,例如与订单交易、金融相关的应用。

生产消息:可以采用同步消息、异步消息保障消息生产的可靠性,也有事务消息可以保障在分布式系统中的最终一致性

                同步消息失败会重投(切换broker,重投失败会报错),异步消息失败会重试(不切换broker,重试达到超时就不再重试)

                如果业务对消息可靠性要求比较高,建议应用增加相应的重试逻辑:比如调用send同步方法发送失败时,则尝试将消息存储到db,然后由后台线程定时重试,确保消息一定到达Broker。

(ps:重试时不改变messageId,重投会改变,也就是重投虽然内容一样但不是同一条消息了)

(官方解释上述db重试方式为什么没有集成到MQ客户端内部做,而是要求应用自己去完成,主要基于以下几点考虑:首先,MQ的客户端设计为无状态模式,方便任意的水平扩展,且对机器资源的消耗仅仅是cpu、内存、网络。其次,如果MQ客户端内部集成一个KV存储模块,那么数据只有同步落盘才能较可靠,而同步落盘本身性能开销较大,所以通常会采用异步落盘,又由于应用关闭过程不受MQ运维人员控制,可能经常会发生 kill -9 这样暴力方式关闭,造成数据没有及时落盘而丢失。第三,Producer所在机器的可靠性较低,一般为虚拟机,不适合存储重要数据。综上,建议重试过程交由应用来控制。)

保存消息:同步刷盘、同步双写

消费消息:RokcetMQ可以保证消息至少被消费一次

注:RocketMQ从3.0版本开始支持同步双写。  

5.如何保证消息幂等性

首先RocketMQ(和其他各种MQ)无法避免消息重复,所以重点不在避免重复,而是即使多次重复也可以保证和消费一次的效果相同。

所以如果业务对消费重复非常敏感,务必要在业务层面进行去重处理。可以借助关系数据库进行去重。首先需要确定消息的唯一键,可以是msgId,也可以是消息内容中的唯一标识字段,例如订单Id等。在消费之前判断唯一键是否在关系数据库中存在。如果不存在则插入,并消费,否则跳过。(实际过程要考虑原子性问题,判断是否存在可以尝试插入,如果报主键冲突,则插入失败,直接跳过)

msgId一定是全局唯一标识符,但是实际使用中,可能会存在相同的消息有两个不同msgId的情况(消费者主动重发、因客户端重投机制导致的重复等),这种情况就需要使业务字段进行重复消费。

6.Dledger自动选举过程

Raft算法

常用的三节点集群选举过程:

Raft协议多副本消息同步:

(redis集群、哨兵模式选举也是用的Raft算法,不过选举的依据是自己配置的优先级id、与master偏移量等条件

Raft、zab都源自paxos,但是略有不同,更详细的:https://blog.csdn.net/qq_40994017/article/details/90749940

7.namesrv

名称服务充当路由消息的提供者,相当于kafka中的zookeeper。生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但相互独立,没有信息交换,只要有一台存活就可以继续提供服务。

8.怎样保证消息存储的高效

顺序读写:避免每次随机存取数据,而是事先申请一块较大内存区(之前配置时特意提到过默认4M之类需要根据自己电脑配置更改),顺序写速度比随机写高出6倍,加上现在很多高速硬盘,写的速度完全可以赶上网络IO速度。

零拷贝:原本一次读操作需要将磁盘文件copy到内核态,再copy到用户态,再copy到网络驱动的内核态,再copy到网卡的内核态,四次copy过程。

(为什么需要这样呢?因为你用户没有系统底层操作权限,所以用户想要执行的操作需要让内核代劳)

但是RocketMQ采用MMap映射的方式,内核态向用户态不是copy数据而是copy存储位置指针,JVM执行操作后可以直接在内核态copy到网卡,全程copy两次。

(MMap文件大小最大1.5-2G(这就是存储消息的commitLog文件限制1G大小的原因),还有一种零copy方式是sendfile,直接走DMA,磁盘->网卡,全程无需拷贝,但是对文件操作不够灵活,kafka用的就是sendfile,https://www.xttblog.com/?p=5101

异步刷盘

9.消息存储结构

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值