RocketMQ ---- 学习记录

RocketMQ:

RocketMQ是一款分布式、高吞吐量、可靠的消息中间件,最初是由阿里巴巴团队开发,目前是Apache顶级开源项目之一。它提供了完整的MQ请求-响应模式和异步通信模式,支持以互联网规模处理大量的事务性消息。

RocketMQ具有以下特点:

1. 高吞吐量和低延迟: RocketMQ的预写日志(commit log)的存储格式采用类似于零拷贝 (zero-copy) 的方式,减少了磁盘 I/O 的消耗并提高了存储效率,从而能够实现高吞吐量和低延迟。

2. 分布式架构: RocketMQ采用分布式架构,可以通过部署多个Broker节点来实现负载均衡和故障转移,从而保证了系统的高可用性和扩展性。

3. 顺序消息: RocketMQ支持以业务逻辑相关的“区域”(如订单ID等)为单位发送顺序消息,并能确保这些消息按照发送的顺序被消费者接收。此外,RocketMQ还支持多个区域之间的顺序消息重复消费。

4. 消息可靠性: RocketMQ支持主从同步和异步刷盘方式,能够确保消息的可靠性。例如,当集群中一台Broker节点挂掉时,该节点上未被刷盘的消息将被同步到其他的Broker节点。

5. 非常适合大规模分布式系统: RocketMQ采用了非常灵活的多维度负载均衡策略,可以针对各种复杂业务场景进行定制,同时支持水平扩展和无状态节点的动态添加。

6. 丰富的功能和易用性: RocketMQ支持顺序消息、定时消息、延时消息等高级特性,并提供多种客户端语言的API(Java、C++、Python等),使用起来非常方便。

7. 灵活的部署方式: RocketMQ不仅支持集群模式,还支持以命名空间为单位的虚拟专有云(VPC)模式,可以满足不同场景下的需求。

8. 易于扩展和自定义化: RocketMQ已经成为了Apache顶级开源项目之一,社区生态非常丰富。开发者可以通过阅读RocketMQ源代码、编写自定义插件等方式扩展和完善系统功能。

9. 支持分布式事务: RocketMQ除了纯粹的消息队列外,还提供了基于半消息(half message)的分布式事务机制,能够很好地保证多个消息的原子性提交。

总之,RocketMQ是一个可靠、高效、强大的分布式消息框架 ,非常适合大规模分布式系统的应用,如电商交易、数据传输、日志处理、微服务通信等领域。

一、安装运行RocketMQ

1、拉取镜像
docker pull apache/rocketmq
2、启动namesrv服务
docker run -d -p 9876:9876 -v /usr/local/rocketmq/mqnamesrv/logs:/root/logs -v /usr/local/rocketmq/mqnamesrv/store:/root/store --restart=always --name mqnamesrv -e "MAX_POSSIBLE_HEAP=100000000" apache/rocketmq:latest sh mqnamesrv
参数说明
-e "MAX_POSSIBLE_HEAP=100000000" :jvm内存限制
-v /usr/local/rocketmq/mqnamesrv/logs:/root/logs :数据卷挂载
-v /usr/local/rocketmq/mqnamesrv/store:/root/store:数据港挂载
-d 后台守护
-p 端口映射
3、启动broker服务
1、创建配置文件(下面的namesrvAddr需要修改为自己的)
# broker集群名称
brokerClusterNam = DefaultCluster
# broker节点名称
brokerName = broker-a
# broker节点id
brokerId = 0
# 删除条件?这个还不清楚用途,后续研究下
deleteWhen = 04
# 文件保留时间(单位小时),默认为3天
fileReservedTime = 48
# broker角色
brokerRole = ASYNC_MASTER
# 磁盘同步方式:同步,异步
flushDiskType = ASYNC_FLUSH
# 类似注册中心改为自己的
namesrvAddr=192.168.19.129:9876
# 当前broker监听的IP(主)改为自己的
brokerIP1 = 192.168.19.129

docker run -d -p 10911:10911 -p 10909:10909 --name mqbroker --restart=always -v /usr/local/rocketmq/mqbroker/data:/root/store -v /usr/local/rocketmq/mqbroker/logs:/root/logs -v /usr/local/rocketmq/mqbroker/broker.conf:/opt/rocketmq/conf/broker.conf -e "NAMESRV_ADDR=192.168.19.129:9876" -e "JAVA_OPT_EXT=-server -Xms128m -Xmx128m -Xmn64m" apache/rocketmq:latest  sh mqbroker -c /opt/rocketmq/conf/broker.conf
命令说明:
-d 参数指定容器将在后台运行,并且将分配一个唯一的容器ID;
-p 参数指定将本地主机的端口映射到容器的端口,例如 “10911:10911” 表示 RocketMQ Broker 容器的 10911 端口将映射到本地主机的 10911 端口;
--name 参数指定 Docker 容器的名称,可以自定义名称;
--restart 参数指定在容器意外停止或者系统重启时容器是否自动重启;
-v 参数指定将本地目录或文件映射到容器中某个目录下,例如将本地主机的 “/usr/local/rocketmq/mqbroker/data” 和 “/usr/local/rocketmq/mqbroker/logs” 目录映射到 RocketMQ Broker 容器的 “/root/store” 和 “/root/logs” 目录中;
-e 参数指定将环境变量传递给容器中的应用程序,例如 “NAMESRV_ADDR=192.168.19.129:9876” 指定了 Name Server 的IP地址和端口号;
apollo/rocketmq:latest 是基于 RocketMQ 官方 Docker 镜像的名称和标签;

sh mqbroker -c /opt/rocketmq/conf/broker.conf 是在容器中要执行的命令,其中的 “-c /opt/rocketmq/conf/broker.conf” 参数指定了启动 Broker 时所使用的配置文件。

