手撕RocketMQ源码——TransactionMQProducer

引言

事务消息发送的实现

Demo方法

public static void main(String[] args) throws MQClientException, InterruptedException {
     
        TransactionListener transactionListener = new TransactionListenerImpl();
        TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name");
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
     
            @Override
            public Thread newThread(Runnable r) {
     
                Thread thread = new Thread(r);
                thread.setName("client-transaction-msg-check-thread");
                return thread;
            }
        });

        producer.setExecutorService(executorService);
        producer.setTransactionListener(transactionListener);
        producer.start();

        String[] tags = new String[] {
     "TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 10; i++) {
     
            try {
     
                Message msg =
                    new Message("TopicTest1234", tags[i % tags.length], "KEY" + i,
                        ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
                SendResult sendResult = producer.sendMessageInTransaction(msg, null);
                System.out.printf("%s%n", sendResult);

                Thread.sleep(10);
            } catch (MQClientException | UnsupportedEncodingException e) {
     
                e.printStackTrace();
            }
        }

        for (int i = 0; i < 100000; i++) {
     
            Thread.sleep(1000);
        }
        producer.shutdown();
    }

事务消息使用见 -> RocketMq 事务消息使用

订单子系统案例

RocketMQ技术内幕提供的事务消息案例

订单子系统创建订单,需要将订单数据下发到其他子系统这个场景。使用MQ来解耦上下游,并且保证分布式事务。

业务实现的步骤通常有下面几部

  • A系统创建订单并入库
  • 发送消息到MQ
  • MQ消费者消费消息,发送远程RPC服务调用,完成订单数据的同步

伪代码如下

public Map createOrder(){
     
	Map result = new HashMap();
	//执行下订单相关的业务流程,例如操作本地数据库落库
	//调用消息发送端API发送消息
	//返回结果 提交事务
	return result;
}

实现分布式事务常用的方案有两种本地消息表和消息中间件本身支持回查。

我们来讲讲RocketMQ4.3版本的事务消息

public Map createOrder(){
     
	Map result = new HashMap();
	//执行下订单相关的业务流程,例如操作本地数据库落库 
	Message msg = new Message(“topic”,JSON.toJSONString(order).getBytes());
	msg.putUserProperty("bizUniNo",id);
	txProductor.sendMessageInTransaction(msg,null);
	//返回结果 提交事务
	return result;
}
public class OrderTxListenerImpl implements TransactionListener{
     
	
	@Override
	LocalTransactionState executeLocalTransaction(final Message msg, final Object arg){
     
		String bizUniNo= msg.getUserProperty("bizUniNo");
		// 将bizUniNo入库,表名t_message_transaction 表结构 bizUniNo(主键),业务类型
		return LocalTransactionState.UNKNOW;
		
	}
	@Override
	LocalTransactionState checkLocalTransaction(final MessageExt msg){
     
	// 查询t_message_transaction表 如果存在则提交 返回commit_message
		// select count(1) from t_message_transaction a where a.biz_uni_no = #{bizUniNo}
		if(exist(msg.msg.getUserProperty("bizUniNo"))){
     
			return LocalTransactionState.COMMIT_MESSAGE;
		}
		return LocalTransactionState.ROLLBACK_MESSAGE;
	}
}

问题
此案例存在的问题就是,每次发送的消息都需要回查,那么比如会降低发送消息的吞吐量。增大了broker的性能消耗。
而且每次消息的延迟取决于broker回查的频率,此方案看似解决了分布式事务的问题,但实际带来的性能消耗过大,建议量小的系统可以使用。大吞吐量的系统还需要另寻他法,先留坑,后面来解决。
换个思路,其实不一定要用事务消息。使用分布式事务框架也是一种好的选择。

你可能感兴趣的:(RocketMQ源码分析)