阿里開源Fescar分佈式事務解決 demo運行初探

項目地址

Github :https://github.com/alibaba/fescar
中文文檔:https://github.com/alibaba/fescar/wiki/Home_Chinese
demo:https://github.com/fescar-group/fescar-samples
dubbo官網使用fecar的案例

從Github上下載的fescar目測是沒有example的,所以需要從demo地址裏下載demo。關於官網的案例就不贅述了,直接開始運行步驟。

一、導入項目

1.導入案例demo

個人使用的是STS,解壓從第三個地址下載的fescar-samples壓縮包。然後用STS導入。
導入項目
沒必要全部導入,我測試的是dubbo例子。springboot的例子還沒測,有興趣的自己動手。

2.導入server

從第一個地址下載的zip解壓後,導入該目錄下的所有工程。主要是server工程裏有依賴的版本號需要從父工程獲取,如果只導入server會提示Maven找不到依賴的。所以乾脆全導入即可。
在這裏插入圖片描述

二、創建數據庫

官網給的例子是在一個庫裏創建三張表,然後配置相同的數據源,追求簡單。我選擇創建三個庫,分別將賬戶、訂單、庫存的表放到各自對應的數據庫中,也是爲了更貼近實際生產的情況模擬。
在這裏插入圖片描述

1.每個庫先分別創建表undo_log

CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `rollback_info` longblob NOT NULL,
  `log_status` int(11) NOT NULL,
  `log_created` datetime NOT NULL,
  `log_modified` datetime NOT NULL,
  `ext` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_unionkey` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8

UNDO_LOG 此表用於 Fescar 的AT模式。

2.創建庫存表storage_tbl

在fescar_storage庫創建表storage_tbl

DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3.創建訂單表order_tbl

在fescar_order庫創建表order_tbl

DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `commodity_code` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT 0,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

4.創建賬戶表account_tbl

在fescar_account庫創建表account_tbl

DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` varchar(255) DEFAULT NULL,
  `money` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

至此,數據庫的準備完成
在這裏插入圖片描述

三、修改配置文件

在這裏插入圖片描述
如圖,修改數據庫配置文件。三個數據源對應的數據庫url以及賬號密碼。

四、啓動服務

1.啓動server

在這裏插入圖片描述

2.啓動業務服務

在這裏插入圖片描述
前三個服務的啓動順序隨意,啓動後最終啓動DubboBusinessTester接口運行。運行後程序會給賬戶表和庫存表各自初始化一條數據。
在這裏插入圖片描述
在這裏插入圖片描述
此時運行DubboBusinessTester的main方法後控制檯會報錯
在這裏插入圖片描述
原因是demo裏的purchase方法中官方拋出一個異常。

	@Override
	@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
	public void purchase(String userId, String commodityCode, int orderCount) {
		LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
		storageService.deduct(commodityCode, orderCount);
		orderService.create(userId, commodityCode, orderCount);
        throw new RuntimeException("xxx");
	}

註釋掉後再運行就可以看到事務全部提交,三個庫的業務表都更新或插入了相關記錄。至此,官方demo運行完畢。

五、debug看事務提交及回滾情況

在這裏插入圖片描述
當代碼執行這一步之前,查看數據庫。
庫存表的count減少了2變爲98
庫存表的count減少了2變爲98
賬戶表金額money減少了400變爲599
在這裏插入圖片描述
並且訂單表生成了一條記錄
在這裏插入圖片描述
由此證明,本地事務已經提交了。但是全局事務還是可以回滾的,繼續執行,會拋出事先寫好的異常。全局事務回滾,再看數據庫時,三張表的數據全部回滾爲初始狀態。

public class TransactionalTemplate {

    /**
     * Execute object.
     *
     * @param business the business
     * @return the object
     * @throws ExecutionException the execution exception
     */
    public Object execute(TransactionalExecutor business) throws TransactionalExecutor.ExecutionException {

        // 1. get or create a transaction
        GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();

        // 2. begin transaction開啓事務
        try {
            tx.begin(business.timeout(), business.name());

        } catch (TransactionException txe) {
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.BeginFailure);

        }

        Object rs = null;
        try {

            // Do Your Business業務處理
            rs = business.execute();

        } catch (Throwable ex) {

            // 3. any business exception, rollback.
            try {
               //回滾
                tx.rollback();

                // 3.1 Successfully rolled back
                throw new TransactionalExecutor.ExecutionException(tx, TransactionalExecutor.Code.RollbackDone, ex);

            } catch (TransactionException txe) {
                // 3.2 Failed to rollback
                throw new TransactionalExecutor.ExecutionException(tx, txe,
                    TransactionalExecutor.Code.RollbackFailure, ex);

            } finally {
                GlobalTransactionContext.clean();
            }

        }

        // 4. everything is fine, commit.
        try {
            tx.commit();

        } catch (TransactionException txe) {
            // 4.1 Failed to commit
            throw new TransactionalExecutor.ExecutionException(tx, txe,
                TransactionalExecutor.Code.CommitFailure);

        } finally {
            GlobalTransactionContext.clean();
        }
        return rs;
    }

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