3、rocketmq的控制台(rocketmq-console-ng)
1、拉取镜像
docker pull styletang/rocketmq-console-ng
2、运行镜像
docker run -e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.19.129:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" -d -p 8080:8080 styletang/rocketmq-console-ng
命令说明:
-e 参数指定将环境变量传递给容器中的应用程序
3、效果(访问http://192.168.19.129:8080/

在这里插入图片描述

二、RocketMQ的基础知识

1、MQ的用途
  • 限流削峰
    MQ 可以通过缓冲消息来平衡系统的负载,它能缓解高并发访问对后端服务的压力,并且通过消息流量控制机制来限制服务的消费速率。例如在某一时刻有大量的请求(50000请求)突然打进来,但是后端服务只能处理20000请求,此时就会只放20000请求到后端,剩下的都会在mq中缓存,慢慢消费。否则后端服务一下就被打垮了。
  • 服务解耦
    MQ 可以将消息发送到独立的队列中,并且由消费者按照其自己的速度进行处理,实现了不同服务之间的解耦。
  • 异步通信
    通过 MQ,发送方和接收方可以异步地进行通信,避免了等待请求响应所造成的时间浪费和阻塞。例如我支付成功了需要对库存操作减一,可以在支付成功的时候就直接响应,而不是需要等待库存的操作完成了再一起返回。
2、领域模型

Apache RocketMQ 是一款典型的分布式架构下的中间件产品,使用异步通信方式和发布订阅的消息传输模型,Apache RocketMQ 产品具备异步通信的优势,系统拓扑简单、上下游耦合较弱,主要应用于异步解耦,流量削峰填谷等场景。
在这里插入图片描述
Apache RocketMQ 中消息的生命周期主要分为消息生产消息存储消息消费三部分。

生产者生产消息并发送至 Apache RocketMQ 服务端,消息被存储在服务端的主题中,消费者通过订阅主题消费消息

  • 消息生产
    生产者(producer): Apache RocketMQ 中用于生产消息的运行实体,一般集成在业务调用链路的上游,生产者是轻量级匿名无身份的。
  • 消息存储
    • 主题(Topic) :Apache RocketMQ 消息传输和存储的分组容器,主题内部由多个队列组成,消息的存储和水平扩展实际是通过主题内的队列实现的。
    • 队列(Queue):Apache RocketMQ的消息传输和存储的实际单元容器,类比于其他消息队列中的分区,RocketMQ通过流式特性的无限队列来存储消息,消息在队列内具备顺序存储特征。
    • 消息(Message):Apache RocketMQ 中的最小传输单元,消息具备不可变性,在初始化发送之前和完成存储后既不可变。
  • 消息消费
    • 消费者(Consumer): Apache RocketMQ 消费消息的运行实体,一般集成在调用链路的下游,每一个消费者必须被指定到某一个消费者组里面去。
    • 消费者组(ConsumerGroup):Apache RocketMQ 发布订阅模型中定义的独立的消费身份分组,用于统一管理底层运行的多个消费者(Consumer)。同一个消费组的多个消费者必须保持消费逻辑和配置一致,共同分担该消费组订阅的消息,实现消费能力的水平扩展。
    • 订阅关系(Subscription):Apache RocketMQ 发布订阅模型中消息过滤、重试、消费进度的规则配置。订阅关系以消费组粒度进行管理,消费组通过定义订阅关系控制指定消费组下的消费者如何实现消息过滤、消费重试及消费进度恢复等。
      Apache RocketMQ 的订阅关系除过滤表达式之外都是持久化的,即服务端重启或请求断开,订阅关系依然保留。
3、传输模型
主流的消息中间件的传输模型主要为点对点模型和发布订阅模型。
  • 点对点模型
    在这里插入图片描述

点对点模型也叫队列模型,具有如下特点:
1、消费匿名:消息上下游沟通的唯一的身份就是队列,下游消费者从队列获取消息无法申明独立身份。
2、一对一通信:基于消费匿名特点,下游消费者即使有多个,但都没有自己独立的身份,因此共享队列中的消息,每一条消息都只会被唯一一个消费者处理。因此点对点模型只能实现一对一通信。

  • 发布订阅模型
    在这里插入图片描述
    发布订阅模型具有如下特点:
    1、消费独立:相比队列模型的匿名消费方式,发布订阅模型中消费方都会具备的身份,一般叫做订阅组(订阅关系),不同订阅组之间相互独立不会相互影响。
    2、一对多通信:基于独立身份的设计,同一个主题内的消息可以被多个订阅组处理,每个订阅组都可以拿到全量消息。因此发布订阅模型可以实现一对多通信。

传输模型对比

点对点模型和发布订阅模型各有优势,点对点模型更为简单,而发布订阅模型的扩展性更高。 Apache RocketMQ 使用的传输模型为发布订阅模型,因此也具有发布订阅模型的特点。

4、主题(Topic)
  • 定义
    主题是 Apache RocketMQ 中消息传输和存储的顶层容器,用于标识同一类业务逻辑的消息。
    主题的作用主要如下:

    • 定义数据的分类隔离: 在 Apache RocketMQ 的方案设计中,建议将不同业务类型的数据拆分到不同的主题中管理,通过主题实现存储的隔离性和订阅隔离性
    • 定义数据的身份和权限: Apache RocketMQ 的消息本身是匿名无身份的,同一分类的消息使用相同的主题来做身份识别和权限管理。
  • 内部属性

    • 主题名称
      • 定义:主题的名称,用于标识主题,主题名称集群内全局唯一。
      • 取值:由用户创建主题时定义。
    • 队列列表
      • 定义:队列作为主题的组成单元,是消息存储的实际容器,一个主题内包含一个或多个队列,消息实际存储在主题的各队列内。
      • 取值:系统根据队列数量给主题分配队列,队列数量创建主题时定义
    • 消息类型
      • 定义:主题所支持的消息类型。
      • 取值:创建主题时选择消息类型。Apache RocketMQ 支持的主题类型如下:
        Normal:普通消息,消息本身无特殊语义,消息之间也没有任何关联。
        FIFO:顺序消息,Apache RocketMQ 通过消息分组MessageGroup标记一组特定消息的先后顺序,可以保证消息的投递顺序严格按照消息发送时的顺序。
        Delay:定时/延时消息,通过指定延时时间控制消息生产后不要立即投递,而是在延时间隔后才对消 费者可见。
        Transaction:事务消息,Apache RocketMQ 支持分布式事务消息,支持应用数据库更新和消息调用的事务一致性保障。
  • 其他参考官网

5、队列(MessageQueue)
  • 定义
    队列是 Apache RocketMQ 中消息存储和传输的实际容器,也是 Apache RocketMQ 消息的最小存储单元。 Apache RocketMQ 的所有主题都是由多个队列组成,以此实现队列数量的水平拆分和队列内部的流式存储。
    队列的主要作用如下:
    • 存储顺序性
      队列天然具备顺序性即消息按照进入队列的顺序写入存储,同一队列间的消息天然存在顺序关系,队列头部为最早写入的消息,队列尾部为最新写入的消息。消息在队列中的位置和消息之间的顺序通过位点(Offset)进行标记管理。
    • 流式操作语义
      Apache RocketMQ 基于队列的存储模型可确保消息从任意位点读取任意数量的消息,以此实现类似聚合读取回溯读取等特性,这些特性是RabbitMQ、ActiveMQ等非队列存储模型不具备的。
  • 内部属性
    读写权限
    • 定义: 当前队列是否可以读写数据
    • 取值:由服务端定义,枚举值如下:
      • 6:读写状态,当前队列允许读取消息和写入消息
      • 4:只读状态,当前队列只允许读取消息,不允许写入消息。
      • 2:只写状态,当前队列只允许写入消息,不允许读取消息。
      • 0:不可读写状态,当前队列不允许读取消息和写入消息
  • 其他参考官网
6、消息(Message)
  • 定义
    消息是 Apache RocketMQ 中的最小数据传输单元。生产者将业务数据的负载和拓展属性包装成消息发送到 Apache RocketMQ 服务端,服务端按照相关语义将消息投递到消费端进行消费。
    消息模型具备如下特点:
    • 消息不可变性
      消息本质上是已经产生并确定的事件,一旦产生后,消息的内容不会发生改变。即使经过传输链路的控制也不会发生变化,消费端获取的消息都是只读消息视图
    • 消息持久化
      Apache RocketMQ 会默认对消息进行持久化,即将接收到的消息存储到 Apache RocketMQ 服务端的存储文件中,保证消息的可回溯性和系统故障场景下的可恢复性
  • 内部属性
    • 主题名称

      • 定义:当前消息所属的主题的名称。集群内全局唯一。
      • 取值:从客户端SDK接口获取。
    • 消息类型

      • 定义:当前消息的类型
      • 取值:从客户端SDK接口获取。Apache RocketMQ 支持的消息类型如下:
        • Normal:普通消息,消息本身无特殊语义,消息之间也没有任何关联。
        • FIFO:顺序消息,Apache RocketMQ 通过消息分组MessageGroup标记一组特定消息的先后顺序,可以保证消息的投递顺序严格按照消息发送时的顺序。
        • Delay:定时/延时消息,通过指定延时时间控制消息生产后不要立即投递,而是在延时间隔后才对消费者可见。
        • Transaction:事务消息,Apache RocketMQ 支持分布式事务消息,支持应用数据库更新和消息调用的事务一致性保障。
    • 约束:Apache RocketMQ 从5.0版本开始,支持强制校验消息类型,即每个主题只允许发送一种消息类型的消息,这样可以更好的运维和管理生产系统,避免混乱。为保证向下兼容4.x版本行为,强制校验功能默认关闭,推荐通过服务端参数 enableTopicMessageTypeCheck 开启校验。

    • 消息队列

      • 定义:实际存储当前消息的队列
      • 取值:由服务端指定并填充。
    • 消息位点

      • 定义:当前消息存储在队列中的位置。
      • 取值:由服务端指定并填充。取值范围:0~long.Max。
    • 消息ID

      • 定义:消息的唯一标识,集群内每条消息的ID全局唯一。
      • 取值:生产者客户端系统自动生成。固定为数字和大写字母组成的32位字符串。
    • 索引Key列表(可选)

      • 定义:消息的索引键,可通过设置不同的Key区分消息和快速查找消息。
      • 取值:由生产者客户端定义
    • 过滤标签Tag(可选)

      • 定义:消息的过滤标签。消费者可通过Tag对消息进行过滤,仅接收指定标签的消息。
      • 取值:由生产者客户端定义。
      • 约束:一条消息仅支持设置一个标签。
    • 定时时间(可选)

      • 定义:定时场景下,消息触发延时投递的毫秒级时间戳。
      • 取值:由消息生产者定义。
      • 约束:最大可设置定时时长为40天。
    • 消息发送时间

      • 定义:消息发送时,生产者客户端系统的本地毫秒级时间戳。
      • 取值:由生产者客户端系统填充。
      • 说明:客户端系统时钟和服务端系统时钟可能存在偏差,消息发送时间是以客户端系统时钟为准。
    • 消息保存时间戳

      • 定义:消息在Apache RocketMQ 服务端完成存储时,服务端系统的本地毫秒级时间戳。 对于定时消息和事务消息,消息保存时间指的是消息生效对消费方可见的服务端系统时间。
      • 取值:由服务端系统填充。
      • 说明:客户端系统时钟和服务端系统时钟可能存在偏差,消息保留时间是以服务端系统时钟为准。
    • 消费重试次数

      • 定义:消息消费失败后,Apache RocketMQ 服务端重新投递的次数。每次重试后,重试次数加1。
      • 取值:由服务端系统标记。首次消费,重试次数为0;消费失败首次重试时,重试次数为1。
    • 业务自定义属性

      • 定义:生产者可以自定义设置的扩展信息。
      • 取值:由消息生产者自定义,按照字符串键值对设置。
    • 消息负载

      • 定义:业务消息的实际报文数据。
      • 取值:由生产者负责序列化编码,按照二进制字节传输。
  • 其他参考官网
7、生产者(Producer)
  • 定义
    生产者是 Apache RocketMQ 系统中用来构建并传输消息到服务端的运行实体。
    生产者通常被集成在业务系统中,将业务消息按照要求封装成 Apache RocketMQ 的消息(Message)并发送至服务端。
    在消息生产者中,可以定义如下传输行为:
    • 发送方式:生产者可通过API接口设置消息发送的方式。Apache RocketMQ 支持同步传输和异步传输。

    • 批量发送:生产者可通过API接口设置消息批量传输的方式。例如,批量发送的消息条数或消息大小。

    • 事务行为:Apache RocketMQ 支持事务消息,对于事务消息需要生产者配合进行事务检查等行为保障事务的最终一致性。

  • 内部属性
    • 客户端ID
      • 定义:生产者客户端的标识,用于区分不同的生产者。集群内全局唯一。
      • 取值:客户端ID由Apache RocketMQ 的SDK自动生成,主要用于日志查看、问题定位等运维场景,不支持修改。
    • 通信参数
      • 接入点信息 (必选) :连接服务端的接入地址,用于识别服务端集群。 接入点必须按格式配置,建议使用域名,避免使用IP地址,防止节点变更无法进行热点迁移。
      • 身份认证信息 (可选) :客户端用于身份验证的凭证信息。 仅在服务端开启身份识别和认证时需要传输。
      • 请求超时时间 (可选) :客户端网络请求调用的超时时间。
    • 预绑定主题列表
      • 定义:Apache RocketMQ 的生产者需要将消息发送到的目标主题列表,主要作用如下:

        • 事务消息 (必须设置) :事务消息场景下,生产者在故障、重启恢复时,需要检查事务消息的主题中是否有未提交的事务消息。避免生产者发送新消息后,主题中的旧事务消息一直处于未提交状态,造成业务延迟。
        • 非事务消息 (建议设置) :服务端会在生产者初始化时根据预绑定主题列表,检查目标主题的访问权限和合法性,而不需要等到应用启动后再检查。

        若未设置,或后续消息发送的目标主题动态变更, Apache RocketMQ 会对目标主题进行动态补充检验。

      • 约束:对于事务消息,预绑定列表必须设置,且需要和事务检查器一起配合使用。

    • 事务检查器
      • 定义:Apache RocketMQ 的事务消息机制中,为保证异常场景下事务的最终一致性,生产者需要主动实现事务检查器的接口。
      • 发送事务消息时,事务检查器必须设置,且需要和预绑定主题列表一起配合使用。
    • 发送重试策略:
      • 定义: 生产者在消息发送失败时的重试策略。
  • 其他参考官网
8、消费者(Consumer)
  • 定义
    消费者是 Apache RocketMQ 中用来接收并处理消息的运行实体。 消费者通常被集成在业务系统中,从 Apache RocketMQ 服务端获取消息,并将消息转化成业务可理解的信息,供业务逻辑处理。
    在消息消费端,可以定义如下传输行为:
    • 消费者身份:消费者必须关联一个指定的消费者分组,以获取分组内统一定义的行为配置和消费状态。
    • 消费者类型:Apache RocketMQ 面向不同的开发场景提供了多样的消费者类型,包括PushConsumer类型、SimpleConsumer类型、PullConsumer类型(仅推荐流处理场景使用)等。
    • 消费者本地运行配置:消费者根据不同的消费者类型,控制消费者客户端本地的运行配置。例如消费者客户端的线程数,消费并发度等,实现不同的传输效果。
  • 内部属性
    • 消费者分组名称
      • 定义:当前消费者关联的消费者分组名称,消费者必须关联到指定的消费者分组,通过消费者分组获取消费行为。
      • 消费者分组为Apache RocketMQ 的逻辑资源,需要您提前通过控制台或OpenAPI创建。
    • 客户端ID
      • 定义:消费者客户端的标识,用于区分不同的消费者。集群内全局唯一。
      • 取值:客户端ID由Apache RocketMQ 的SDK自动生成,主要用于日志查看、问题定位等运维场景,不支持修改。
    • 通信参数
      • 接入点信息 (必选) :连接服务端的接入地址,用于识别服务端集群。 接入点必须按格式配置,建议使用域名,避免使用IP地址,防止节点变更无法进行热点迁移。
      • 身份认证信息 (可选) :客户端用于身份验证的凭证信息。 仅在服务端开启身份识别和认证时需要传输。
      • 请求超时时间 (可选) :客户端网络请求调用的超时时间。
    • 预绑定订阅关系列表
      • 定义:指定消费者的订阅关系列表。 Apache RocketMQ 服务端可在消费者初始化阶段,根据预绑定的订阅关系列表对目标主题进行权限及合法性校验,无需等到应用启动后才能校验。
      • 取值:建议在消费者初始化阶段明确订阅关系即要订阅的主题列表,若未设置,或订阅的主题动态变更,Apache RocketMQ 会对目标主题进行动态补充校验。
    • 消费监听器
      • 定义:Apache RocketMQ 服务端将消息推送给消费者后,消费者调用消息消费逻辑的监听器。
      • 取值:由消费者客户端本地配置。
      • 约束:使用PushConsumer类型的消费者消费消息时,消费者客户端必须设置消费监听器。
  • 其他参考官网
9、消费者组(ConsumerGroup)
  • 定义
    消费者分组是 Apache RocketMQ 系统中承载多个消费行为一致的消费者的负载均衡分组。

    和消费者不同,消费者分组并不是运行实体,而是一个逻辑资源。在 Apache RocketMQ 中,通过消费者分组内初始化多个消费者实现消费性能的水平扩展以及高可用容灾。

    在消费者分组中,统一定义以下消费行为,同一分组下的多个消费者将按照分组内统一的消费行为和负载均衡策略消费消息。

    • 订阅关系:Apache RocketMQ 以消费者分组的粒度管理订阅关系,实现订阅关系的管理和追溯。
    • 投递顺序性:Apache RocketMQ 的服务端将消息投递给消费者消费时,支持顺序投递和并发投递,投递方式在消费者分组中统一配置。
    • 费重试策略: 消费者消费消息失败时的重试策略,包括重试次数、死信队列设置等。
  • 内部属性

    • 消费者分组名称
      • 定义:消费者分组的名称,用于区分不同的消费者分组。集群内全局唯一。
      • 取值:消费者分组由用户设置并创建。
    • 投递顺序性
      • 定义:消费者消费消息时,Apache RocketMQ 向消费者客户端投递消息的顺序。
        根据不同的消费场景,Apache RocketMQ 提供顺序投递和并发投递两种方式。
      • 取值:默认投递方式为并发投递。
    • 消费重试策略
      • 定义:消费者消费消息失败时,系统的重试策略。消费者消费消息失败时,系统会按照重试策略,将指定消息投递给消费者重新消费。
      • 取值:重试策略包括:
        • 最大重试次数:表示消息可以重新被投递的最大次数,超过最大重试次数还没被成功消费,消息将被投递至死信队列或丢弃。
        • 重试间隔:Apache RocketMQ 服务端重新投递消息的间隔时间。
    • 订阅关系
      • 定义:当前消费者分组关联的订阅关系集合。包括消费者订阅的主题,以及消息的过滤规则等。订阅关系由消费者动态注册到消费者分组中,Apache RocketMQ 服务端会持久化订阅关系并匹配消息的消费进度。
  • 其他参考官网

10、订阅关系(Subscription)
  • 定义
    订阅关系是 Apache RocketMQ 系统中消费者获取消息、处理消息的规则和状态配置。

    订阅关系由消费者分组动态注册到服务端系统,并在后续的消息传输中按照订阅关系定义的过滤规则进行消息匹配和消费进度维护。

    通过配置订阅关系,可控制如下传输行为:

    • 消息过滤规则:用于控制消费者在消费消息时,选择主题内的哪些消息进行消费,设置消费过滤规则可以高效地过滤消费者需要的消息集合,灵活根据不同的业务场景设置不同的消息接收范围。

    • 消费状态:Apache RocketMQ 服务端默认提供订阅关系持久化的能力,即消费者分组在服务端注册订阅关系后,当消费者离线并再次上线后,可以获取离线前的消费进度并继续消费。

    不同消费者分组对于同一个主题的订阅相互独立,同一个消费者分组对于不同主题的订阅也相互独立

  • 内部属性

    • 过滤类型
      • 定义:消息过滤规则的类型。订阅关系中设置消息过滤规则后,系统将按照过滤规则匹配主题中的消息,只将符合条件的消息投递给消费者消费,实现消息的再次分类。

      • 取值:
        TAG过滤:按照Tag字符串进行全文过滤匹配。
        SQL92过滤:按照SQL语法对消息属性进行过滤匹配。

    • 过滤表达式:定义:自定义的过滤规则表达式。
  • 其他参考官网

11、NameServer

主要作用:

Broker 注册与发现:每当一个新的 Broker 启动时,它会将自己注册到 NameServer 的列表中,同时定期向其汇报自己的运行状态和负载情况;订阅者或生产者需要消息时,则通过查询 NameServer 将连向可用的 Broker。

Topic 管理:NameServer 会记录所有存在的 topic 和其相应的 Broker 第一次创建 Topic 信息,接下来通过定期心跳和主动上报信息的方式获取 Topic 相关信息的变化,并通知 Broker 进行调整。

负载均衡:NameServer 在接收到消费者订阅请求后,根据订阅 topic 的订阅规则,结合 Broker 内存和磁盘等信息,计算出一个可以让消费者优先从近处的Broker中拉取消息的路由表并返回给消费者,因此,NameServer具有路由服务的作用。

路由剔除管理:RocketMQ 支持 Broker 的动态扩容和缩容,当部分 Broker 发生故障或网络异常时,NameServer 会检测对应的 Topic 下的路由是否有问题,将不可用的 Broker 相关信息从 NameServer 中移除,避免消息转发出现意外。

12、Broker
  • 概念
    Broker 是 RocketMQ 的核心组件之一,负责存储和转发消息。它是一个搭载了消息存储、消息处理、网络传输等模块的服务器进程,用于接收生产者发送的消息并将其存储到特定的 Topic 中,同时将这些消息经过处理后转发给订阅了相应 Topic 的消费者。
    Broker节点集群是一个主从集群,所以在集群中存在两个角色,Master和Slave,Master角色的 Broker支持读和写, Slave角色的 Broker仅支持读。一个Master对应多个Slave,但一个Slave只能属于一个Master。

  • 复制策略
    复制是指Master角色的数据复制到Slave的过程

    • 同步复制:同步复制是指在消息数据写入了Master会等待Slave同步数据成功才向生产者返回ACK,效率较低但能保证ACK的时候消息一定复制到了Slave。
    • 异步复制:异步复制是指消息数据写入了Master之后就返回成功ACK消息给生产者,不等待Slave同步数据。效率较高但出现意外的时候会丢失消息

    异步复制降低写入延迟,RT变小,提高了系统的吞吐量。

  • 刷盘策略
    刷盘(落盘)是指在broker内部消息数据从内存持久化到磁盘的操作。

    • 同步刷盘:当消息写入到broker的磁盘才算消息写入成功。
    • 异步刷盘:当消息写入broker的内存中就算写入成功,无需等待持久化到磁盘。

    异步刷盘降低写入延迟,RT变小,提高了系统的吞吐量。
    异步刷盘丢失少量消息,同步刷盘一条不丢

  • 功能作用
    主要负责接收、存储、检索和分发消息,并提供管理和监控功能
    下面是 Broker 主要的功能作用:

    • 提供高可用和可扩展的消息存储方式:Broker 根据集群规模动态调整数据分片数量和负载平衡策略,保证消息系统的可靠性和水平扩展性。
    • 实现高效的消息传输和路由机制:Broker 提供丰富的消息订阅、轨迹处理和消息查询接口,以便协助开发者使用灵活的方式控制和优化消息传输和消费流程。
    • 提供丰富的管理和监控功能:Broker 提供了很多管理和监控工具,以便实时或离线地了解队列数据状态、发送和消费流量情况、性能指标等运行状况信息,并根据业务需求及时补救方案。
    • 支持消息事务:Broker 支持基于两阶段提交的分布式事务消息,可以实现像交易处理、资金清算等高强度一致性的多节点操作。

三、功能特性

  • 普通消息

    • 定义:普通消息是Apache RocketMQ基本消息功能,支持生产者和消费者的异步解耦通信。
    • 生命周期:
      在这里插入图片描述
      • 初始化:消息被生产者初始化完成,待发送到服务端的状态
      • 待消费:消息被发送到了服务端,消费者可见的状态,等待消费者的消费的状态。
      • 消费中:消息被消费者获取,并按照消费者本地的业务逻辑进行处理的过程。 此时服务端会等待消费者完成消费并提交消费结果,如果一定时间后没有收到消费者的响应,Apache RocketMQ会对消息进行重试处理。
      • 消费提交:消费者完成消费处理,并向服务端提交消费结果,服务端标记当前消息已经被处理(包括消费成功和失败)。 Apache RocketMQ默认支持保留所有消息,此时消息数据并不会立即被删除,只是逻辑标记已消费。消息在保存时间到期或存储空间不足被删除前,消费者仍然可以回溯消息重新消费。
      • 消息删除:Apache RocketMQ按照消息保存机制滚动清理最早的消息数据,将消息从物理文件中删除。
    • 使用注意事项:设置全局唯一业务索引键,方便问题追踪
      Apache RocketMQ支持自定义索引键(消息的Key),在消息查询和轨迹查询时,可以通过索引键高效精确地查询到消息。
      因此,发送消息时,建议设置业务上唯一的信息作为索引,方便后续快速定位消息。例如,订单ID,用户ID等。
  • 顺序消息

    • 定义:
      顺序消息是 Apache RocketMQ 提供的一种高级消息类型,支持消费者按照发送消息的先后顺序获取消息,从而实现业务场景中的顺序处理。 相比其他类型消息,顺序消息在发送、存储和投递的处理过程中,更多强调多条消息间的先后顺序关系。

      Apache RocketMQ 顺序消息的顺序关系通过消息组(MessageGroup)判定和识别,发送顺序消息时需要为每条消息设置归属的消息组,相同消息组的多条消息之间遵循先进先出的顺序关系,不同消息组、无消息组的消息之间不涉及顺序性。

      基于消息组的顺序判定逻辑,支持按照业务逻辑做细粒度拆分,可以在满足业务局部顺序的前提下提高系统的并行度和吞吐能力。

    • 生命周期:与普通消息基本一致

    • 使用注意事项:串行消费,避免批量消费导致乱序
      消息消费建议串行处理,避免一次消费多条消费,否则可能出现乱序情况。
      例如:发送顺序为1->2->3->4,消费时批量消费,消费顺序为1->23(批量处理,失败)->23(重试处理)->4,此时可能由于消息3的失败导致消息2被重复处理,最后导致消息消费乱序。

  • 定时/延时消息

    • 定义:定时消息是 Apache RocketMQ 提供的一种高级消息类型,消息被发送至服务端后,在指定时间后才能被消费者消费。通过设置一定的定时时间可以实现分布式场景的延时调度触发效果。
    • 生命周期:相比之下就是在初始化和待消费之间多了In timing阶段也就是定时中。
      在这里插入图片描述
    • 使用注意事项:** 避免大量相同定时时刻的消息**
      定时消息的实现逻辑需要先经过定时存储等待触发,定时时间到达后才会被投递给消费者。因此,如果将大量定时消息的定时时间设置为同一时刻,则到达该时刻后会有大量消息同时需要被处理,会造成系统压力过大,导致消息分发延迟,影响定时精度。
  • 事务消息:

    • 定义:事务消息是 Apache RocketMQ 提供的一种高级消息类型,支持在分布式场景下保障消息生产和本地事务的最终一致性。

    • 生命周期:
      相比普通消息多了一个提交或者回滚的状态,如果提交了就进入了待消费的阶段,回滚则不往下继续,直接结束。并且在初始化完成发送的时候属于半事务消息,也就是对消费者是不可见的一种状态。
      在这里插入图片描述

    • 使用注意事项:

      • 消费事务性
        Apache RocketMQ 事务消息保证本地主分支事务和下游消息发送事务的一致性,但不保证消息消费结果和上游事务的一致性。因此需要下游业务分支自行保证消息正确处理,建议消费端做好消费重试,如果有短暂失败可以利用重试机制保证最终处理成功
      • 避免大量未决事务导致超时
        Apache RocketMQ支持在事务提交阶段异常的情况下发起事务回查,保证事务一致性。但生产者应该尽量避免本地事务返回未知结果。大量的事务检查会导致系统性能受损,容易导致事务处理延迟。
  • 消息发送重试和流控机制

    • 发送重试: 指在生产者发送消息到broker的时候可能会因为网络故障、服务异常等原因导致调用失败。为保证消息的可靠性, Apache RocketMQ 在客户端SDK中内置请求重试逻辑,尝试通过重试发送达到最终调用成功的效果。同步发送和异步发送模式均支持消息发送重试。
    • 什么情况下会进行重试:
      • 客户端网络异常导致连接失败或请求超时
      • broker服务端重启或下线
      • 客户端消息发送请求调用失败或请求超时
      • 服务端性能缘故导致造成请求超时。
      • 服务端返回错误码:系统逻辑错误:因运行逻辑不正确造成的错误。系统流控错误:因容量超限造成的流控错误。
      • 对于事务消息,只会进行透明重试(transparent retries),网络超时或异常等场景不会进行重试。透明重试(Transparent Retries)是指在消息中间件中,当消息发送失败时,消息中间件会自动进行重试操作,对于消息的发送方来说是透明的。
    • 重试的流程: 生产者在初始化时设置消息发送最大重试次数,当出现上述触发条件的场景时,生产者客户端会按照设置的重试次数一直重试发送消息,直到消息发送成功或达到最大重试次数重试结束,并在最后一次重试失败后返回调用错误响应。
      同步发送:调用线程会一直阻塞,直到某次重试成功或最终重试失败,抛出错误码和异常。
      异步发送:调用线程不会阻塞,但调用结果会通过异常事件或者成功事件返回。
    • 重试的间隔: 如果是服务端的流控错误响应码,系统会按照指数退避策略进行延迟重试。指数退避算法通过以下参数控制重试行为(其他的情况都是立即重试):
      • INITIAL_BACKOFF: 第一次失败重试前后需等待多久,默认值:1秒。

      • MULTIPLIER :指数退避因子,即退避倍率,默认值:1.6。

      • JITTER :随机抖动因子,默认值:0.2。

      • MAX_BACKOFF :等待间隔时间上限,默认值:120秒

      • MIN_CONNECT_TIMEOUT :最短重试间隔,默认值:20秒。

    指数退避算法(官方建议算法):

    ConnectWithBackoff()
    current_backoff = INITIAL_BACKOFF
    current_deadline = now() + INITIAL_BACKOFF
    while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT))!= SUCCESS)
    SleepUntil(current_deadline)
    current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
    current_deadline = now() + current_backoff + UniformRandom(-JITTER * current_backoff, JITTER * current_backoff)
    
    • 消息流控机制: 消息流控指的是系统容量或水位过高, Apache RocketMQ 服务端会通过快速失败返回流控错误来避免底层资源承受过高压力。
    • 何时触发流控? Apache RocketMQ 的消息流控触发条件如下:
      存储压力大: 参考消费进度管理的原理机制,消费者分组的初始消费位点为当前队列的最大消费位点。若某些场景例如业务上新等需要回溯到指定时刻前开始消费,此时队列的存储压力会瞬间飙升,触发消息流控。
      服务端请求任务排队溢出: 若消费者消费能力不足,导致队列中有大量堆积消息,当堆积消息超过一定数量后会触发消息流控,减少下游消费系统压力。
      如何实现流控: 当满足消息流控的时候,有客户端发送消息时会立即返回流控错误码,使客户端根据指数退避算法计算重试间隔再次进行重试
      在这里插入图片描述
  • 消费者分类: Apache RocketMQ 支持 PushConsumer 、 SimpleConsumer 以及 PullConsumer 这三种类型的消费者
    在这里插入图片描述

  • 消息过滤:消费者订阅了某个主题后,Apache RocketMQ 会将该主题中的所有消息投递给消费者。若消费者只需要关注部分消息,可通过设置过滤条件在 Apache RocketMQ 服务端进行过滤,只获取到需要关注的消息子集,避免接收到大量无效的消息

    • 过滤流程:消息过滤主要通过以下几个关键流程实现:
      生产者:生产者在初始化消息时预先为消息设置一些属性和标签,用于后续消费时指定过滤目标。
      消费者:消费者在初始化及后续消费流程中通过调用订阅关系注册接口,向服务端上报需要订阅指定主题的哪些消息,即过滤条件。
      服务端:消费者获取消息时会触发服务端的动态过滤计算,Apache RocketMQ 服务端根据消费者上报的过滤条件的表达式进行匹配,并将符合条件的消息投递给消费者。
    • 过滤方式:有tag和SQL两种方式(selectorExpression 属性配置):
      tag就是在发送消息的时候添加对应的tag,然后再客户端上报对应的过滤规则即可,SQL过滤需要参考对应的SQL92语法在这里插入图片描述
    • tag过滤:接收tagA或者tagB的消息
