RocketMQ 消息队列
学习RocketMQ 消息队列,以及对于学习过程中的知识的总结归纳
前言
消息队列是一种先进先出的数据结构,消息队列有流量削峰、应用解耦和异步通信三个作用
内容
一、ActiveMQ、Kafka与RocketMQ
ActiveMQ 是 Apache 下的一个子项目。
优点:单机吞吐量每秒万级,时效性毫秒级,可用性高,基于主从架构实现高可用性,消息可靠性较低的概率丢失数据。支持多种语言、支持 Spring2 的特性、支持多种传送协议、支持通过 JDBC 和 journal 提供高速的消息持久化。
缺点:官方社区现在的维护越来越少;社区活跃度不高。
Kafka 是一个分布式消息发布订阅系统。为大数据而生的消息中间件,大数据的杀手锏
优点:单机吞吐量每秒百万级,时效性毫秒级,不会丢失数据,不会导致不可用
缺点:支持消息顺序,但是一台代理宕机后,就会产生消息乱序;消费失败不支持重试;社区更新较慢
RocketMQ 是阿里系下开源的一款分布式、队列模型的消息中间件,3.0 版本名称改为 RocketMQ,是阿里参照kafka 设计思想使用 java 实现的一套消息队列。
优点:单机吞吐量十万级,时效性毫秒级,消息可以做到 0 丢失,支持 10 亿级别的消息堆积
缺点:支持的客户端语言不多,目前是 java 及 c++;社区活跃度一般;
Cloud Stream 屏蔽底层消息中间件的差异,降低切换版本,统一消息的编程模型通过定义绑定器 Binder 作为中间件,实现了应用程序与消息中间件细节之间的隔离通过向应用程序暴露统一的 Channel 通道,使得应用程序不需要再考虑各种不同消息中间件实现遵循发布-订阅模式,通过主题进行广播RocketMQ 主要由 Producer、Broker、Consumer 三部分组成,其中 Producer 负责生产消息,Consumer 负责消费消息,Broker 负责存储消息。
Consumer 支持 Push 和 Pull 两种消费模式,支持集群消费和广播消费。
Producer 支持以多种负载均衡的模式向 Broker 发送消息。
NameSever 需要独立服务部署,但是不妨碍节点互相通信构成集群。
NameSever 可以看作是轻量级的单体服务,主要的功能:
1、管理 Broker 集群,定期发送心跳包检测 Broker 是否存在。
2、为生产者和消费者以及 Broker 进行路由管理。
Broker 负责 Topic 和 Queue 的消息存储,支持推和拉两种模式。提供上亿级别的消息顺序消息堆积。此外提供可视化管理平台 rocketmq-console-ng,这些都是特有功能。

1、NameServer 启动,Broker 进行服务注册,NameServer 需要注册所有的 Broker。
2、服务发现,连接所有的生产者和消费者,并且定时进行心跳包发送。
3、生产者发送消息之前从 NameServer 获取 Broker 的注册列表,根据负载均衡算法选出其中一台 Broker 进行消息推送。
4、Nameserver 和 Broker 保持长连接,同时每 30S 进行一次心跳检测,如果检测到超过 120S 没有心跳,从路由表将其删除。
5、消费者订阅某个主题前,需要先从 NameSever 查找对应的 Broker 列表或者某个集群,从 Broker 当中订阅消息进行消费,同时由 Broker 指定订阅规则。
生产者是一类生产者的集合,这类生产者通常发送一类消息并且发送逻辑一致,所以将这些生产者分组在一起。从部署结构上看,生产者通过生产者组的名字来标识自己是一个群体。一个生产者组可以有很多生产者,只需要在创建生产者的时候指定生产者组,那么这个生产者就在那个生产者组消费者组是一类消费者的集合,这类消费者通常消费同一类消息,并且消费逻辑一致。RocketMQ 是通过这种分组机制,实现了天然的消息负载均衡。在消费消息时,通过消费者组实现了将消息分发到多个消费者服务器实例。每个消费者都有所在的消费者组,一个消费者组可以有很多的消费者,不同的消费者组消费消息是互不影响的。
二、RocketMQ使用
1、首先安装 rocketmq4.9.4,要求 JDK1.8,并需要配置环境变量 ROCKETMQ_HOME启动 namesrv 命令为 start mqnamesrv.cmd再启动 mqbroker命令为 start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true,把 broker 注册给 namesrv 指定配置文件位置创建主题 topic 命令为 mqadmin updateTopic -n 127.0.0.1:9876 -c DefaultCluster -t test1 不能重复创建主题
关闭 NameServer 命令为 mqshutdown namesrv
关闭 Broker 命令为 mqshutdown broker
启动消费者
set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Consumer
启动生产者
set NAMESRV_ADDR=localhost:9876
tools.cmd org.apache.rocketmq.example.quickstart.Producer
针对同一个 ConsumerGroup 中同一个消息只有一个消费者消费即可;针对不同的 ConsumerGroup 中,同一个消息,每个消费组中都有一个消费者需要消费。
RocketMQ 控制台 rocketmq-console-ng-1.0.1.jar,启动端口号 9999
依赖:

配置信息

