[RocketMQ]高度な使用法:4つの質問でトランザクションメッセージを詳細に説明しています

 

RocketMQと他のメッセージミドルウェアの最大の違いは、トランザクションメッセージをサポートしていることです。これは、分散トランザクションにおけるメッセージベースの結果整合性スキームでもあります。

1.トランザクションメッセージとは何ですか?

トランザクションメッセージ:トランザクション特性を持つメッセージ。つまり、プロデューサーがブローカーに送信された後、メッセージをロールバックまたは送信できます(コンシューマーはコミット後にのみ表示されます)。

2.トランザクションメッセージの用途は何ですか?

RocketMQの公式例:ユーザーAが注文を開始し、100元を支払い、操作の完了後に100ポイントを獲得します。アカウントサービスとメンバーシップサービスは、上記の問題に応じて、それぞれ独自のデータベースを持つ2つの独立したマイクロサービスモジュールです。これらの状況があります:

  • 最初にお金を差し引いてからメッセージを送信すると、お金が差し引かれたばかりで、マシンがダウンし、メッセージが送信されず、結果としてポイントが増加しなかった可能性があります。

  • 最初にメッセージを送信してからお金を差し引くと、ポイントが増える可能性がありますが、お金は差し引かれず、100ポイントが無料で他の人に与えられます。

// 先扣款,再加积分伪代码:
@Transational
pay() {
    mysql.payMoney() // 数据库中添加用户信息
    reduceRepo() // 数据库中减少库存  
}
-------------------------------------// 若先发消息再加积分,那在这行宕机怎么办?
producer.send(msg) // 发送100积分

==>したがって、上記の方法は実行不可能です。最初にメッセージ(プラスポイント)をブローカーに送信できますが、メッセージをコンシューマーの非表示状態に設定します

  • ローカルトランザクション(控除)が正常に処理されたら、コンシューマーに表示させます
  • ローカルトランザクション(控除)が失敗した場合、現在のメッセージはロールバックされます

ここで問題が発生している可能性があります。プロデューサーのローカルトランザクションが成功した後、ブローカーへのトランザクション確認メッセージの送信に失敗します。どうすればよいですか?
今回は、消費者がニュースを正常に消費できないことを意味します。したがって、RocketMQはメッセージレビューメカニズムを提供します。トランザクションメッセージが常に中間状態にある場合、ブローカーは再試行を開始して、ブローカーでのトランザクションの処理ステータスを照会します。トランザクションが成功したことが判明すると、現在のメッセージが表示されるように設定されます

全体的なモデル図は次のとおりです。

ここに画像の説明を挿入します

上記の例から、トランザクションメッセージは一般的にローカルトランザクションの前に使用されることがわかります。これはネストされたトランザクションとして理解することもでき、メッセージの送信は外部トランザクションであり、ローカルトランザクションはメモリトランザクションです。

3. Javaはトランザクションメッセージを使用しますか?

上記の例では、特定のコードを使用して実装する方法を見てみましょう。

TransactionProducer

public class TransactionProducer {
    public static void main(String[] args) throws Exception {  
        // 这里用的是事务Producer(TransactionMQProducer)
        TransactionMQProducer transactionProducer=new TransactionMQProducer("tx_producer_group");
        transactionProducer.setNamesrvAddr("43.105.136.120:9876");    
        // 自定义线程池,用于异步执行事务操作     
        transactionProducer.setExecutorService(Executors.newFixedThreadPool(10); );  
        // 核心!!添加事务消息监听     
        transactionProducer.setTransactionListener(new TransactionListenerLocal());
        transactionProducer.start();   
        
        for(int i=0;i<20;i++) {       
            String orderId= UUID.randomUUID().toString();    
            String body="{'operation':'doOrder','orderId':'"+orderId+"'}";     
            // 构建消息
            Message message = new Message("pay_tx_topic", "TagA",orderId, body.getBytes(RemotingHelper.DEFAULT_CHARSET));  
            // 发送消息, 注:是发送事务消息
            transactionProducer.sendMessageInTransaction(message, orderId+"&"+i);   
            Thread.sleep(1000); // 1秒一次
        }
    } 
}

TransactionListenerLocal(トランザクションメッセージコア)

