springboot项目中使用seata实现分布式事务

前言

  • 什么是分布式事务
    在业务系统中,如果发起了一个rpc请求(远程调用请求)访问了其他的服务。
    那么该服务的事务我们调用方是无法控制的,比如回滚或者提交。
    所以这就涉及到数据到一致性问题。
    就如同下面这种情况

    此次业务调用中,发起者是Business,如果调用到rpc服务中,三有一是出现异常到情况,那么就需要统一回滚,就算之前有某个调用成功到服务,也需要回滚。
    事务要么一起成功,要么一起失败,所以数据才可以一致。

  • seate怎么实现分布式事务
    seate实现分布式事务的方法如下图

    其实就是维护”一个大的事务“,子事务成功,才可以全部提交。

开始

模拟场景

项目地址
参考里面的springboot


1. 安装一个mysql(我使用的是5.7)

2.初始化一个数据库用这sql文件 [sql/init_db.sql](https://github.com/seata/seata-samples/blob/master/springboot/src/main/resources/sql/initial_db.sql)  

3. 修改yml的配置,设置mysql的用户名和密码(https://github.com/seata/seata-samples/blob/master/springboot/src/main/resources/application.yml#L13-L35)
 
4. 设置dubbo的注册中心 `zookeeper://localhost:2181`

5.启动 seata server

6. 启动 SeataSpringbootApp .

7. 访问 http://127.0.0.1:9999/demo//asset/assign , 观察事务的一致性

说明:
1.zookeeper我使用的是zookeeper:3.5.5
2.seata server地址,启动seata/server/src/main/java/io/seata/server/Server.java

代码

SeataSpringbootApp主要逻辑
这是我们访问的接口

	@RequestMapping(value = "/asset/assign")
    @ResponseBody
    public String assetAssign() {
        LOGGER.info("welcome to deposit");

        String result;
        try {
            AssetAssign assetAssign = assignService.increaseAmount(
                ASSET_ID);
            result = assetAssign.toString();
        } catch (Exception e) {
            result = ExceptionUtils.getMessage(e);

        }
        return result;
    }

主要的业务方法是这个increaseAmount

    @Override
	@Transactional
	@GlobalTransactional
	public AssetAssign increaseAmount(String id) {
		LOGGER.info("Assign Service Begin ... xid: " + RootContext.getXID() + "\n");
		//增加资产
		AssetAssign assetAssign = assignRepository.findById(id).get();
		assetAssign.setStatus("2");
		assignRepository.save(assetAssign);

		// 调用rpc服务增加资产
		assetService.increase();
		return assetAssign;
	}

这里主要是assetService.increase,这个rpc。

    @Override
    public int increase() {
        LOGGER.info("Asset Service Begin ... xid: " + RootContext.getXID() + "\n");
        Asset asset = assetRepository.findById(ASSET_ID).get();
        asset.setAmount(asset.getAmount().add(new BigDecimal("1")));
        assetRepository.save(asset);
        //如果上面调用方没有GlobalTransactional注解的话,那么就会出现下面的情况
        //这里是远程的方法,抛出异常,数据已经插入到数据库里面了
        throw new RuntimeException("test exception for seata, your transaction should be rollbacked,asset=" + asset);
    }

加了GlobalTransactional注解,那么调用到远程rpc,后面远程抛出RuntimeException,那么事务会回滚,实现了我们的目的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章