事务消息(半消息 + 回查)想让“本地事务提交”和“消息发出”要么都成要么都不成。它怎么保证?本地事务结果丢了怎么办?生产者崩溃后谁来回答?回查答不上来又会怎样?这篇用真实的 RocketMQ 和 MySQL,把这六种情形逐个跑出来,也撞上了一个新生产者收不到回查的坑。
0 ms [生产者] HALF_SENT o-1
0 ms [生产者] LOCAL_TX_BEGIN
1643 ms [生产者] LOCAL_TX_COMMITTED
1645 ms [生产者] SEND_RESULT SEND_OK local=COMMIT_MESSAGE
1656 ms [消费者] CONSUMED o-1
半消息发出后 1643 ms 本地事务才提交,这段时间消费者什么都没收到;提交后 13 ms 消费者收到 o-1;订单库里 o-1 有 1 行先把半消息发到 broker,此时消费者看不到;本地事务提交后返回 COMMIT_MESSAGE,消息才对消费者可见。
0 ms [生产者] HALF_SENT o-2
0 ms [生产者] LOCAL_TX_BEGIN
620 ms [生产者] LOCAL_TX_ROLLED_BACK
622 ms [生产者] SEND_RESULT SEND_OK local=ROLLBACK_MESSAGE
等了 5 秒:消费者收到 o-2 的次数 0;订单库里 o-2 有 0 行本地事务回滚返回 ROLLBACK_MESSAGE,半消息被丢弃,下游不会看到没成功的订单。
0 ms [生产者] HALF_SENT o-3
146 ms [生产者] LOCAL_TX_COMMITTED (listener returns UNKNOW)
149 ms [生产者] SEND_RESULT SEND_OK local=UNKNOW
5502 ms [生产者] CHECK_BACK o-3 times=1 订单存在=true -> COMMIT
5513 ms [消费者] CONSUMED o-3
回查发生在半消息发出 5502 ms 之后(transactionTimeOut = 3000 ms);回查 1 次;消费者收到 o-3 1 次本地事务结果没告诉 broker(UNKNOW),broker 等 transactionTimeOut 之后回查,生产者查库:订单在就 COMMIT。
0 ms [旧生产者] kill -9(订单已提交,事务结果没有告诉 broker)
385 ms [新生产者(没声明 topic)] PRODUCER_STARTED
订单库里 o-4 有 1 行;消费者收到 o-4 0 次;新生产者收到回查 0 次。broker 的 transaction.log 里和这条半消息有关的行(4 行):
22:16:28 WARN Transaction-msg-check-thread2 - Check transaction failed, channel is null. groupId=pg-order
22:16:31 WARN Transaction-msg-check-thread1 - Check transaction failed, channel is null. groupId=pg-order
22:16:34 WARN Transaction-msg-check-thread2 - Check transaction failed, channel is null. groupId=pg-order
22:16:37 INFO TransactionalMessageCheckService - Put checked-too-many-time half message to TRANS_CHECK_MAXTIME_TOPIC OK. Restored in queueOffset=7, co
0 ms [旧生产者] kill -9(o-4b,订单已提交)
461 ms [新生产者(声明了 topic)] PRODUCER_STARTED
2997 ms [新生产者(声明了 topic)] CHECK_BACK o-4b times=1 订单存在=true -> COMMIT
3003 ms [消费者] CONSUMED o-4b
订单库里 o-4b 有 1 行;消费者收到 o-4b 1 次 —— 声明了 topic 的新生产者启动时就取了路由、向 broker 注册,broker 才找得到“能回查的人”;事务结果由能查本地数据库的人在回查里补上kill -9 后换一个新生产者(同一个 group)上线。只启动、没声明 topic 的新生产者没有向 broker 注册,broker 回查找不到人,3 次后丢弃半消息,订单已提交而消息没发出;声明了 topic 的新生产者启动时取路由并注册,回查到达,按订单是否存在回答。
4368 ms [新生产者] CHECK_BACK o-5 times=1 订单存在=false -> ROLLBACK 等了 5 秒:订单库里 o-5 有 0 行;消费者收到 o-5 0 次
崩溃发生在提交之前,回查发现订单不在,回复 ROLLBACK,消息不发出,两边一致。
0 ms [生产者] HALF_SENT o-6
97 ms [生产者] LOCAL_TX_COMMITTED (listener returns UNKNOW)
99 ms [生产者] SEND_RESULT SEND_OK local=UNKNOW
3423 ms [生产者] CHECK_BACK o-6 times=1 -> UNKNOW(查不出来)
6426 ms [生产者] CHECK_BACK o-6 times=2 -> UNKNOW(查不出来)
9430 ms [生产者] CHECK_BACK o-6 times=3 -> UNKNOW(查不出来)
broker 的 transaction.log:22:17:07 INFO TransactionalMessageCheckService - Put checked-too-many-time half message to TRANS_CHECK_MAXTIME_TOPIC OK. Restored in queueOffset=15, c
回查共 3 次;消费者收到 o-6 0 次;订单库里 o-6 有 1 行(本地事务其实提交了,但消息被 broker 丢掉了:本地有订单、下游收不到)一直返回 UNKNOW,回查到上限后 broker 丢弃半消息:本地有订单、下游收不到。回查必须能给出确定答案。