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,那麼事務會回滾,實現了我們的目的。

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