商城下单场景:下单->减库存->扣费
很容易想到利用mq来异步解决,先上一段伪代码
try{
在数据库生成订单操作
发mq消息到下游
}catch(Exception e){
conn.rollback();
}finally{
conn.commit();
}
这段程序的貌似是可以完成任务的。逻辑如下:
如果第一步生成订单操作失败了,就不会发消息。事务回滚。
如果发消息失败了,本地事务回滚。
如果第一步生成订单成功,发消息也成功,就提交本地事务,
下游成功,则整个事务成功,下游失败,自己重试。
那么问题在哪里呢?
问题1,mq有可能超时,但其实发成功了,按上边逻辑本地事务要回滚,这就破坏了数据完整性。
问题2,第一步生成订单没报错,第二步发消息也成功了,但是commit的时候失败了,这个时候,本地订单没创建,但是下游消息已经发出去了。
如何改进上边代码?
增加一个事务表(msgid,content,topic,status)
利用本地事务,在创建订单时,也新增事务表一条记录。此时不发消息。
同进程内有一单线程的线程池,不断查询事务表,查到事务记录,将事务记录,发送到mq中,传递给下游。
同时监听mq的回执,收到回执后删除本地事务表记录。
消费端要自己去重。
如果下游失败,记录错误日志,报警,人工介入。
部署多实例时,发送端也需要排重,避免重复提交到mq。