@Component
@RocketMQMessageListener(consumerGroup = "consumerGroup", topic = "topic", selectorExpression = "tagA || tagB" ,selectorType = SelectorType.TAG)
public class TagFilterConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        System.out.println("Received message with tag filter: " + message);
    }
}

sql 过滤:只接收消息header里面的存在 key = 'value’的消息

@Component
@RocketMQMessageListener(consumerGroup = "consumerGroup", topic = "topic", selectorExpression = "key = 'value'", selectorType = SelectorType.SQL92)
public class SQLFilterConsumer implements RocketMQListener<String> {

    @Override
    public void onMessage(String message) {
        System.out.println("Received message with SQL filter: " + message);
    }
}
  • 消费者负载均衡:通过消费者负载均衡策略,可将主题内的消息分配给指定消费者分组中的多个消费者共同分担,提高消费并发能力和消费者的水平扩展能力。

    • 消费组内共享消费场景下(通常是指消费者组内存在多个消费者),消费者分组内多个消费者共同分担消息,消息按照哪种逻辑分配给哪个消费者,就是由消费者负载均衡策略所决定的。
      根据消费者类型的不同,消费者负载均衡策略分为以下两种模式:
      消息粒度负载均衡:PushConsumer和SimpleConsumer默认负载策略
      队列粒度负载均衡:PullConsumer默认负载策略
    • 消息粒度负载均衡: 消息粒度负载均衡策略中,同一消费者分组内的多个消费者将按照消息粒度平均分摊主题中的所有消息,即同一个队列中的消息,可被平均分配给多个消费者共同消费。
      • 策略特点:
        相对于队列粒度负载均衡策略,消息粒度负载均衡策略有以下特点:
        消费分摊更均衡: 对于传统队列级的负载均衡策略,如果队列数量和消费者数量不均衡,则可能会出现部分消费者空闲,或部分消费者处理过多消息的情况。消息粒度负载均衡策略无需关注消费者和队列的相对数量,能够更均匀地分摊消息。
        对非对等消费者更友好: 在线上生产环境中,由于网络机房分区延迟、消费者物理资源规格不一致等原因,消费者的处理能力可能会不一致,如果按照队列分配消息,则可能出现部分消费者消息堆积、部分消费者空闲的情况。消息粒度负载均衡策略按需分配,消费者处理任务更均衡。
        队列分配运维更方便: 传统基于绑定队列的负载均衡策略必须保证队列数量大于等于消费者数量,以免产生部分消费者获取不到队列出现空转的情况,而消息粒度负载均衡策略则无需关注队列数。
    • 队列粒度负载均衡: 队列粒度负载均衡策略中,同一消费者分组内的多个消费者将按照队列粒度消费消息,即每个消息队列仅被一个消费者消费。
      • 策略特点:相对于消息粒度负载均衡策略,队列粒度负载均衡策略分配粒度较大,不够灵活。但该策略在流式处理场景下有天然优势,能够保证同一队列的消息被相同的消费者处理,对于批量处理、聚合处理更友好。
  • 消息进度管理:消息是按到达服务端的先后顺序存储在指定主题的多个队列中,每条消息在队列中都有一个唯一的Long类型坐标,这个坐标被定义为消息位点。
    任意一个消息队列在逻辑上都是无限存储,即消息位点会从0到Long.MAX无限增加。通过主题、队列和位点就可以定位任意一条消息的位置。
    消费位点管理消息的消费进度。每条消息被某个消费者消费完成后不会立即在队列中删除,Apache RocketMQ 会基于每个消费者分组维护一份消费记录,该记录指定消费者分组消费某一个队列时,消费过的最新一条消息的位点,即消费位点。最新的消息为最大消息位点,最早的消息为最小消息位点。可以因为业务、物理空间等去进行重置消费位点

  • 消费重试: 消费者出现异常,消费某条消息失败时, Apache RocketMQ 会根据消费重试策略重新投递该消息进行故障恢复。消费者在消费某条消息失败后,Apache RocketMQ 服务端会根据重试策略重新消费该消息,超过一次定数后若还未消费成功,则该消息将不再继续重试,直接被发送到死信队列中。通过消费死信队列的消息进行业务恢复

    • 重试策略

      • 消息重试的触发条件
        消费失败,包括消费者返回消息失败状态标识或抛出非预期异常。
        消息处理超时,包括在PushConsumer中排队超时。
      • 消息重试策略主要行为
        重试过程状态机:控制消息在重试流程中的状态和变化逻辑。
        重试间隔:上一次消费失败或超时后,下次重新尝试消费的间隔时间。
        最大重试次数:消息可被重试消费的最大次数。

      在这里插入图片描述

    • 具体流程:消费重试策略的基本流程:

      • 消费者收到消息:消费者从 RocketMQ Broker 收到待消费的消息。

      • 消费消息:消费者尝试对消息进行消费,执行业务逻辑。

      • 消费成功:如果消息成功被消费,消费者会向 Broker 发送消息消费成功的确认。

      • 消费失败:如果消息消费失败,消费者会根据配置的重试策略进行重试。

      • 消费者会根据配置的最大重试次数和重试间隔来决定是否进行重试。
        重试间隔可以是固定的时间间隔,也可以是根据指数增长的时间间隔。
        消费者可以选择在本地进行重试,也可以选择将失败的消息发送到另一个消费者进行重试。

      • 重试次数达到限制:如果重试次数达到了配置的最大重试次数,消费者会放弃继续重试,并根据配置决定是否记录失败的消息或进行其他处理。

      • 消息进入 DLQ(死信队列):如果启用了 DLQ 功能,在消费者重试次数达到限制后,消息会被发送到 DLQ 中,以便进一步处理。

  • 消息存储和清理机制:

    • 消息存储:Apache RocketMQ 使用存储时长作为消息存储的依据,即每个节点对外承诺消息的存储时长。在存储时长范围内的消息都会被保留,无论消息是否被消费;超过时长限制的消息则会被清理掉。
    • 消息存储管理粒度:Apache RocketMQ 按存储节点管理消息的存储时长,并不是按照主题或队列粒度来管理。
    • 消息存储判断依据:消息存储按照存储时间作为判断依据,相对于消息数量、消息大小等条件,使用存储时间作为判断依据,更利于业务方对消息数据的价值进行评估。
    • 消息存储和是否消费状态无关:Apache RocketMQ 的消息存储是按照消息的生产时间计算,和消息是否被消费无关。按照统一的计算策略可以有效地简化存储机制。
    • 清理机制:在 Apache RocketMQ中,消息保存时长并不能完整控制消息的实际保存时间,因为消息存储仍然使用本地磁盘,本地磁盘空间不足时,为保证服务稳定性消息仍然会被强制清理,导致消息的实际保存时长小于设置的保存时长。

