• RocketMQ入门了解


    RocketMQ

    消息队列是一种先进先出的数据结构

    为什么使用RocketMQ

    • 应用解耦
      系统的耦合性越高,容错性就越低。以电商应用为例,用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验。
      在这里插入图片描述
      使用消息队列解耦合,系统的耦合性就会提高了。比如物流系统发生故障,需要几分钟才能来修复,在这段时间内,物流系统要处理的数据被缓存到消息队列中,用户的下单操作正常完成。当物流系统回复后,补充处理存在消息队列中的订单消息即可,终端系统感知不到物流系统发生过几分钟故障

    • 流量削峰
      应用系统如果遇到系统请求流量的瞬间猛增,有可能会将系统压垮。有了消息队列可以将大量请求缓存起来,分散到很长一段时间处理,这样可以大大提到系统的稳定性和用户体验。例如业务系统正常时段的QPS如果是1000,流量最高峰是10000,为了应对流量高峰配置高性能的服务器显然不划算,这时可以使用消息队列对峰值流量削峰
      在这里插入图片描述

    • 数据分发
      在这里插入图片描述
      通过消息队列可以让数据在多个系统更加之间进行流通。数据的产生方不需要关心谁来使用数据,只需要将数据发送到消息队列,数据使用方直接在消息队列中直接获取数据即可

    在这里插入图片描述

    RocketMQ优缺点比较

    优点

    解耦、削峰、数据分发

    缺点

    • 系统可用性降低
      系统引入的外部依赖越多,系统稳定性越差。一旦MQ宕机,就会对业务造成影响。
      如何保证MQ的高可用?
    • 系统复杂性提高
      MQ的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过MQ进行异步调用。
      如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性?
    • 一致性问题
      A系统处理完业务,通过MQ给B、C、D三个系统发消息数据,如果B系统、C系统处理成功,D系统处理失败。
      如何保证消息数据处理的一致性?

    关于MQ产品比较

    在这里插入图片描述

    RocketMQ安装

    • 安装包下载

    wget https://archive.apache.org/dist/rocketmq/4.7.1/rocketmq-all-4.7.1-bin-release.zip --no-check-certificate

    解压

    unzip rocketmq-all-4.7.1-bin-release.zip

    • 安装必备条件:Linux64位系统;jdk环境;maven环境(jdk和maven最好线上安装)
    • jdk下载

    yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel

    • maven下载

    yum install maven -y

    利用文件传输助手,例如xftp5传输到Linux服务器,进行解压;(本次将加压后的文件放在了虚拟机的 /usr/local 下)
    完成后可进行运行

    启动RocketMQ

    启动NameServer

    首先进入解压后的bin目录
    执行(保险起见启动命令用下文中4.7.1安装使用较为合适)
    一般启动

    nohup sh bin/mqnamesrv &

    4.7.1安装使用(本机使用)

    nohup sh mqnamesrv > nameser.log 2>&1 &

    其余启动

    将 nohup 的日志输出到 /dev/null,这个目录会让所有到它这的信息自动消失

    nohup sh mqnamesrv > /dev/null

    2>&1标识将错误输出重定向到标准输出

    nohup sh >> nameserver.log 2>&1 &

    1. 查看启动日志

    tail -f ~/logs/rocketmqlogs/namesrv.log

    启动broker

    一般启动

    nohup sh bin/mqbroker -n localhost:9876 &
    4.7.1安装使用
    nohup sh mqbroker -c …/conf/broker.conf > mqbroker.log 2>&1 &

    其余启动

    将 nohup 的日志输出到 /dev/null,这个目录会让所有到它这的信息自动消失

    nohup sh mqbroker -n localhost:9876 autoCreateTopicEnable=true > /dev/null 2> /dev/null &

    查看启动日志

    tail -f ~/logs/rocketmqlogs/broker.log

    关闭命令

    sh bin/mqshutdown namesrv
    sh bin/mqshutdown broker
    或者直接进入bin目录执行
    sh mqshutdown namesrv
    sh mqshutdown broker

    注意:其中启动两个命令和JVM大小有关,我们可以进行编辑runbroker.sh和runserver.sh进行修改
    在bin目录下进行

    vi runbroker.sh
    vi runserver.sh

    参考设置

    JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m
    -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m

    测试发送消息和接收消息

    进入安装的bin目录下
    测试发送消息

    1,设置环境变量
    export NAMESRV_ADDR=localhost:9876
    2,使用安装包demo发送消息
    sh tools.sh org.apache.rocketmq.example.quickstart.Producer

    测试接收消息

    设置环境变量
    export NAMESRV_ADDR=localhost:9876
    测试接收消息
    sh tools.sh org.apache.rocketmq.example.quickstart.Consumer

    遇错:若遇到 No route info this topic:text-topic
    在mq的conf目录下的broker.conf文件中加入如下图配置、然后重新启动mq
    在这里插入图片描述
    原博客内容:
    https://blog.csdn.net/qq_40346958/article/details/119979300

    各角色介绍

    • Producer:消息的发送者;举例:发信者
    • Consumer:消息接收者;举例:收信者
    • Broker:暂存和传输消息;举例:邮局
    • NameServer:管理Broker;举例:各个邮局的管理机构
    • Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
    • Message Queue:相当于是Topic的分区;用于并行发送和接收消息

    在这里插入图片描述
    简述:首先producer会去给broker发送消息,但是不知道给哪个broker发送;于是producer会先去找nameserver,让nameserver去给它分配一个broker的地址再去给相应的broker发送消息;(nameserver是broker的管理者)

    • nameserver是broker的管理者
      broker上报信息给nameserver
    • 关于consumer
      consumer会找broker去消费消息;但是他要去找哪一个broker去消费?那么它就要去找nameserver询问broker的地址
    • 总结:nameserver管理broker;producer发送消息;consumer接收消息

    集群特点

    • NameServer、producer和consumer可集群部署,节点之间无任何信息同步。

    • Broker部署相对复杂,Broker分为Master与Slave(主从节点Master主,主要是用于producer的写操作;Slave从,主要是用于consumer的读操作),一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。

    • Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

    • Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳(心跳机制方便于consumer接收消息;consumer可以自己从broker拉取消息,broker也可以主动向consumer发送消息)。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

    集群模式

    单master

    这种方式风险较大,一旦Broker重启或者宕机时,会导致整个服务不可用。不建议线上环境使用,可以用于本地测试。

    多master

    一个集群无Slave,全是Master,例如2个Master或者3个Master,这种模式的优缺点如下:

    • 优点:配置简单,单个Master宕机或重启维护对应用无影响,在磁盘配置为RAID10时,即使机器宕机不可恢复情况下,由于RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢),性能最高;
    • 缺点:单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到影响。

    多master多slave(异步)

    每个Master配置一个Slave,有多对Master-Slave,HA采用异步复制方式,主备有短暂消息延迟(毫秒级),这种模式的优缺点如下:

    • 优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,同时Master宕机后,消费者仍然可以从Slave消费,而且此过程对应用透明,不需要人工干预,性能同多Master模式几乎一样;
    • 缺点:Master宕机,磁盘损坏情况下会丢失少量消息。

    多master多slave(同步)

    每个Master配置一个Slave,有多对Master-Slave,HA采用同步双写方式,即只有主备都写成功,才向应用返回成功,这种模式的优缺点如下:

    • 优点:数据与服务都无单点故障,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高;
    • 缺点:性能比异步复制模式略低(大约低10%左右),发送单个消息的RT会略高,且目前版本在主节点宕机后,备机不能自动切换为主机。

    多master多slave(同步和异步比较)

    • 异步:producer给master(broker的主节点)发送消息之后,master(broker的主节点)自己存一份,分别给slave(master的从节点)和producer返回消息;效率更高
    • 同步:producer给master(broker的主节点)发送消息之后,master(broker的主节点)自己存一份,先给slave(master的从节点),让slave(master的从节点)先把消息存起来,然后给producer返回消息;

    双主双从工作流程(同步)

    在这里插入图片描述

    1. 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。
    2. Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。
    3. 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。
    4. Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。
    5. Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。

    集群搭建

    服务器环境

    准备两台服务器

    序号IP角色架构模式
    1192.168.138.135nameserver、brokerserverMaster1、Slave2
    2192.168.138.136nameserver、brokerserverMaster2、Slave1

    host添加信息

    vim /etc/hosts

    配置如下

    #nameserver
    192.168.138.135 rocketmq-nameserver1
    192.168.138.136 rocketmq-nameserver2
    #broker
    192.168.138.135 rocketmq-master1
    192.168.138.135 rocketmq-slave2
    192.168.138.136 rocketmq-master2
    192.168.138.136 rocketmq-slave1

    配置完成后,重启网卡

    systemctl restart network

    需要注意防火墙状态处于关闭状态

    #关闭防火墙
    systemctl stop firewalld.service
    #查看防火墙的状态
    firewall-cmd --state
    #禁止firewall开机启动
    systemctl disable firewalld.service

    配置环境变量

    配置环境变量就不需要进入bin目录下开启mq了

    vim /etc/profile

    末尾加入

    #set rocketmq
    ROCKETMQ_HOME=/usr/local/rocketmq/rocketmq-all-4.7.1-bin-release
    PATH= P A T H : PATH: PATH:ROCKETMQ_HOME/bin
    export ROCKETMQ_HOME PATH

    使配置生效

    source /etc/profile

    创建消息存储路径

    主节点存储

    mkdir /usr/local/rocketmq/store
    mkdir /usr/local/rocketmq/store/commitlog
    mkdir /usr/local/rocketmq/store/consumequeue
    mkdir /usr/local/rocketmq/store/index

    从节点存储

    mkdir /usr/local/rocketmq/store1
    mkdir /usr/local/rocketmq/store1/commitlog
    mkdir /usr/local/rocketmq/store1/consumequeue
    mkdir /usr/local/rocketmq/store1/index

    修改broker配置

    服务器192.168.138.135

    vi /usr/soft/rocketmq/conf/2m-2s-sync/broker-a.properties

    将文件中原来的注释掉,在最后增加如下配置

    #所属集群名字
    brokerClusterName=rocketmq-cluster
    #broker名字,注意此处不同的配置文件填写的不一样
    brokerName=broker-a
    #0 表示 Master,>0 表示 Slave
    brokerId=0
    #nameServer地址,分号分割
    namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
    #在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
    defaultTopicQueueNums=4
    #是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
    autoCreateTopicEnable=true
    #是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
    autoCreateSubscriptionGroup=true
    #Broker 对外服务的监听端口
    listenPort=10911
    #删除文件时间点,默认凌晨 4点
    deleteWhen=04
    #文件保留时间,默认 48 小时
    fileReservedTime=120
    #commitLog每个文件的大小默认1G
    mapedFileSizeCommitLog=1073741824
    #ConsumeQueue每个文件默认存30W条,根据业务情况调整
    mapedFileSizeConsumeQueue=300000
    #destroyMapedFileIntervalForcibly=120000
    #redeleteHangedFileInterval=120000
    #检测物理文件磁盘空间
    diskMaxUsedSpaceRatio=88
    #存储路径
    storePathRootDir=/usr/local/rocketmq/store
    #commitLog 存储路径
    storePathCommitLog=/usr/local/rocketmq/store/commitlog
    #消费队列存储路径存储路径
    storePathConsumeQueue=/usr/local/rocketmq/store/consumequeue
    #消息索引存储路径
    storePathIndex=/usr/local/rocketmq/store/index
    #checkpoint 文件存储路径
    storeCheckpoint=/usr/local/rocketmq/store/checkpoint
    #abort 文件存储路径
    abortFile=/usr/local/rocketmq/store/abort
    #限制的消息大小
    maxMessageSize=65536
    #flushCommitLogLeastPages=4
    #flushConsumeQueueLeastPages=2
    #flushCommitLogThoroughInterval=10000
    #flushConsumeQueueThoroughInterval=60000
    #Broker 的角色
    #- ASYNC_MASTER 异步复制Master
    #- SYNC_MASTER 同步双写Master
    #- SLAVE
    brokerRole=SYNC_MASTER
    #刷盘方式
    #- ASYNC_FLUSH 异步刷盘
    #- SYNC_FLUSH 同步刷盘
    flushDiskType=SYNC_FLUSH
    #checkTransactionMessageEnable=false
    #发消息线程池数量
    #sendMessageThreadPoolNums=128
    #拉消息线程池数量
    #pullMessageThreadPoolNums=128

    从配置修改

    vi /usr/soft/rocketmq/rocketmq-all-4.7.1-bin-release/conf/2m-2s-sync/broker-b-s.properties

    将文件中原来的注释掉,在最后增加如下配置

    #所属集群名字
    brokerClusterName=rocketmq-cluster
    #broker名字,注意此处不同的配置文件填写的不一样
    brokerName=broker-b
    #0 表示 Master,>0 表示 Slave
    brokerId=1
    #nameServer地址,分号分割
    namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
    #在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
    defaultTopicQueueNums=4
    #是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
    autoCreateTopicEnable=true
    #是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
    autoCreateSubscriptionGroup=true
    #Broker 对外服务的监听端口
    listenPort=11011
    #删除文件时间点,默认凌晨 4点
    deleteWhen=04
    #文件保留时间,默认 48 小时
    fileReservedTime=120
    #commitLog每个文件的大小默认1G
    mapedFileSizeCommitLog=1073741824
    #ConsumeQueue每个文件默认存30W条,根据业务情况调整
    mapedFileSizeConsumeQueue=300000
    #destroyMapedFileIntervalForcibly=120000
    #redeleteHangedFileInterval=120000
    #检测物理文件磁盘空间
    diskMaxUsedSpaceRatio=88
    #存储路径
    storePathRootDir=/usr/local/rocketmq/store1
    #commitLog 存储路径
    storePathCommitLog=/usr/local/rocketmq/store1/commitlog
    #消费队列存储路径存储路径
    storePathConsumeQueue=/usr/local/rocketmq/store1/consumequeue
    #消息索引存储路径
    storePathIndex=/usr/local/rocketmq/store1/index
    #checkpoint 文件存储路径
    storeCheckpoint=/usr/local/rocketmq/store1/checkpoint
    #abort 文件存储路径
    abortFile=/usr/local/rocketmq/store/abort
    #限制的消息大小
    maxMessageSize=65536
    #flushCommitLogLeastPages=4
    #flushConsumeQueueLeastPages=2
    #flushCommitLogThoroughInterval=10000
    #flushConsumeQueueThoroughInterval=60000
    #Broker 的角色
    #- ASYNC_MASTER 异步复制Master
    #- SYNC_MASTER 同步双写Master
    #- SLAVE
    brokerRole=SLAVE
    #刷盘方式
    #- ASYNC_FLUSH 异步刷盘
    #- SYNC_FLUSH 同步刷盘
    flushDiskType=ASYNC_FLUSH
    #checkTransactionMessageEnable=false
    #发消息线程池数量
    #sendMessageThreadPoolNums=128
    #拉消息线程池数量
    #pullMessageThreadPoolNums=128

    服务器192.168.138.136
    主配置修改

    vi /usr/soft/rocketmq/conf/2m-2s-sync/broker-b.properties

    将文件中原来的注释掉,在最后增加如下配置

    #所属集群名字
    brokerClusterName=rocketmq-cluster
    #broker名字,注意此处不同的配置文件填写的不一样
    brokerName=broker-b
    #0 表示 Master,>0 表示 Slave
    brokerId=0
    #nameServer地址,分号分割
    namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
    #在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
    defaultTopicQueueNums=4
    #是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
    autoCreateTopicEnable=true
    #是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
    autoCreateSubscriptionGroup=true
    #Broker 对外服务的监听端口
    listenPort=10911
    #删除文件时间点,默认凌晨 4点
    deleteWhen=04
    #文件保留时间,默认 48 小时
    fileReservedTime=120
    #commitLog每个文件的大小默认1G
    mapedFileSizeCommitLog=1073741824
    #ConsumeQueue每个文件默认存30W条,根据业务情况调整
    mapedFileSizeConsumeQueue=300000
    #destroyMapedFileIntervalForcibly=120000
    #redeleteHangedFileInterval=120000
    #检测物理文件磁盘空间
    diskMaxUsedSpaceRatio=88
    #存储路径
    storePathRootDir=/usr/local/rocketmq/store
    #commitLog 存储路径
    storePathCommitLog=/usr/local/rocketmq/store/commitlog
    #消费队列存储路径存储路径
    storePathConsumeQueue=/usr/local/rocketmq/store/consumequeue
    #消息索引存储路径
    storePathIndex=/usr/local/rocketmq/store/index
    #checkpoint 文件存储路径
    storeCheckpoint=/usr/local/rocketmq/store/checkpoint
    #abort 文件存储路径
    abortFile=/usr/local/rocketmq/store/abort
    #限制的消息大小
    maxMessageSize=65536
    #flushCommitLogLeastPages=4
    #flushConsumeQueueLeastPages=2
    #flushCommitLogThoroughInterval=10000
    #flushConsumeQueueThoroughInterval=60000
    #Broker 的角色
    #- ASYNC_MASTER 异步复制Master
    #- SYNC_MASTER 同步双写Master
    #- SLAVE
    brokerRole=SYNC_MASTER
    #刷盘方式
    #- ASYNC_FLUSH 异步刷盘
    #- SYNC_FLUSH 同步刷盘
    flushDiskType=SYNC_FLUSH
    #checkTransactionMessageEnable=false
    #发消息线程池数量
    #sendMessageThreadPoolNums=128
    #拉消息线程池数量
    #pullMessageThreadPoolNums=128

    从配置修改

    vi /usr/soft/rocketmq/conf/2m-2s-sync/broker-a-s.properties

    将文件中原来的注释掉,在最后增加如下配置

    #所属集群名字
    brokerClusterName=rocketmq-cluster
    #broker名字,注意此处不同的配置文件填写的不一样
    brokerName=broker-a
    #0 表示 Master,>0 表示 Slave
    brokerId=1
    #nameServer地址,分号分割
    namesrvAddr=rocketmq-nameserver1:9876;rocketmq-nameserver2:9876
    #在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
    defaultTopicQueueNums=4
    #是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
    autoCreateTopicEnable=true
    #是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
    autoCreateSubscriptionGroup=true
    #Broker 对外服务的监听端口
    listenPort=11011
    #删除文件时间点,默认凌晨 4点
    deleteWhen=04
    #文件保留时间,默认 48 小时
    fileReservedTime=120
    #commitLog每个文件的大小默认1G
    mapedFileSizeCommitLog=1073741824
    #ConsumeQueue每个文件默认存30W条,根据业务情况调整
    mapedFileSizeConsumeQueue=300000
    #destroyMapedFileIntervalForcibly=120000
    #redeleteHangedFileInterval=120000
    #检测物理文件磁盘空间
    diskMaxUsedSpaceRatio=88
    #存储路径
    storePathRootDir=/usr/local/rocketmq/store1
    #commitLog 存储路径
    storePathCommitLog=/usr/local/rocketmq/store1/commitlog
    #消费队列存储路径存储路径
    storePathConsumeQueue=/usr/local/rocketmq/store1/consumequeue
    #消息索引存储路径
    storePathIndex=/usr/local/rocketmq/store1/index
    #checkpoint 文件存储路径
    storeCheckpoint=/usr/local/rocketmq/store1/checkpoint
    #abort 文件存储路径
    abortFile=/usr/local/rocketmq/store1/abort
    #限制的消息大小
    maxMessageSize=65536
    #flushCommitLogLeastPages=4
    #flushConsumeQueueLeastPages=2
    #flushCommitLogThoroughInterval=10000
    #flushConsumeQueueThoroughInterval=60000
    #Broker 的角色
    #- ASYNC_MASTER 异步复制Master
    #- SYNC_MASTER 同步双写Master
    #- SLAVE
    brokerRole=SLAVE
    #刷盘方式
    #- ASYNC_FLUSH 异步刷盘
    #- SYNC_FLUSH 同步刷盘
    flushDiskType=ASYNC_FLUSH
    #checkTransactionMessageEnable=false
    #发消息线程池数量
    #sendMessageThreadPoolNums=128
    #拉消息线程池数量
    #pullMessageThreadPoolNums=128

    启动nameserver集群

    类似单机启动
    192.168.138.135和192.168.138.136分别启动nameserver
    进入bin目录

    cd /usr/local/rocketmq/bin

    启动

    nohup sh mqnamesrv &

    启动broker集群

    192.168.138.135进入bin目录启动master1和slave2
    master1

    nohup sh mqbroker -c /usr/local/rocketmq/rocketmq-all-4.7.1-bin-release/conf/2m-2s-sync/broker-a.properties &

    slave2

    nohup sh mqbroker -c /usr/local/rocketmq/rocketmq-all-4.7.1-bin-release/conf/2m-2s-sync/broker-b-s.properties &

    192.168.138.136进入bin目录启动master2和slave1
    master2

    nohup sh mqbroker -c /usr/local/rocketmq/rocketmq-all-4.7.1-bin-release/conf/2m-2s-sync/broker-b.properties &

    slave1

    nohup sh mqbroker -c /usr/local/rocketmq/rocketmq-all-4.7.1-bin-release/conf/2m-2s-sync/broker-a-s.properties &

    集群监控平台搭建

    1. 在https://github.com/apache/rocketmq-externals下载源码,其中里面用到的rocketmq-console是我们需要的
      在这里插入图片描述
      console包网盘地址
      链接:https://pan.baidu.com/s/1K6iFFSnPgM5atj2YJZcjOw
      提取码:i88v

    注意在打包前在rocketmq-console中配置namesrv地址
    rocketmq-externals-master\rocketmq-console\src\main\resources\application.properties
    在这里插入图片描述

    在pom.xml所在文件夹下git bash here
    在这里插入图片描述
    输入:

    mvn clean package -Dmaven.test.skip=true

    打包完成后会在pom.xml同级目录生成target,下面的rocketmq-console-ng-1.0.1.jar是我们需要传输的jar包
    在这里插入图片描述

    1. 利用远程连接工具将jar包传输至/usr/soft文件存储随意
      执行命令运行jar文件

    java -jar rocketmq-console-ng-1.0.1.jar

    1. 执行成功可以在本地浏览器访问http://192.168.138.135:8080进入控制台界面
      在这里插入图片描述

    消息发送步骤

    • 导入MQ客户端依赖
    <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-client</artifactId>
        <version>4.7.1</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 消息发送者步骤分析r
    1.创建消息生产者producer,并制定生产者组名
    2.指定Nameserver地址
    3.启动producer
    4.创建消息对象,指定主题Topic、Tag和消息体
    5.发送消息
    6.关闭生产者producer
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 消息消费者步骤分析
    1.创建消费者Consumer,制定消费者组名
    2.指定Nameserver地址
    3.订阅主题Topic和Tag
    4.设置回调函数,处理消息
    5.启动消费者consumer
    
    
    ##  发送同步消息
    ```java
    public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
            for (int i = 0; i < 10; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("springboot-mq", "Tag1", ("Hello World" + i).getBytes());
                //5.发送消息
                SendResult result = producer.send(msg);
                //发送状态
                SendStatus status = result.getSendStatus();
                //消息id
                String msgId = result.getMsgId();
                //消息接收队列的id
                int queueId = result.getMessageQueue().getQueueId();
    
                System.out.println("发送结果:" + result + status + msgId + queueId);
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(1);
            }
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    发送异步消息

    异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。

    public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
            for (int i = 0; i < 10; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("base", "Tag2", ("Hello World" + i).getBytes());
                //5.发送异步消息
                producer.send(msg, new SendCallback() {
                    /**
                     * 发送成功回调函数
                     * @param sendResult
                     */
                    public void onSuccess(SendResult sendResult) {
                        System.out.println("发送结果:" + sendResult);
                    }
    
                    /**
                     * 发送失败回调函数
                     * @param e
                     */
                    public void onException(Throwable e) {
                        System.out.println("发送异常:" + e);
                    }
                });
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(1);
            }
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    发送单向消息

    public static void main(String[] args) throws Exception, MQBrokerException {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
            for (int i = 0; i < 3; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("base", "Tag3", ("Hello World,单向消息" + i).getBytes());
                //5.发送单向消息
                producer.sendOneway(msg);
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(5);
            }
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    消息消费基本流程

    此过程暂时用到的是broker主动向consumer推

    public static void main(String[] args) throws MQClientException {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("base","Tag1");
            //4.设置回调函数,处理消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                //接收消息内容
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                    for (MessageExt msg : list) {
                        System.out.println("consumeThread=" + Thread.currentThread().getName() + "," + new String(msg.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //5.启动消费者consumer
            consumer.start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    消费消息

    一种是负载均衡一种是广播模式

    广播模式

    消费者都消费了一下发送者发来的消息
    在这里插入图片描述

    负载均衡模式

    共同承担消息的处理
    在这里插入图片描述

    分担了消息消费的压力
    消费者默认是负载均衡
    广播模式

    //广播模式消费
        consumer.setMessageModel(MessageModel.BROADCASTING);
    
    • 1
    • 2

    负载均衡模式

    consumer.setMessageModel(MessageModel.CLUSTERING);
    
    • 1

    顺序消息分析

    类似producer从创建订单到付款的操作;比如张三有创建,付款,完成三个步骤,李四同时也有这三个步骤;顺序消息就是,张三和李四分别在消息队列里各自执行自己的步骤互不干扰

    /**
     * 订单构建
     */
    public class OrderStep {
        private long orderId;
        private String desc;
    
        public long getOrderId() {
            return orderId;
        }
    
        public void setOrderId(long orderId) {
            this.orderId = orderId;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        @Override
        public String toString() {
            return "OrderStep{" +
                    "orderId=" + orderId +
                    ", desc='" + desc + '\'' +
                    '}';
        }
    
        public static List<OrderStep> buildOrders() {
            //  1039L   : 创建    付款 推送 完成
            //  1065L   : 创建   付款
            //  7235L   :创建    付款
            List<OrderStep> orderList = new ArrayList<OrderStep>();
    
            OrderStep orderDemo = new OrderStep();
            orderDemo.setOrderId(1039L);
            orderDemo.setDesc("创建");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1065L);
            orderDemo.setDesc("创建");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1039L);
            orderDemo.setDesc("付款");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(7235L);
            orderDemo.setDesc("创建");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1065L);
            orderDemo.setDesc("付款");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(7235L);
            orderDemo.setDesc("付款");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1065L);
            orderDemo.setDesc("完成");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1039L);
            orderDemo.setDesc("推送");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(7235L);
            orderDemo.setDesc("完成");
            orderList.add(orderDemo);
    
            orderDemo = new OrderStep();
            orderDemo.setOrderId(1039L);
            orderDemo.setDesc("完成");
            orderList.add(orderDemo);
    
            return orderList;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    public class Producer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
            //构建消息集合
            List<OrderStep> orderSteps = OrderStep.buildOrders();
            //发送消息
            for (int i = 0; i < orderSteps.size(); i++) {
                String body = orderSteps.get(i) + "";
                Message message = new Message("OrderTopic", "Order", "i" + i, body.getBytes());
                /**
                 * 参数一:消息对象
                 * 参数二:消息队列的选择器
                 * 参数三:选择队列的业务标识(订单ID)
                 */
                SendResult sendResult = producer.send(message, new MessageQueueSelector() {
                    /**
                     *
                     * @param mqs:队列集合
                     * @param msg:消息对象
                     * @param arg:业务标识的参数
                     * @return
                     */
                    @Override
                    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                        long orderId = Long.valueOf(String.valueOf(arg));
                        long index = orderId % mqs.size();
                        return mqs.get((int) index);
                    }
                }, orderSteps.get(i).getOrderId());
    
                System.out.println("发送结果:" + sendResult);
            }
            producer.shutdown();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    public class Consumer {
        public static void main(String[] args) throws MQClientException {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("OrderTopic", "*");
    
            //4.注册消息监听器
            consumer.registerMessageListener(new MessageListenerOrderly() {
    
                @Override
                public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.println("线程名称:【" + Thread.currentThread().getName() + "】:" + new String(msg.getBody()));
                    }
                    return ConsumeOrderlyStatus.SUCCESS;
                }
            });
    
            //5.启动消费者
            consumer.start();
    
            System.out.println("消费者启动");
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    延迟消息

    比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

    public class Producer {
    
        public static void main(String[] args) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
            for (int i = 0; i < 10; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("DelayTopic", "Tag1", ("Hello World" + i).getBytes());
                //设定延迟时间
                msg.setDelayTimeLevel(2);
                //5.发送消息
                SendResult result = producer.send(msg);
                //发送状态
                SendStatus status = result.getSendStatus();
    
                System.out.println("发送结果:" + result);
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(1);
            }
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    public class Consumer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("DelayTopic", "*");
    
            //4.设置回调函数,处理消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
    
                //接受消息内容
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.println("消息ID:【" + msg.getMsgId() + "】,延迟时间:" + (System.currentTimeMillis() - msg.getStoreTimestamp()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //5.启动消费者consumer
            consumer.start();
    
            System.out.println("消费者启动");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    批量消息

    批量发送消息能显著提高传递小消息的性能。限制是这些批量消息应该有相同的topic,相同的waitStoreMsgOK,而且不能是延时消息。此外,这一批消息的总大小不应超过4MB。总长度大于4M时需要进行分割

    public class Producer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
    
            List<Message> msgs = new ArrayList<Message>();
    
    
            //4.创建消息对象,指定主题Topic、Tag和消息体
            /**
             * 参数一:消息主题Topic
             * 参数二:消息Tag
             * 参数三:消息内容
             */
            Message msg1 = new Message("BatchTopic", "Tag1", ("Hello World" + 1).getBytes());
            Message msg2 = new Message("BatchTopic", "Tag1", ("Hello World" + 2).getBytes());
            Message msg3 = new Message("BatchTopic", "Tag1", ("Hello World" + 3).getBytes());
    
            msgs.add(msg1);
            msgs.add(msg2);
            msgs.add(msg3);
    
            //5.发送消息
            SendResult result = producer.send(msgs);
            //发送状态
            SendStatus status = result.getSendStatus();
    
            System.out.println("发送结果:" + result);
    
            //线程睡1秒
            TimeUnit.SECONDS.sleep(1);
    
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    public class Consumer {
        public static void main(String[] args) throws Exception {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("BatchTopic", "*");
    
            //4.设置回调函数,处理消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
    
                //接受消息内容
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.println("consumeThread=" + Thread.currentThread().getName() + "," + new String(msg.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //5.启动消费者consumer
            consumer.start();
    
            System.out.println("消费者启动");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    过滤消息

    tag过滤

    根据tag去生产和消费消息
    在这里插入图片描述
    在这里插入图片描述

    sql过滤

    类似于选择消息进行消费
    如下案例,消费者只能消费大于5的数据

    public class Producer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            DefaultMQProducer producer = new DefaultMQProducer("group1");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.启动producer
            producer.start();
    
            for (int i = 0; i < 10; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("FilterSQLTopic", "Tag1", ("Hello World" + i).getBytes());
    
                msg.putUserProperty("i", String.valueOf(i));
    
                //5.发送消息
                SendResult result = producer.send(msg);
                //发送状态
                SendStatus status = result.getSendStatus();
    
                System.out.println("发送结果:" + result);
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(2);
            }
    
            //6.关闭生产者producer
            producer.shutdown();
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    在这里插入代码片public class Consumer {
        public static void main(String[] args) throws Exception {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("FilterSQLTopic", MessageSelector.bySql("i>5"));
    
            //4.设置回调函数,处理消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
    
                //接受消息内容
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.println("consumeThread=" + Thread.currentThread().getName() + "," + new String(msg.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //5.启动消费者consumer
            consumer.start();
            System.out.println("消费者启动");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    事务消息

    事务消息共有三种状态,提交状态、回滚状态、中间状态:

    • TransactionStatus.CommitTransaction: 提交事务,它允许消费者消费此消息。
    • TransactionStatus.RollbackTransaction: 回滚事务,它代表该消息将被删除,不允许被消费。
    • TransactionStatus.Unknown: 中间状态,它代表需要检查消息队列来确定状态。
    public class Producer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消息生产者producer,并制定生产者组名
            TransactionMQProducer producer = new TransactionMQProducer("group5");
            //2.指定Nameserver地址
            producer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
    
            //添加事务监听器
            producer.setTransactionListener(new TransactionListener() {
                /**
                 * 在该方法中执行本地事务
                 * @param msg
                 * @param arg
                 * @return
                 */
                @Override
                public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                    if (StringUtils.equals("TAGA", msg.getTags())) {
                        return LocalTransactionState.COMMIT_MESSAGE;
                    } else if (StringUtils.equals("TAGB", msg.getTags())) {
                        return LocalTransactionState.ROLLBACK_MESSAGE;
                    } else if (StringUtils.equals("TAGC", msg.getTags())) {
                        return LocalTransactionState.UNKNOW;
                    }
                    return LocalTransactionState.UNKNOW;
                }
    
                /**
                 * 该方法时MQ进行消息事务状态回查
                 * @param msg
                 * @return
                 */
                @Override
                public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                    System.out.println("消息的Tag:" + msg.getTags());
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
            });
    
            //3.启动producer
            producer.start();
    
            String[] tags = {"TAGA", "TAGB", "TAGC"};
    
            for (int i = 0; i < 3; i++) {
                //4.创建消息对象,指定主题Topic、Tag和消息体
                /**
                 * 参数一:消息主题Topic
                 * 参数二:消息Tag
                 * 参数三:消息内容
                 */
                Message msg = new Message("TransactionTopic", tags[i], ("Hello World" + i).getBytes());
                //5.发送消息
                SendResult result = producer.sendMessageInTransaction(msg, null);
                //发送状态
                SendStatus status = result.getSendStatus();
    
                System.out.println("发送结果:" + result);
    
                //线程睡1秒
                TimeUnit.SECONDS.sleep(2);
            }
    
            //6.关闭生产者producer
            //producer.shutdown();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    /**
     * 消息的接受者
     */
    public class Consumer {
    
        public static void main(String[] args) throws Exception {
            //1.创建消费者Consumer,制定消费者组名
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
            //2.指定Nameserver地址
            consumer.setNamesrvAddr("192.168.138.137:9876;192.168.138.136:9876");
            //3.订阅主题Topic和Tag
            consumer.subscribe("TransactionTopic", "*");
    
    
            //4.设置回调函数,处理消息
            consumer.registerMessageListener(new MessageListenerConcurrently() {
    
                //接受消息内容
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    for (MessageExt msg : msgs) {
                        System.out.println("consumeThread=" + Thread.currentThread().getName() + "," + new String(msg.getBody()));
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                }
            });
            //5.启动消费者consumer
            consumer.start();
            System.out.println("生产者启动");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 相关阅读:
    【小尘送书-第六期】《巧用ChatGPT轻松玩转新媒体运营》AI赋能运营全流程,帮你弯道超车、轻松攀登运营之巅
    Ubuntu/Debian Hat 系 Linux 使用
    Andorid项目源码(167套)
    3. 实战入门
    ProTable高级表格获取表单数据
    OpenCV颜色识别及应用
    NEFU数字图像处理(5)图像压缩编码
    机器学习知识点:模型加权集成7种方法
    高性能计算(HPC)存储高校科研应用分析
    华为认证 | 安全HCIP和数通HCIP,该怎么选?
  • 原文地址:https://blog.csdn.net/lm_0921_miaodameng/article/details/125216195