分布式事务:解决方案之最大努力通知实战

1. 业务说明

下面我们通过RocketMQ中间件实现最大努力通知型分布式事务来模拟充值过程。我们有账户系统和充值系统两个微服务,其中账户系统对应的数据库是bank1,其中包括一条张三账户记录。充值系统的数据库使用pay数据库,记录了账户的充值记录。业务流程图如下:

在这里插入图片描述

2. 开发环境

  • 数据库:MySQL-5.6
  • 微服务框架:spring-boot-2.2.2.RELEASEspring-cloud-Hoxton.SR1rocketmq-spring-boot-2.0.2

2.1 启动程序

源码地址:https://gitee.com/anbang713/distributed-transaction-study

启动程序之前,我们需要使用源码工程中对应的sql脚本创建相对应的数据库和表,以及插入测试数据。

(1)首先我们要启动registry-server服务注册中心;

(2)分别启动notice-demo-banknotice-demo-pay微服务;

(3)启动rocketmq服务(可参考:《RocketMQ:快速入门》)。

3. 技术架构

在这里插入图片描述

交互流程如下:

(1)用户请求充值系统进行充值。

(2)充值系统完成充值将充值结果发给MQ。

(3)账户系统监听MQ,接收充值结果通知。如果接收不到消息,MQ会重复发送通知。接收到充值结果通知账户系统增加充值金额。

(4)账户系统也可以主动查询充值系统的充值结果查询接口,增加金额。

4. 数据结构

  • bank1数据库
DROP TABLE IF EXISTS `account_info`;

CREATE TABLE `account_info` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `account_name` VARCHAR(100) DEFAULT NULL,
  `account_no` VARCHAR(100) DEFAULT NULL,
  `account_balance` DOUBLE DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE de_duplication (
   tx_no VARCHAR(64) NOT NULL COMMENT '事务id',
   create_time DATETIME,
   PRIMARY KEY (`tx_no`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT  INTO `account_info`(`id`,`account_name`,`account_no`,`account_balance`) VALUES (1,'张三的账号','1',100);
  • pay数据库
DROP TABLE IF EXISTS `account_pay`;

CREATE TABLE `account_pay` (
  `id` VARCHAR(64) NOT NULL,
  `result` VARCHAR(100) DEFAULT NULL,
  `account_no` VARCHAR(100) DEFAULT NULL,
  `pay_amount` DOUBLE DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

5. 核心代码介绍

5.1 notice-demo-bank的核心代码

(1)mq监听充值结果消息

public void onMessage(AccountPay accountPay) {
    log.info("接收到消息:{}", JSON.toJSONString(accountPay));
    if("success".equals(accountPay.getResult())){
        //更新账户金额
        AccountChangeEvent accountChangeEvent = new AccountChangeEvent();
        accountChangeEvent.setAccountNo(accountPay.getAccountNo());
        accountChangeEvent.setAmount(accountPay.getPayAmount());
        accountChangeEvent.setTxNo(accountPay.getId());
        accountInfoService.updateAccountBalance(accountChangeEvent);
    }
    log.info("处理消息完成:{}", JSON.toJSONString(accountPay));
}

(2)远程调用查询充值结果

public AccountPay queryPayResult(String tx_no) {
    //远程调用
    AccountPay payresult = payClient.payresult(tx_no);
    if("success".equals(payresult.getResult())){
        //更新账户金额
        AccountChangeEvent accountChangeEvent = new AccountChangeEvent();
        accountChangeEvent.setAccountNo(payresult.getAccountNo());//账号
        accountChangeEvent.setAmount(payresult.getPayAmount());//金额
        accountChangeEvent.setTxNo(payresult.getId());//充值事务号
        updateAccountBalance(accountChangeEvent);
    }
    return payresult;
}

5.2 notice-demo-pay的核心代码

// 插入充值记录
@Override
public AccountPay save(AccountPay accountPay) {
    accountPay = accountPayDao.save(accountPay);
    //发送通知,使用普通消息发送通知
    accountPay.setResult("success");
    rocketMQTemplate.convertAndSend("topic_notifymsg",accountPay);
    return accountPay;
}

// 查询充值记录,接收通知方调用此方法来查询充值结果
@Override
public AccountPay findById(String txNo) {
    Optional<AccountPay> optional =  accountPayDao.findById(txNo);
    return optional.isPresent()?optional.get():null;
}

6. 测试

(1)充值

http://localhost:9011/pay/paydo

// 请求体
{
	"accountNo":1,
	"payAmount":1
}

(2)主动查询充值结果

http://localhost:9012/bank/payresult/{txNo}
——End——
更多精彩分享,可扫码关注微信公众号哦。

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值