四、Spring boot集成RocketMQ案例

(0)引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>producer</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>producer</name>
    <description>producer</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <version>2.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

(一)发送消息

构建消息的两种方式:

// 第一种
    private Message  builderMsg(String topic,Object msg,String key) {
        Message<Object> message = MessageBuilder
                .withPayload(msg) // 消息内容
                .setHeader(RocketMQHeaders.TOPIC, topic) // 设置消息topic
                .setHeader(RocketMQHeaders.KEYS, key) // 设置消息的key
                .build();// 最后构建出消息对象
        return message;
    }
// 第二种
     private org.apache.rocketmq.common.message.Message builderMessage(String topic,String tags,Object msg,String key){
        org.apache.rocketmq.common.message.Message message = new org.apache.rocketmq.common.message.Message();
        message.setKeys(key);
        message.setTopic(topic);
        message.setBody(JSON.toJSONBytes(msg));
        message.setTags(tags);
        message.setDeliverTimeMs(System.currentTimeMillis());
        return message;
    }

首先看生成的消息对象的内容来看:第一种没有tags,因为我像设置key的那个方式去设置不生效,需要使用下面的方法来实现发送携带tags:使用英文:来进行并在topic后面

    public void sendMsg(String topic,String tags,Object msg,String key){
        try {
            Message message = builderMsg(topic, msg, key);
            // 如果有tags,则使用英文:来进行并在topic后面
            if (tags!=null && !"".equals(tags)){
                rocketMQTemplate.syncSend(topic+":"+tags,message);
            } else {
                rocketMQTemplate.syncSend(topic,message);
            }
        } catch (Exception e){
            log.error("Failed to send message to RocketMQ: {}", e.getMessage());
        }
    }

