SpringBoot整合RocketMQ事务/广播/顺序消息

95 篇文章 3 订阅
60 篇文章 2 订阅

环境:springboot2.3.9RELEASE + RocketMQ4.8.0


依赖

<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.0</version>
</dependency>

配置文件

server:
  port: 8080
---
rocketmq:
  nameServer: localhost:9876
  producer:
    group: demo-mq

普通消息

发送

@Resource
private RocketMQTemplate rocketMQTemplate ;
	
public void send(String message) {
  rocketMQTemplate.convertAndSend("test-topic:tag2", MessageBuilder.withPayload(message).build());
}

接受

@RocketMQMessageListener(topic = "test-topic", consumerGroup = "consumer01-group", selectorExpression = "tag1 || tag2")
@Component
public class ConsumerListener implements RocketMQListener<String> {

	@Override
	public void onMessage(String message) {
		System.out.println("接收到消息:" + message) ;
	}

}

顺序消息

发送

@Resource
private RocketMQTemplate rocketMQTemplate ;

public void sendOrder(String topic, String message, String tags, int id) {
	rocketMQTemplate.asyncSendOrderly(topic + ":" + tags, MessageBuilder.withPayload(message).build(), 
			"order-" + id, new SendCallback() {
				@Override
				public void onSuccess(SendResult sendResult) {
					System.err.println("msg-id: " + sendResult.getMsgId() + ": " + message +"\tqueueId: " + sendResult.getMessageQueue().getQueueId()) ;
				}
				@Override
				public void onException(Throwable e) {
					e.printStackTrace() ;
				}
			});
}

这里是根据hashkey将消息发送到不同的队列中

@RocketMQMessageListener(topic = "order-topic", consumerGroup = "consumer02-group", 
	selectorExpression = "tag3 || tag4", consumeMode = ConsumeMode.ORDERLY)
@Component
public class ConsumerOrderListener implements RocketMQListener<String> {

	@Override
	public void onMessage(String message) {
		System.out.println(Thread.currentThread().getName() + " 接收到Order消息:" + message) ;
	}

}

consumeMode = ConsumeMode.ORDERLY,指明了消息模式为顺序模式,一个队列,一个线程。

结果

SpringBoot整合RocketMQ事务/广播/顺序消息

 

当consumeMode = ConsumeMode.CONCURRENTLY执行结果如下:

SpringBoot整合RocketMQ事务/广播/顺序消息

 

 

集群/广播消息模式

发送端

@Resource
private RocketMQTemplate rocketMQTemplate ;
	
public void send(String topic, String message, String tags) {
	rocketMQTemplate.send(topic + ":" + tags, MessageBuilder.withPayload(message).build()) ;
}

集群消息模式

消费端

@RocketMQMessageListener(topic = "broad-topic", consumerGroup = "consumer03-group", 
	selectorExpression = "tag6 || tag7", messageModel = MessageModel.CLUSTERING)
@Component
public class ConsumerBroadListener implements RocketMQListener<String> {

	@Override
	public void onMessage(String message) {
		System.out.println("ConsumerBroadListener1接收到消息:" + message) ;
	}

}

messageModel = MessageModel.CLUSTERING

测试

启动两个服务分别端口是8080,8081

8080服务

SpringBoot整合RocketMQ事务/广播/顺序消息

 

8081服务

SpringBoot整合RocketMQ事务/广播/顺序消息

 

集群消息模式下,每个服务分别接收一部分消息,实现了负载均衡

广播消息模式

消费端

@RocketMQMessageListener(topic = "broad-topic", consumerGroup = "consumer03-group", 
	selectorExpression = "tag6 || tag7", messageModel = MessageModel.BROADCASTING)
@Component
public class ConsumerBroadListener implements RocketMQListener<String> {

	@Override
	public void onMessage(String message) {
		System.out.println("ConsumerBroadListener1接收到消息:" + message) ;
	}

}

messageModel = MessageModel.BROADCASTING

测试

启动两个服务分别端口是8080,8081

8080服务

SpringBoot整合RocketMQ事务/广播/顺序消息

 

8081服务

SpringBoot整合RocketMQ事务/广播/顺序消息

 

集群消息模式下,每个服务分别都接受了同样的消息。

事务消息

RocketMQ事务的3个状态


TransactionStatus.CommitTransaction:提交事务消息,消费者可以消费此消息

TransactionStatus.RollbackTransaction:回滚事务,它代表该消息将被删除,不允许被消费。
TransactionStatus.Unknown :中间状态,它代表需要检查消息队列来确定状态。

RocketMQ实现事务消息主要分为两个阶段:正常事务的发送及提交、事务信息的补偿流程 整体流程为:

正常事务发送与提交阶段

1、生产者发送一个半消息给MQServer(半消息是指消费者暂时不能消费的消息)
2、服务端响应消息写入结果,半消息发送成功
3、开始执行本地事务
4、根据本地事务的执行状态执行Commit或者Rollback操作

