Rocket事务消息用来解决什么问题的?[面试7.0]
RoketMq事务消息用来解决消息发送与业务的数据一致性问题,如:本地数据库提交事务后发送消息到消息队列
RocketMq的本地事务状态有多少种?[面试7.0]
COMMIT_MESSAGE # 提交消息
ROLLBACK_MESSAGE # 回滚消息
UNKNOW # 未知
RocketMq的原理是什么(RocketMq怎么保证生产者一定会成功发送消息)?[面试7.5]
RocketMq是基于2PC分布式事务的,见图
生产者发送一个prepare消息(半消息)到消息队列,消息队列返回act给生产者,此时消费者对这种状态的消息无法消费
生产者接下来会执行本地事务,执行完后发送commit或rollback,一般本地事务产生了异常会发送rollback给消息队列
消息队列若收到了commit,这时会直接发送给消费者执行消费逻辑,若收到的是rollback会标记为失败,并在一段时间后自动清除
若消息队列没有收到commit或rollback,这种情况要么是网络不好,要么是生产者挂了(特别是在本地事务已经提交后挂了,会导致数据不一致性),这时消息队列会走超时回查策略,且超时回查是任务调度不断重试,若网络恢复或生产者恢复后回查调用会直接访问到生产者
生产者接收到回查请求,就立即检查回查事务状态(这一步一般是查询数据库的记录状态是否已经被修改),若回查成功,生产者基于回查结果再次发送commit或rollback给消息队列,再由消息队列处理后续逻辑
RocketMq的本地事务的实现大致是怎么样的?[面试7.0]
@Service
public class UserRegTransactionListener implements TransactionListener {
@Autowired
private UserMapper userMapper;
@Override
@Transactional
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 从消息体中解析出数据
UserDto userDto = JSON.parseObject(msg.getBody(), UserDto.class);
User user = new User();
user.setEmail(userDto.getEmail());
user.setPassword(userDto.getPassword());
user.setSex(userDto.getSex());
user.setUsername(userDto.getUserName());
try {
userMapper.insert(user);
} catch (Throwable e) {
e.printStackTrace();
// 系统异常->指定消息状态为RollBack
return LocalTransactionState.ROLLBACK_MESSAGE;
}
// 返回UNKNOW->因为此时事务还未提交
return LocalTransactionState.UNKNOW;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
UserDto userDto = JSON.parseObject(msg.getBody(), UserDto.class);
User user = new User();
user.setUsername(userDto.getUserName());
// 若存在用户名->说明本地事务执行成功->可提交
if (userMapper.selectOne(user) != null) {
return LocalTransactionState.COMMIT_MESSAGE;
} else {
return LocalTransactionState.UNKNOW;
}
}
}
RocketMq提供了一个(事务监听接口)TransactionListener,当半消息发送完毕后,本地事务的实现可以定义一个本地事务消息监听器实现该接口,并在实现执行本地事务方法(executeLocalTransaction),该方法是开启本地数据库事务的