区别:

  • 第一种是Spring的message,所以使用第一种方式必须是基于spring的,而且呢在底层也是去生成了rocketmq的message的可以说是一种spring基于rocketmq的封装。第二种是直接使用 Apache RocketMQ 的 Message 对象来构建消息。这种方式更加底层,适用于与 RocketMQ 客户端或原生 API 进行集成的场景,如使用 RocketMQ 的事务消息、定时消息等特性。

  • 消息体内容不同,如下:

    spring boot集成的发送方法消息为:在这里插入图片描述
    原生方法:

选用哪种?如果是Spring Boot框架则采用第一种更好,如果是原生的则使用第二种。
在这里插入图片描述

我呢采用的是Spring boot框架因此使用第一种
发送消息的工具类(可以拿去看一下,看看各类消息如何实现发送)
Spring boot集成了rocketmq,因此我们引入依赖直接使用rocketmq即可

RocketMqTemplate常见的方法:

  • 异步发送消息(asyncSend):
    ListenableFuture asyncSend(Message<?> message);
    message:要发送的消息对象。
    使用方法示例:
rocketMQTemplate.asyncSend(message, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        // 处理发送成功逻辑
    }

    @Override
    public void onException(Throwable throwable) {
        // 处理发送失败逻辑
    }
});
  • 同步发送消息(syncSend):
    SendResult syncSend(Message<?> message);
    message:要发送的消息对象。
    使用方法示例:
