RocketMQ个人理解

本文深入剖析RocketMQ中间件,探讨其在消息重复消费、消息丢失、高可用性等方面的问题解决方案。包括Producer的同步、异步、单向发送策略,Broker的刷盘策略和主从复制机制,Consumer的幂等性处理,以及NameServer和Broker的高可用设计。此外,还介绍了消息顺序消费和RocketMQ在系统解耦、削峰填谷中的作用。
摘要由CSDN通过智能技术生成


高并发系统:

  1. 系统拆分 2. 缓存 3. MQ
    4. 分库分表 5. 读写分离 6. ElasticSearch

MQ==》解耦、异步、削峰

通过MQ,Pub/Sub发布订阅消息的一个模型,系统之间就可以进行解耦合了

剖析项目

一个模块,调用了那些系统或模块,相互之间调用关系,维护起来很麻烦,只要这个调用不需要直接同步调用接口,用MQ进行异步化解耦===》MQ做解耦

MQ缺点解决

MQ缺点解决:如何保证RocketMQ高可用、一致性问题、系统复杂度提高了(如何保证消费没有重复消费—>幂等性操作、消息丢失情况)

保证消息不被重复消费

  1. Redis天然幂等性
  2. 数据库查看

消息不丢失

  • Producer阶段

    方案一:send的三种方法

    • 同步发送
      发消息的时候会同步阻塞等待broker返回的结果,如果没成功,则不会收到SendResult,这种是最可靠的
    • 异步发送
      异步发送,再回调方法里可以得知是否发送成功
    • 单向发送
      最不靠谱的一种发送方式,我们无法保证消息真正可达
//{@link org.apache.rocketmq.client.producer.DefaultMQProducer}

// 同步发送
public SendResult send(Message msg) throws MQClientException, RemotingException, 	 MQBrokerException, InterruptedException {}

// 异步发送,sendCallback作为回调
public void send(Message msg,SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {}

// 单向发送,不关心发送结果,最不靠谱
public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {}

​ 方案二:失败或超时,自动重试发送,默认重试三次
​ 方案三: 多Master多Slave

Producer保证发送阶段消息可达: 同步发送+自动重试机制+Master节点

  • Broker存储阶段

    Broker先把消息放入内存,然后根据刷盘策略持久化到硬盘中,收到Producer,再读到内存中,若此时异常宕机,导致消息丢失?

    • 方案一:修改刷盘方式
      MQ持久化分为同步刷盘和异步刷盘,默认异步刷盘
      配置

      //默认情况为 ASYNC_FLUSH,修改为同步刷盘:SYNC_FLUSH,
      //同步刷盘效率不如异步刷盘高。异步默认10s执行一次
      flushDiskType = SYNC_FLUSH
      
    • 方案二: 主从模式
      根据业务判断,是否是Master刷盘通知Producer还是Master和Slave同时刷完盘再通知Producer

      ## 默认为 ASYNC_MASTER
      brokerRole=SYNC_MASTER
      
  • Consumer存储阶段

    • 方案一
      消费者把消息拉取到本地,业务逻辑执行完成后手动sck确认,代表真正消费完成

    • 方案二

      消息消费失败自动重试,若消费消息失败,没有ack确认,自动重试,配置好重试策略和次数即可

RocketMQ保证队列完全顺序消费

生产者将消息顺序发往同一个queue,依靠先进先出机制FIFO===》消息的消费顺序和产生顺序相同

  • 全局顺序消费
    设置读写队列数量为1,保证了并发时,消息消费的顺序性----》系统吞吐量低

  • 局部顺序消费
    消息顺序消费—> 1.消息顺序发送 2.消息顺序存储 3.消息顺序消费,满足这三点即可

    1. 消息顺序发送,多线程发送的消息无法保证有序性,但是同一个业务的消息需保证再一个线程内顺序发送
    2. 消息顺序存储,MQ的topic下存在多个queue,要保证消息的顺序存储,同一个业务编号需发送到一个queue中(使用MessageQueueSeletor来选择要发送的queue,一般对业务编号hash,然后根据队列数量hash值取模,将消息发送到一个queue)
    3. 消息顺序消费,保证同一个queue只能被一个消费者消费即可

    1、2两点,通过mq同步发送和MessageQueueSelector保证即可

RocketMQ原理

服务组件

​ NameServer、Broker、FilterServer

  • Name Server
    是RocketMQ的寻址服务,用于把Broker的路由信息做聚合。客户端依靠Name Server决定去获取对应topic的路由信息,从而决定对哪些Broker做连接
  • Broker
    是处理消息存储,转发等处理的服务器
    • Broker以group分开,每个group只允许一个master,若干个slave
    • 只有master才能进行写入操作,slave不可以
    • slave从master中同步数据
    • 客户端可以从master或slave消费,master挂掉后,客户端从Name Server感知Broker挂机,从slave消费
  • Filter Server
    Rocket运行消费者上传一个Java类给Filter Server过滤

角色

  • Producer
    生产者。发送消息的客户端角色,发送消息需知道Topic
  • Consumer
    消费者,消费消息的客户端角色。通常是后台处理异步消费的系统
    • 实现方式
      1. PushConsumer
        推送模式的消费者–>内部已处理如线程池消费、流控、负载均衡、异常处理
      2. PullConsumer
        拉取模式的消费者,应用主动控制拉取的动机,如何拉取、如何消费—>主动权更高,但要自行处理各种场景

Producer Group : 表示发送同一类消息的Producer,做标识使用
Consumer Group : 表示消费同一类消息,表示一类Consumer的集合名称

Topic : 标识一类信息的逻辑名字,消息的逻辑管理单位(生产者、消费者都需要指定Topic)
Message Queue : 一个Topic可能有若干Q,若Topic同时创建在不同Broker,则不同Broker有若干Q=====》Producer发送消息的时候,会预先选择好该Topic下面的某一条Q发送,Consumer消费的时候也会负载均衡的分配若干Q,拉取对应Q信息

NameServer 路由注册与路由发现

Broker–》路由的注册,消息的存储与投递

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U4JkDSVd-1629904140433)(/Users/wujiewei/Library/Application Support/typora-user-images/image-20210824102935192.png)]