默认 nameserver 的端口号为 9876,如果需要修改则在 rocket 的 conf 目录下添加 namesrv.properties 文件,文件中添加端口配置 listenPort=8876
修改 broker 连接 nameserver 端口号的配置方法 conf/broker.conf:namesrvAddr=localhost:8876
消息生产者
@Autowired
private RocketMQTemplate rocketMQTemplate;
rocketMQTemplate.convertAndSend("order-topic", order);下单成功之后,将消息放到 mq 中
消息消费者
1 @Service 2 @RocketMQMessageListener(consumerGroup=shop-user, topic=order-topic) 3 public class SmsService implements RocketMQListener<Order> { 4 public void onMessage(Order message){ 5 log.info("接收到订单信息{}", message); 6 } 7 }
消息发送
RocketMQ 支持 3 种消息发送方式:同步 sync、异步 async、单向 oneway
RocketMQ 可以发送可靠同步消息。可靠同步消息是指消息发送方发出消息后,会在收到接收方发回响应之后
才发送下一个数据包的通信方式。这种方式应用场景非常广泛,例如重要通知邮件、短信通知等
SendResult res =rocketMQTemplate.syncSend("test-topic-1", "这是一条同步消息");
System.out.println(res);

rocketMQTemplate.asyncSend("test-topic-1", "这是一条异步消息", new SendCallback() {
public void onSuccess(SendResult sendResult) { 成功响应的回调
System.out.println(sendResult);
}
public void onException(Throwable throwable) { 异常响应的回调
System.out.println(throwable);
}
}); 需要阻塞线程等待执行结束

RocketMQ 可以发送单向消息。单向发送是指发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。 适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集
rocketMQTemplate.sendOneWay("test-topic-1", "这是一条单向消息");
RocketMQ 可以发送单向消息。单向发送是指发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。 适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集
r
ocketMQTemplate.sendOneWay("test-topic-1", "这是一条单向消息");

顺序消息
顺序消息可以分为全局有序和分区有序,绝大部分场景下,分区有序就已经能够满足需求了。
1、全局有序:某个Topic下所有的消息都是有序的,所有消息按照严格的先进先出的顺序进行生产和消费,要求Topic下只能有一个分区队列,且Consumer只能有一个线程消费,适用对性能要求不高的场景。
2、分区有序:某个Topic下所有消息根据ShardingKey进行分区,相同ShardingKey的消息必须被发送到同一个分区队列,因为队列本身是可以保证先进先出的,此时只要保证Consumer同一个队列单线程消费即可。
RocketMQTemplate 提供三种发送顺序消息的方式:
1、syncSendOrderly():同步发送顺序消息
2、asyncSendOrderly():异步发送顺序消息
for (int i = 0; i < 10; i++)
rocketMQTemplate.syncSendOrderly("Demo:order", "同步顺序消息"+i, 0+"");
for (int i = 0; i < 10; i++) {
rocketMQTemplate.asyncSendOrderly("Demo:order", "异步顺序消息" + i, 0 + "", new SendCallback() {
public void onSuccess(SendResult sendResult) {
return;
}
public void onException(Throwable e) {
return;
}
});
}
for (int i = 0; i < 10; i++)
rocketMQTemplate.sendOneWayOrderly("Demo:order", "单向发送顺序消息" + i, 0 + "");
事务消息
RocketMQ 已经支持分布式事务消息,采用了 2PC 的思想来实现了提交事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息。

发送指定事务 id 的消息
Message msg = MessageBuilder.withPayload("事务消息" + i).setHeader(RocketMQHeaders.KEYS, i).build();
TransactionSendResult result = rocketMQTemplate.sendMessageInTransaction("Demo:tx", msg, null);
其次编写一个事务监听器类实现 RocketMQLocalTransactionListener 的执行本地事务方法和检查事务状态方法。当执行完本地事务方法后,消息的状态为 UNKNOWN 则会在指定次数下调用检查事务状态方法。
1 @Component 2 @RocketMQTransactionListener 3 public class TransactionListenerImpl implements RocketMQLocalTransactionListener { 4 public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {执行本地事逻 5 辑 6 MessageHeaders headers = msg.getHeaders();模拟当 key 为偶数时提交事务,为基数 UNKNOWN(在指定次数下检查当前事务状态) 7 String transactionId = (String) headers.get(RocketMQHeaders.PREFIX + 8 RocketMQHeaders.TRANSACTION_ID);获取事务 ID 9 String key = (String) headers.get(RocketMQHeaders.PREFIX + RocketMQHeaders.KEYS); 10 log.info("执行本地事务,事务 id:{},key:{}", transactionId, key); 11 if (Integer.parseInt(key) % 2 == 0) return RocketMQLocalTransactionState.COMMIT;执行成功,可以提交事务 12 else return RocketMQLocalTransactionState.UNKNOWN; } 13 public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { 模拟检查事务状态时回滚 14 MessageHeaders headers = msg.getHeaders(); 15 String transactionId = (String) headers.get(RocketMQHeaders.PREFIX + 16 RocketMQHeaders.TRANSACTION_ID); 17 String key = (String) headers.get(RocketMQHeaders.PREFIX + RocketMQHeaders.KEYS); 18 log.info("检查本地事务,事务 id:{},key:{}", transactionId, key); 19 return RocketMQLocalTransactionState.ROLLBACK; } 20 }
正常事务发送与提交阶段
1、生产者发送一个半消息给 MQServer,半消息是指消费者暂时不能消费的消息
2、服务端响应消息写入结果,半消息发送成功
3、开始执行本地事务
4、根据本地事务的执行状态执行 Commit 或者 Rollback 操作事务信息的补偿流程,补偿阶段主要是用于解决生产者在发送 Commit 或者 Rollback 操作时发生超时或失败的情况。
1、如果 MQServer 长时间没收到本地事务的执行状态会向生产者发起一个确认回查的操作请求
2、生产者收到确认回查请求后,检查本地事务的执行状态
3、根据检查后的结果执行 Commit 或者 Rollback 操作

浙公网安备 33010602011771号