Spring Cloud Alibaba - 消息队列(三)(RocketMQ 消息发送(普通消息 / 顺序消息 / 事务消息))
回溯
上一篇博文,我们惊心动魄的讲解了 RocketMQ 的安装,接下来我们讲解它的使用。
此篇博文读者可参考文档 https://github.com/apache/rocketmq/tree/master/docs/cn
简单使用
环境准备
首先我们需要一个生产者和消费者 ,最好都注册在 nacos 上,如果读者有看过博主的博文,那么可以直接拿以前测试的项目进行测试。
消费者有一个方法能调用控制者。
配置使用
无论是生产者还是消费者,我们都要添加以下依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
生产者需要配置 rocketMQ服务的地址 , 以及 生产者组名
rocketmq:
name-server: 127.0.0.1:9876 #rocketMQ服务的地址
producer:
group: scz_group # 生产者组
编写测试代码
@Autowired
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("testNotParamFunction")
public String testNotParamFunction(){
rocketMQTemplate.convertAndSend("scz-topic","testmessage");
return "success";
}
rocketMQTemplate 是 rocketmq 提供的调用接口
scz-topic 是发送到 rocketmq 上对应存放的主题名字
testmessage 是测试用的信息,类型是Object
发送一下请求进行测试:
可以观察到消息有进来。
消费者 需要配置 rocketMQ服务的地址
rocketmq:
name-server: 127.0.0.1:9876 #rocketMQ服务的地址
启动一个监听者监听 rocketMQ 进行消费
@Slf4j
@Service
@RocketMQMessageListener(consumerGroup = "scz_group", topic = "scz-topic")
public class XfzService implements RocketMQListener<String> {
@Override
public void onMessage(String s) {
log.info("成功受到消费信息: "+s);
}
}
此类必须实现 RocketMQListener ,里面的泛型对应的是传入 rocketMQ 的消息。
需要填写 consumerGroup 和 topic 。
启动消费者后就会实时监听这个主题,进行消费、
普通消息
RocketMQ提供三种方式来发送普通消息:可靠同步发送、可靠异步发送和单向发送。
- 可靠同步发送:同步发送是指消息发送方发出数据后,会在收到接收方发回响应之后才发下一个数据包的通讯方式。(此种方式应用场景非常广泛,例如重要通知邮件、报名短信通知、营销短信系统等。)
- 可靠异步发送:异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。发送方通过回调接口接收服务器响应,并对响应结果进行处理。(异步发送一般用于链路耗时较长,对 RT 响应时间较为敏感的业务场景,例如用户视频上传后通知启动转码服务,转码完成后通知推送转码结果等。)
- 单向发送:单向发送是指发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不
等待应答。(适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。)
可靠同步发送
调用同步的方法:
@RequestMapping("testNotParamFunctionSync")
public String testNotParamFunctionSync(){
//arg[0] 主题 arg[1]信息 arg[2] 超时时间
SendResult sendResult = rocketMQTemplate.syncSend("scz_topic", "testmessage", 10000);
log.info(sendResult.toString());
return "success";
}
测试:
成功打印出返回的信息。
可靠异步发送
代码编写
@RequestMapping("testNotParamFunctionAsSync")
public String testNotParamFunctionAsSync(){
//arg[0] 主题 arg[1]信息 arg[2] 回调类
rocketMQTemplate.asyncSend("scz_topic", "testmessage", new SendCallback() {
//成功响应回调
@Override
public void onSuccess(SendResult sendResult) {
log.info("成功响应回调");
}
@Override
public void onException(Throwable throwable) {
log.info("失败响应回调");
}
});
return "success";
}
测试结果
单向发送
主要用于发送日志,因为无论是否成功,都不会再去管理它。
@RequestMapping("testNotParamFunctionSendOneWay")
public String testNotParamFunctionSendOneWay(){
//arg[0] 主题 arg[1]信息
rocketMQTemplate.sendOneWay("scz_topic", "testmessage");
return "success";
}
三种发送方式的对比
发送方式 | 发送 TPS | 发送结果反馈 | 可靠性 |
---|---|---|---|
同步发送 | 快 | 有 | 不丢失 |
异步发送 | 快 | 有 | 不丢失 |
单向发送 | 最快 | 无 | 可能丢失 |
顺序消息
顺序消息是消息队列提供的一种严格按照顺序来发布和消费的消息类型。
我们之前的普通消息,并不能保证消息的一致性,因为虽然rocketMq是先进先出的,但是它一个主题有多个队列,发送消息的时候是分散到多个队列中,所以消费者消费的时候会从多个队列取数据,导致并不是顺序的。
@RequestMapping("testNotParamFunctionSyncSendOrderly")
public String testNotParamFunctionSyncSendOrderly(){
//arg[0] 主题 arg[1]信息 arg[2] 放置一个随机的key就成
rocketMQTemplate.syncSendOrderly("scz_topic", "testmessage","random");
return "success";
}
事务消息
RocketMQ提供了事务消息,通过事务消息就能达到分布式事务的最终一致。
事务消息交互流程:
两个概念:
- 半事务消息:暂不能投递的消息,发送方已经成功地将消息发送到了RocketMQ服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
- 消息回查:由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,RocketMQ服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit 或是 Rollback),该询问过程即消息回查。
事务消息发送步骤:
- 发送方将半事务消息发送至RocketMQ服务端。
- RocketMQ服务端将消息持久化之后,向发送方返回Ack确认消息已经发送成功,此时消息为半事务消息。
- 发送方开始执行本地事务逻辑。
- 发送方根据本地事务执行结果向服务端提交二次确认(Commit 或是 Rollback),服务端收到Commit 状态则将半事务消息标记为可投递,订阅方最终将收到该消息;服务端收到 Rollback 状态则删除半事务消息,订阅方将不会接受该消息。
事务消息回查步骤:
- 在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达服务端,经过固定时间后服务端将对该消息发起消息回查。
- 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
- 发送方根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行操作。
代码编写如下:
首先编写发送事务消息:
@RequestMapping("testNotParamFunctionTransaction")
public String testNotParamFunctionTransaction(){
//arg[0] 组 arg[1]主题 arg[2] 信息 arg[3]参数
rocketMQTemplate.sendMessageInTransaction("scz_transaction_group","transaction_topic", MessageBuilder.withPayload("testmessage").build(),"testmessage");
return "success";
}
继续编写接收到 半事务消息发送成功 ,执行本地事务,以及检查本地事务状态的类
@Service
@RocketMQTransactionListener(txProducerGroup = "scz_transaction_group")
public class TransactionListener implements RocketMQLocalTransactionListener {
//执行本地事务
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object o) {
//执行本地事务成功 return RocketMQLocalTransactionState.COMMIT;
//执行本地事务失败 return RocketMQLocalTransactionState.ROLLBACK;
return null;
}
//消息回查
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
//回查消息成功 return RocketMQLocalTransactionState.COMMIT;
//回查消息失败 return RocketMQLocalTransactionState.ROLLBACK;
return null;
}
}
ps:这边的组名会和发送消息时填写的组名相同。
end:RocketMQ 讲解到此结束,过程中发先了一些控制台查询时会报的异常,暂时还没解决,若是解决了会对博文进行相对应的补充。