SendResult sendResult = rocketMQTemplate.syncSend(message);
  • 发送事务消息(sendMessageInTransaction):
    TransactionSendResult sendMessageInTransaction(String destination, Message<?> message, Object arg);
    destination:目标主题。
    message:要发送的消息对象。
    arg:用于传递给事务监听器的参数。
    使用方法示例:
TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction("topic", message, null);
  • 单向发送消息(sendOneWay):
    void sendOneWay(String destination, Message<?> message);
    destination:目标主题。
    message:要发送的消息对象。
    使用方法示例:
rocketMQTemplate.sendOneWay("topic", message);
  • convertAndSend:将对象转换为消息并发送。
    destination:目标主题。
    payload:要发送的消息对象。
void convertAndSend(String destination, Object payload);

工具类:

package com.example.util;
import com.alibaba.fastjson.JSON;
import com.example.listener.TransactionListenerImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.MessageConst;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;

import java.util.Date;


/**
 * @Classname RockeMQUtil
 * @Description TODO
 * @Version 1.0.0
 * @Date 2023/5/16 11:03
 * @Created by wlh12
 */
@Component
@Slf4j
public class RockeMQUtil {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private TransactionListenerImpl transactionListener;
    /**
     * 发送普通消息
     * @param topic
     * @param tags
     * @param msg
     * @param key
     */
    public void sendMsg(String topic,String tags,Object msg,String key){
        try {
            String description = createDescription(topic, tags);
            Message message = builderMsg(topic, msg, key);
            // syncSend方法是同步asyncSend是异步方法
            rocketMQTemplate.syncSend(description,message);
        } catch (Exception e){
            log.error("Failed to send message to RocketMQ: {}", e.getMessage());
        }
    }
    /**
     * 异步消息
     * @param topic
     * @param tags
     * @param msg
     * @param key
     */
    public void asyncSendMsg(String topic,String tags,Object msg,String key){
        try {

            String description = createDescription(topic, tags);
            Message message = builderMsg(topic, msg, key);
            rocketMQTemplate.asyncSend(description, message, new SendCallback() {
                @Override
                public void onSuccess(SendResult sendResult) {
                    log.info("messageId:"+sendResult.getMsgId()+"的异步消息发送成功");
                }
                @Override
                public void onException(Throwable throwable) {
                    log.error("异步消息发送失败",throwable);
                }
            });
        } catch (Exception e){
            log.error("Failed to send message to RocketMQ: {}", e.getMessage());
        }
    }

