本文已收录GitHub,更有互联网大厂面试真题,面试攻略,高效学习资料等
RocketMQ的事务是如何实现的?
首先我们来看 RocketMQ 的事务。我在之前的课程中,已经给大家讲解过 RocketMQ 事务的大致流程,这里我们再一起通过代码,重温一下这个流程。
public class CreateOrderService {
@Inject
private OrderDao orderDao;
//注入订单表的DAO
@Inject
private ExecutorService executorService;
//注入一个ExecutorService
private TransactionMQProducer producer;
//初始化transactionListener和producer
@Init
public void init() throws MQClientException {
TransactionListener transactionListener = createTransactionListener();
producer = new TransactionMQProducer("myGroup");
producer.setExecutorService(executorService);
producer.setTransactionListener(transactionListener);
producer.start();
}
//创建订单服务的请求入口
@PUT
@RequestMapping(...)
public Boolean createOrder(@RequestBody CreateOrderRequest request) {
//根据创建订单请求创建一条消息
Message msg = createMessage(request);
//发送事务消息
SendResult sendResult = producer.sendMessageInTransaction(msg, request);
//返回:事务是否成功
return sendResult.getSendStatus() == SendStatus.SEND_OK;
}
private TransactionListener createTransactionListener() {
return new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
CreateOrderRequest request = (CreateOrderRequest ) arg;
try {
//执行本地事务创建订单
orderDao.createOrderInDB(request);
//如果没抛异常说明执行成功,提交事务消息
return LocalTransactionState.COMMIT_MESSAGE;
}
catch (Throwable t) {
//失败则直接回滚事务消息
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
//反查本地事务
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
//从消息中获得订单ID
String orderId = msg.getUserProperty("orderId");
//去数据库中查询订单号是否存在,如果存在则提交事务;
//如果不存在,可能是本地事务失败了,也可能是本地事务还在执行,所以返回UNKNOW//(PS:这里RocketMQ有个拼写错误:UNKNOW)
return orderDao.isOrderIdExistsInDB(orderId)?
LocalTransactionState.COMMIT_MESSAGE: LocalTransactionState.UNKNOW;
}
}
;
}
//....
}
在这个流程中,我们提供一个创建订单的服务,功能就是在数据库中插入一条订单记录,并发送一条创建订单的消息,要求写数据库和发消息这两个操作在一个事务内执行,要么都成功,要么都失败。在这段代码中,我们首先在 init() 方法中初始化了 transactionListener和发生 RocketMQ 事务消息的变量 producer。真正提供创建订单服务的方法是createOrder(),在这个方法里面,我们根据请求的参数创建一条消息,然后调用RocketMQ producer 发送事务消息,并返回事务执行结果。
之后的 createTransactionListener() 方法是在 init() 方法中调用的,这里面直接构造一个匿名类