// 本地事务监听,实现TransactionListener接口
public class TransactionListenerLocal implements TransactionListener {
    private static final Map<String,Boolean> results=new ConcurrentHashMap<>();
    
    // 执行本地事务   
    @Override   
    public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { 
        System.out.println(":执行本地事务:"+arg.toString());    
        String orderId=arg.toString();  
        // 模拟数据入库操作(成功/失败)
        boolean rs=saveOrder(orderId);
        return rs? LocalTransactionState.COMMIT_MESSAGE:LocalTransactionState.UNKNOW;  
        // 这个返回状态表示告诉broker这个事务消息是否被确认,允许给到consumer进行消费    
        // LocalTransactionState.ROLLBACK_MESSAGE 回滚    
        // LocalTransactionState.UNKNOW  未知   
    }  
    
    // 提供事务执行状态的回查方法,提供给broker回调   
    @Override   
    public LocalTransactionState checkLocalTransaction(MessageExt msg) {  
        String orderId=msg.getKeys();     
        System.out.println("执行事务执行状态的回查,orderId:"+orderId);  
        boolean rs=Boolean.TRUE.equals(results.get(orderId));   
        System.out.println("回调:"+rs);     
        return rs?LocalTransactionState.COMMIT_MESSAGE:     
      							  LocalTransactionState.ROLLBACK_MESSAGE;  
    }   
    
    private boolean saveOrder(String orderId){    
        //如果订单取模等于0,表示成功,否则表示失败  
        boolean success=Math.abs(Objects.hash(orderId))%2==0;   
        results.put(orderId,success);    
        return success;  
    } 
}

TransactionConsumer

public class TransactionConsumer {
    public static void main(String[] args) throws MQClientException, IOException {   
        DefaultMQPushConsumer defaultMQPushConsumer=new      
            DefaultMQPushConsumer("tx_consumer_group");   
        defaultMQPushConsumer.setNamesrvAddr("43.105.136.120:9876");       
        defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_O FFSET);   
        defaultMQPushConsumer.subscribe("pay_tx_topic","*");    
         
        defaultMQPushConsumer.registerMessageListener((MessageListenerConcurrently)
                  (msgs, context) -> {           
                      msgs.stream().forEach(messageExt -> {    
                          try {                 
                              String orderId=messageExt.getKeys();
                              // 拿到消息
                              String body=new String(messageExt.getBody(), 
                                                     RemotingHelper.DEFAULT_CHARSET); 
                              // 扣减库存
                              System.out.println("收到消息:"+body+",开始扣减库存:"+orderId);  
                          } catch (UnsupportedEncodingException e) {    
                              e.printStackTrace();                
                          }             
                      });   
                      return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;    
                  });     
        defaultMQPushConsumer.start();    
        System.in.read();  
    }
}

4.トランザクションメッセージの3つの状態?

上記のTransactionListenerLocalクラスでは、オーバーライドされた両方のメソッドがLocalTransactionStateを返す必要があることがわかりました。これは、この既存のトランザクションメッセージの処理方法をブローカーに指示することを意味します。

  1. ROLLBACK_MESSAGE:トランザクションをロールバックします

    executeLocalTransactionメソッドがROLLBACK_MESSAGEを返す場合、トランザクションが直接ロールバックされることを意味します

  2. COMMIT_MESSAGE:トランザクションをコミットします

  3. UNKNOW:ブローカーは、成功または失敗するまで、プロデューサーメッセージのステータスを定期的にチェックします。

    UNKNOWが返されると、ブローカーは一定期間後にcheckLocalTransactionをチェックバックし、checkLocalTransactionの返されるステータスに従ってトランザクション操作(ロールバックまたはコミット)を実行します。

例のように、ROLLBACK_MESSAGEが返されると、コンシューマーはメッセージを受信せず、チェックバック関数を呼び出しません。COMMIT_MESSAGEが返されると、トランザクションがコミットされ、コンシューマーがメッセージを受信します。UNKNOWを返すと、チェックが行われます。 -一定期間後にback関数が呼び出され、ステータスに応じて送信ステータスまたはロールバックステータスを返します。送信ステータスを返すメッセージはコンシューマーによって消費されるため、この時点でコンシューマーはメッセージの一部を消費できます。 。

おすすめ

転載: blog.csdn.net/qq_33762302/article/details/114858933