    /**
     * 异步定时/延时消息
     * @param topic
     * @param tags
     * @param msg
     * @param key
     */
    public void delaySendMsg(String topic,String tags,Object msg,String key){
        try {
            Message message = builderMsg(topic, msg, key);
            String description = createDescription(topic, tags);
            rocketMQTemplate.syncSend(description,message,1000,3);
        } catch (Exception e){
            log.error("Failed to send message to RocketMQ: {}", e.getMessage());
        }
    }

    /**
     * 事务消息
     * @param topic
     * @param tags
     * @param msg
     * @param key
     */
    public void transactionSendMsg(String topic,String tags,Object msg,String key){
        try {
            Message message = builderMsg(topic, msg, key);
            String description = createDescription(topic, tags);
            rocketMQTemplate.sendMessageInTransaction(description,message,tags);
        } catch (Exception e){
            log.error("Failed to send message to RocketMQ: {}", e.getMessage());
        }
    }

    /**
     * 原生的构建
     * @param topic
     * @param tags
     * @param msg
     * @param key
     * @return
     */
    private org.apache.rocketmq.common.message.Message builderMessage(String topic,String tags,Object msg,String key){
        org.apache.rocketmq.common.message.Message message = new org.apache.rocketmq.common.message.Message();
        message.setKeys(key);
        message.setTopic(topic);
        message.setBody(JSON.toJSONBytes(msg));
        message.setTags(tags);
        message.setDeliverTimeMs(System.currentTimeMillis());
        return message;
    }