Brokers轮询对所有NameServer进行注册

主题Topic

Topic : message 1:n message: Topic 1:1

一个生产者可以同时发送多种Topic消息

一个消费者只能针对某种特定的Topic的消费

Producer:Topic 1:n Consumer:topic 1:1

队列Queue

存储消息的物理实体,一个Topic可以包含多个Queue,每个Queue中存放的就是该Topic的消息,Topic的Queue也被称之为Topic中消息的分区(Partition)

一个Topic的Queue中的消息只能被一个消费者组中的一个消费者消费

一个Queue中的消息同一个消费者组中的多个消费者同时消费

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FwujAJAb-1629904140434)(/Users/wujiewei/Desktop/wcw/Photo/other/项目/0008.分区分片.png)]

分区分片

  • 消息的数据结构在Producer端是Message,到了Broker,Broker就会给消息加字段,变成了MessageExt,MessageExt继承自Message
  • RocketMQ在MessageListenerConcurrently接口的consumeMessage的参数List是一个List消息列表
  • consumeMessage只有两种返回值—成功消费和失败稍后重试
  • 只要是不返回成功消费,就会执行稍后重试
  • 调用consumer.setConsumeMessageBatchMaxSize(10);可以设置该List的最大长度,即最多允许一次消费多少条。(注:这种批量消费的思路很棒,因为有的业务场景有可能处理一个消息需要一秒,处理十条消息仅需要五秒,所以批量消费效率有时候会高一些
  • 管理消费进度,保证消费,ack只在PushConsumer中有实现的
  • PushConsumer为了保证消息肯定消费成功,只有使用方明确表示消费成功,RocketMQ才会认为消息消费成功。中途断电,抛出异常等都不会认为成功——即都会重新投递
  • 为了保证消息是肯定被至少消费成功一次,RocketMQ会把这批消息重发回Broker(topic不是原topic而是这个消费租的RETRY topic),在延迟的某个时间点(默认是10秒,业务可设置)后,再次投递到这个ConsumerGroup。而如果一直这样重复消费都持续失败到一定次数(默认16次),就会投递到DLQ死信队列。应用可以监控死信队列来做人工干预
  • 如果最大批量消费条数为10条,我们在consumeMessage方法中遍历List,并一条一条的发送,当发送到第5条的时候出错了====》客户端就会帮我们把n之后的消息全部重试掉

消息ACK机制

RocketMQ以consumer group+queue为单位来管理消费进度的,以一个consumer offset标记这个消费组在这条queue上的消费进度====》某个已经存在的消费组出现了新消费实例的时候,依靠这个组的消费进度,就可以判断第一次是从哪里开始拉取消息的

文件刷盘机制

RocketMQ的消息是存储在磁盘上的,两个优点:

  • 保证断电后恢复
  • 让存储的消息量超出内存的限制

高可用

Name Server高可用

NameServer节点是无状态的且各个节点之间的数据是一致的,所以存在多个NameServer节点的情况下,部分NameServer不可用也能保证MQ服务正常运行

BrokerServer高可用

RocketMQ通过Master和Slave配合达到BrokerServer模块的高可用性的,Master-Slaver组

消息消费高可用

依赖于Master-Slave配置的,Master支持读写消息,Slave支持读消息,Master繁忙或宕机时,Consumer可自动切换到从Slave读取

消息发送高可用

创建Topic时,把Topic的多个Message Queue创建到多个Broker组上,当一个Broker组的Master不可用后,其他组Master仍可用,Producer仍可以发送消息

消息主从复制

一个Broker组有一个Master、若干Slave,消息从Master复制到Slave上有同步复制和异步复制两种方式

同步复制

等Master和Slave都写成功后,才反馈给客户端写成功状态==》可靠性高,Master宕机,Slave有全部的备份数据,容易恢复,但系统吞吐量低

异步复制

只要Master写成功,就反馈给客户端成功状态==》若Master宕机,可能存在一些数据没来及写入Slave,导致丢失,但写入延迟低,吞吐量高

  • 配置方式

对broker配置文件里的BrokerRole参数配置

ASYNC_MASTER异步复制
SYNC_MASTER同步复制
  • 实际应用

同步刷盘方式会频繁触发磁盘写操作,明显降低性能

刷盘方式ASYNC_FLUSH异步刷盘
主从复制SYNC_MASTER同步复制

异步刷盘能避免频繁触发磁盘写操作,除非服务器宕机,否则不会造成消息丢失

主从同步复制能保证消息不丢失,即使Master节点异常,也能保证Slave节点存储所有消息能正常消费掉

消息重复问题

发送时消息重复

当一条消息已成功发送到服务器并完成持久化,此时网络或客户端宕机,导致服务端对客户端应答失败,生产者意识消息发送失败,尝试再次发送消息,消费者会收到两条内容相同且Message ID也想通的消息

投递时消息重复

消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答时,网络闪断,为了保证消息至少被消费一次,RocketMQ服务端会在网络恢复后再次尝试投递处理过的消息,消费者会收到两条内容相同并且 Message ID 也相同的消息

解决

在消费者端统一进行幂等处理就能够实现消息幂等

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cp41eIH9-1629904140436)(/Users/wujiewei/Desktop/wcw/Photo/0008消息幂等.png)]

@ConditionalOnProperty(prefix = “rocketmq.producer”, value = “isOnOff”, havingValue = “on”)

  • 如果消费者处理一批,部分失败返回,如何记录消费偏移?全部重复投递?还是部分投递?

  • 消费者消费了,但是服务器没有收到成功返回值,怎么处理?===》重复消费?本地记录?—》Redis幂等

  • 生产者发送消息

  • Master死掉后,会从Slave中选举一个Master吗?

  • 消费者,在向服务器返回成功,操作数据库失败

TPS:transaction per second代表每秒执行的事务数量,TPS=事务数/时间(秒)

QPS:Queries-per-second每秒查询率,QPS=req/sec

一个TPS可能包含多个QPS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值