目次
学習する前に、rocketMq のトランザクション メッセージとは何なのかを理解しましょう。
なぜ ack メカニズムがあり、トランザクション メッセージも提案されています。rocketmq トランザクション メッセージは何をもたらしますか?
Springboot は RocketMq を統合してトランザクション メッセージを送信します
学習する前に、rocketMq のトランザクション メッセージとは何なのかを理解しましょう。
怠惰な性格を貫く。ここで他の人が書いたブログをチェックしてください! rocketmq トランザクション メッセージ
大まかなプロセスの概要
1) 送信者は「Prepare」メッセージを RocketMQ に送信します。
2) RocketMQ は、「確認対象」(通常は HalfTopic トピック <RMQ_SYS_TRANS_HALF_TOPIC> に書き込まれます) を正常に受信した後、メッセージが正常に送信されたことを送信者に返信します。
メッセージ送信の最初のフェーズが完了したとき。
送信者はローカル イベント ロジックの実行を開始します。
3) 送信者は、イベントの実行結果に従って、2 番目の確認 (コミットまたはロールバック) メッセージを RocketMQ に送信します。RocketMQ は、コミットを受信した後、第 1 段階のメッセージを配信可能としてマークします (これら
実際のトピック (RealTopic) は、メッセージが運用環境に入ると送信され、サブスクライバはメッセージを受信できるようになります。最初の段階のメッセージは、ロールバック状態が受信されると削除され、サブスクライバはメッセージを受信しません。 。
4) 異常な状況が発生した場合、ステップ 3 で送信された 2 回目の確認は最終的に RocketMQ に届かず、サーバーは一定時間後に「確認待ち」メッセージのチェックバック要求を開始します。
5) 送信者がメッセージのチェックバック リクエストを受信した後 (第 1 段階のメッセージを送信したプロデューサーが動作できない場合、チェックバック リクエストはプロデューサーと同じグループ内の他のプロデューサーに送信されます)、
対応するメッセージのローカルイベント実行結果を確認して、コミットロールバックステータスを返します。
なぜ ack メカニズムがあり、トランザクション メッセージも提案されています。rocketmq トランザクション メッセージは何をもたらしますか?
rocketMqの3つの送信方法の比較
一方向送信を除く 3 つの送信方法。送信エラーのフィードバックはありません。他の 2 つは、メッセージがサーバーに送信されたかどうかを知ることができます。
そうです。最初の 2 つのタイプは、メッセージの送信失敗も返します。実際のメッセージは mq に送信されています。現時点では、消費者は自ら冪等性を確保する必要があります。ここでは考慮しません。
次に、メッセージ確認メカニズムがあります。なぜまだニュースが流れているのか。トランザクション メッセージはどのようなシナリオで使用できますか?
考えた後、自分でシーンをシミュレーションしてみました。おそらく消費が成功したら在庫を差し引くようにメッセージをプッシュするのがビジネスなのだろう。単にメッセージ確認メカニズムを使用する場合。という状況もあるかもしれません。
1. 消費の成功。キューの送信が成功しました。大丈夫、たぶんそうなるよ
2. 消費に失敗しました。直接ロールバックすると、メッセージはプッシュされません。
3. 消費は成功しましたが、送信キューは失敗を返しました。額。これに対処する方法。
消費者向けビジネスを縮小しますか? 送信キューが失敗したため、全員が正常に支払いました。食べた肉を吐き出します。これは明らかに不可能です。
対処しないの?処理、在庫控除、ボーナスポイントはありません。リーダーに次のように答えて、リーダーがあなたに勝つかどうかを確認します。
例外を送信します。送信失敗テーブルを挿入しますか? これは可能だと思われます。多くの人が同じことをしているようですが、そうすることに問題はありますか?おそらく問題はありません。ただし、挿入失敗テーブルの挿入に失敗する可能性もあります。しかし、それはわずかな確率です。
ここにはニュースの現場が映っています。事のニュース。信頼できるニュースとしても理解できます。または解決策。少なくとも私はここでそう理解しています
コードを一言でまとめましょう!
Springboot は RocketMq を統合してトランザクション メッセージを送信します
ここではPomを紹介していますが、ここにブートスターターがあればネイティブクライアントを直接利用することができますが、先に実装を見たい場合は下記のエントリークラスを直接検索してください。
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
家にもっと近い
ビジネスをシミュレートします。注文トランザクションが成功し、注文テーブル order_info がデータを挿入し、フロー テーブル order_ Slip がレコードを挿入します。そしてメッセージキューをプッシュします。
public interface IOrderInfoService extends IService<OrderInfo> {
/**
* 交易成功 插入流水
* @param orderInfo
*/
void insertOrderInfo(OrderInfo orderInfo);
}
public interface IOrderSlipService extends IService<OrderSlip> {
/**
* 是否交易成功
* @param orderId
* @return
*/
boolean isExistTx(String orderId);
}
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
@Resource
private IOrderSlipService orderSlipService;
@Override
@Transactional(rollbackFor = Exception.class)
public void insertOrderInfo(OrderInfo orderInfo) {
baseMapper.insert(orderInfo);
OrderSlip orderSlip = new OrderSlip();
orderSlip.setOrderId(orderInfo.getOrderId());
orderSlip.setStatus(1);
orderSlipService.save(orderSlip);
}
}
@Service
public class OrderSlipServiceImpl extends ServiceImpl<OrderSlipMapper, OrderSlip> implements IOrderSlipService {
@Override
public boolean isExistTx(String orderId) {
QueryWrapper<OrderSlip> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id",orderId);
queryWrapper.eq("status",1);
OrderSlip orderSlip = baseMapper.selectOne(queryWrapper);
return (orderSlip!=null);
}
}
rocketMqTransactionListener の実装
@Component
@RocketMQTransactionListener
public class RocketMqTransactionListenerImpl implements RocketMQLocalTransactionListener{
@Resource
private IOrderInfoService orderInfoService;
@Resource
private IOrderSlipService orderSlipService;
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) {
try{
// 执行业务逻辑 控制本地事务
String jsonString = new String((byte[]) message.getPayload());
// 比如 订单交易-插入流水 (同一事务)
OrderInfo orderInfo = JSON.parseObject(jsonString, OrderInfo.class);
orderInfoService.insertOrderInfo(orderInfo);
return RocketMQLocalTransactionState.COMMIT;
}catch (Exception e){
System.out.println(e);
return RocketMQLocalTransactionState.ROLLBACK;
}
}
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
RocketMQLocalTransactionState state;
String jsonString = new String((byte[]) msg.getPayload());
OrderInfo orderInfo = JSON.parseObject(jsonString, OrderInfo.class);
boolean existTx = orderSlipService.isExistTx(orderInfo.getOrderId());
if(existTx){
state = RocketMQLocalTransactionState.COMMIT;
}else{
state = RocketMQLocalTransactionState.UNKNOWN;
}
return state;
}
}
コントローラーの実装
@RestController
@RequestMapping("/mq/product")
public class RocketMqProductController {
@Resource
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("/test")
public void myTestTran(){
OrderInfo orderInfo = new OrderInfo();
orderInfo.setGoodId(11);
orderInfo.setNum(2);
String orderId = UUID.randomUUID().toString().replaceAll("-","");
orderInfo.setOrderId(orderId);
Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(orderInfo)).build();
TransactionSendResult tx_product_msg = rocketMQTemplate.sendMessageInTransaction("tx_product_msg", message, null);
System.out.println("发送成功");
}
}
こちらがデモです。そして実現可能なこと。もちろん、制作上はそう簡単にはいきませんので、業務に応じて検討する必要があります。rocketMq はテンプレートを提供するだけです。
簡単に言えば、事業が成功すれば。ロケットのコミットが失敗しました。注文が成功したかどうかを確認するためにパイプラインインターフェイスを呼び出すタスクがスケジュールされていますが、もちろん実際にはそのような記録があるかどうかだけを判断することは不可能です。
これは単なる私の意見です。間違いがあればご指摘ください。再度、感謝します