事务信息的补偿流程
1、如果MQServer长时间没收到本地事务的执行状态会向生产者发起一个确认回查的操作请求
2、生产者收到确认回查请求后,检查本地事务的执行状态
3、根据检查后的结果执行Commit或者Rollback操作
补偿阶段主要是用于解决生产者在发送Commit或者Rollback操作时发生超时或失败的情况。

发送端

@Resource
private RocketMQTemplate rocketMQTemplate ;
	
public void sendTx(String topic, Long id, String tags) {
	rocketMQTemplate.sendMessageInTransaction(topic + ":" + tags, MessageBuilder.withPayload(
			new Users(id, UUID.randomUUID().toString().replaceAll("-", ""))).
			setHeader("BID", UUID.randomUUID().toString().replaceAll("-", "")).build(), 
			UUID.randomUUID().toString().replaceAll("-", "")) ;
}

生产者对应的监听器

@RocketMQTransactionListener
public class ProducerTxListener implements RocketMQLocalTransactionListener {
	
	@Resource
	private BusinessService bs ;

	@Override
	public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
		// 这里执行本地的事务操作,比如保存数据。
		try {
			// 创建一个日志记录表,将这唯一的ID存入数据库中,在下面的check方法中可以根据这个id查询是否有数据
			String id = (String) msg.getHeaders().get("BID") ;
			Users users = new JsonMapper().readValue((byte[])msg.getPayload(), Users.class) ;
			System.out.println("消息内容:" + users + "\t参与数据:" + arg + "\t本次事务的唯一编号:" + id) ;
			bs.save(users, new UsersLog(users.getId(), id)) ;
		} catch (Exception e) {
			e.printStackTrace() ;
			return RocketMQLocalTransactionState.ROLLBACK ;
		}
		return RocketMQLocalTransactionState.COMMIT ;
	}

	@Override
	public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
		// 这里检查本地事务是否执行成功
		String id = (String) msg.getHeaders().get("BID") ;
		System.out.println("执行查询ID为:" + id + " 的数据是否存在") ;
		UsersLog usersLog = bs.queryUsersLog(id) ;
		if (usersLog == null) {
			return RocketMQLocalTransactionState.ROLLBACK ;
		}
		return RocketMQLocalTransactionState.COMMIT ;
	}

}

消费端

@RocketMQMessageListener(topic = "tx-topic", consumerGroup = "consumer05-group", selectorExpression = "tag10")
@Component
public class ConsumerTxListener implements RocketMQListener<Users> {

	@Override
	public void onMessage(Users users) {
		System.out.println("TX接收到消息:" + users) ;
	}

}

Service

@Transactional
public boolean save(Users users, UsersLog usersLog) {
	usersRepository.save(users) ;
	usersLogRepository.save(usersLog) ;
	if (users.getId() == 1) {
		throw new RuntimeException("数据错误") ;
	}
	return true ;
}
	
public UsersLog queryUsersLog(String bid) {
	return usersLogRepository.findByBid(bid) ;
}

Controller

@GetMapping("/tx/{id}")
public Object sendTx(@PathVariable("id")Long id) {
	ps.sendTx("tx-topic", id, "tag10") ;
	return "send transaction success" ;
}

测试

调用接口后,控制台输出:

SpringBoot整合RocketMQ事务/广播/顺序消息

 

从打印日志看出来都保存完毕了后 消费端才接受到消息。

SpringBoot整合RocketMQ事务/广播/顺序消息

 

SpringBoot整合RocketMQ事务/广播/顺序消息

 

删除数据,再测试ID为1会报错的。

SpringBoot整合RocketMQ事务/广播/顺序消息

 

数据库中没有数据。。。

是不是也不是很复杂,2个阶段来处理。

完毕!!!

给个关注+转发吧谢谢啊!!!

SpringBoot整合RocketMQ事务/广播/顺序消息

 

SpringBoot整合RocketMQ事务/广播/顺序消息

 

SpringBoot整合RocketMQ事务/广播/顺序消息

 

SpringBoot整合RocketMQ事务/广播/顺序消息

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot整合RocketMQ发送事务消息的步骤如下: 首先,定义一个生产者类,使用@Component注解将其标记为一个组件,并使用@Autowired注解注入RocketMQTemplate实例。在该类中,可以编写一个sendMsg方法来发送消息。该方法接受两个参数,分别是topic和msg,使用MessageBuilder构建一个消息对象,并通过rocketMQTemplate的sendMessageInTransaction方法发送消息。需要注意的是,该方法的第一个参数要与@RocketMQTransactionListener注解中的txProducerGroup属性保持一致。\[1\] 其次,定义一个消费者类,使用@Component和@RocketMQMessageListener注解将其标记为一个组件,并指定topic和consumerGroup。该类需要实现RocketMQListener接口,并实现其中的onMessage方法,用于处理接收到的消息。\[2\] 最后,在引导类中使用@SpringBootApplication注解标记该类为Spring Boot应用程序的入口,并在main方法中调用SpringApplication的run方法启动应用程序。\[3\] 通过以上步骤,就可以在SpringBoot整合RocketMQ发送事务消息了。 #### 引用[.reference_title] - *1* *2* *3* [springboot 整合 rocketmq 发送事务消息](https://blog.csdn.net/weixin_42494845/article/details/109362030)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值