    /**
     * spring boot构建消息
     * @param topic
     * @param msg
     * @param key
     * @return
     */
    private Message  builderMsg(String topic,Object msg,String key) {
        Message<Object> message = MessageBuilder
                .withPayload(msg)
                .setHeader(RocketMQHeaders.TOPIC, topic)
                .setHeader(RocketMQHeaders.KEYS, key)
                .build();
        return message;
    }

    /**
     * 根据topic和tags返回Description
     * @param topic
     * @param tags
     * @return
     */
    private String createDescription(String topic,String tags) {
        String description = topic;
        if (tags!=null && !"".equals(tags)){
            description+=":"+tags;
        }
        return description;
    }
}

事务消息在生产者需要实现事务监听的,不然会异常

@Component
@RocketMQTransactionListener
public class TransactionListenerImpl implements RocketMQLocalTransactionListener {
    // 消费事务消息
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
        System.out.println("执行事务:"+msg);
        // RocketMQLocalTransactionState.COMMIT;提交事务。提交完之后消费者才能消费到这个消息
        // RocketMQLocalTransactionState.ROLLBACK;回滚事务,半事务消息取消,消费者无法消费这条消息
        // RocketMQLocalTransactionState.UNKNOWN;状态未知,需要进行回查()
        return RocketMQLocalTransactionState.COMMIT;
    }

    // 回查事务
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
        System.out.println("检查事务:"+msg);
        return RocketMQLocalTransactionState.COMMIT;
    }
}
(二)消费消息

普通消息可以通过普通的生产者(Producer)发送,消费者(Consumer)使用普通的消息监听器来接收和处理。

事务消息需要通过事务型生产者(Transaction Producer)来发送,并实现事务监听器(TransactionListener)来执行本地事务,并根据本地事务的执行结果提交或回滚事务。

延时/定时消息可以通过设置消息的延时等级来实现定时发送。消费者使用普通的消息监听器来接收和处理延时/定时消息。

顺序消息的发送方无法保证消息的顺序,因此消费者需要实现顺序消息监听器(MessageListenerOrderly)来按照消息的顺序进行消费,以保证顺序消息的顺序性。

因此下面的两种就能实现正确消费所有的消息了

1、所有类型都能消费(消费普通、延时、事务)
package com.example.consumer;

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;

/**
 * @Classname OrderTopicConsumer
 * @Description TODO 消费者(输入order消息组以及消费topic为order的消息)
 * @Version 1.0.0
 * @Date 2023/8/1 22:40
 * @Created by wlh12
 */
@Component
@RocketMQMessageListener(consumerGroup = "order",topic = "order" )
public class OrderTopicConsumer implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        System.out.println("OrderTopicConsumer消费了消息:"+message);
    }
}
2、消费顺序消息
@Component
@RocketMQMessageListener(topic = "topic" ,consumerGroup = "group")
public class OrderTopicConsumerOder implements MessageListenerOrderly {
    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
        // 处理消息

        return ConsumeOrderlyStatus.SUCCESS;
    }
}

五、集群以及高阶使用

1、集群模式

  • 单Master
    • 定义:一个Broker作为主服务,不设置任何Slave,存在单点故障问题,一个挂了整个消息服务都会挂掉。
  • 多Master
    • 定义:所有的Broker都是Master,不存在Slave。
    • 优点:配置会比较简单一些,如果单个Master挂掉或重启维护的话对应用是没有什么影响的。如果磁盘配置为RAID10(服务器的磁盘阵列模式)即使在机器宕机不可恢复的情况下,由于RAID10磁盘本身的可靠性,消息也不会丢失(异步刷盘丢失少量消息,同步刷盘一条不丢),这种Broker的集群模式性能相对来说是最高的。
    • 缺点:某台机器宕机期间,该broker上的消息都是不可订阅(不可消费)的,需要等待该broker恢复服务,对消息的实时性有影响。
  • 多Master多Slave异步复制
    • 定义:Broker集群中有多个Master,每个Master又配备了一个或多个Slave,Master节点与Slave节点之间采用的是异步复制策略,写入Master之后就返回ACK给生产者无需等待Slave同步消息,Master与Slave之间是主备关系,即Master负责消息的读写操作,Slave负责的是备份消息和在Master宕机后进项角色切换。
    • 优点:服务的可用性(Slave会自动切换为Master)以及数据的可用性以及性能都非常高。
    • 缺点:master宕机了,可能会丢失少量消息(异步复制多少可能因同步的时间导致丢失数据)。
  • 多Master多Slave同步双写
    • 定义:Broker集群中有多个Master,每个Master又配备了一个或多个Slave,Master节点与Slave节点之间采用的是同步复制策略,写入Master之后就需等待Slave同步消息完成之后才能返回生产者ACK,因此需要写入Master和Slave,这就是所谓的双写。
    • 优点:消息的安全性更高,不存在消息丢失的情况
    • 缺点:因为是同步操作,性能相对较低,并且。

~~ 未完待续 ~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值