如何使用Seata保證Dubbo微服務(wù)間的一致性

2022-04-12 10:44 更新

本文主要介紹如何使用Seata保證Dubbo微服務(wù)間的一致性

案例

用戶采購商品業(yè)務(wù),整個業(yè)務(wù)包含3個微服務(wù):

  • 庫存服務(wù): 扣減給定商品的庫存數(shù)量。
  • 訂單服務(wù): 根據(jù)采購請求生成訂單。
  • 賬戶服務(wù): 用戶賬戶金額扣減。

業(yè)務(wù)結(jié)構(gòu)圖

Architecture

StorageService

public interface StorageService {

    /**
     * 扣除存儲數(shù)量
     */
    void deduct(String commodityCode, int count);
}

OrderService

public interface OrderService {

    /**
     * 創(chuàng)建訂單
     */
    Order create(String userId, String commodityCode, int orderCount);
}

AccountService

public interface AccountService {

    /**
     * 從用戶賬戶中借出
     */
    void debit(String userId, int money);
}

主要的業(yè)務(wù)邏輯:

public class BusinessServiceImpl implements BusinessService {

    private StorageService storageService;

    private OrderService orderService;

    /**
     * 采購
     */
    public void purchase(String userId, String commodityCode, int orderCount) {

        storageService.deduct(commodityCode, orderCount);

        orderService.create(userId, commodityCode, orderCount);
    }
}
public class StorageServiceImpl implements StorageService {

  private StorageDAO storageDAO;
  
    @Override
    public void deduct(String commodityCode, int count) {
        Storage storage = new Storage();
        storage.setCount(count);
        storage.setCommodityCode(commodityCode);
        storageDAO.update(storage);
    }
}
public class OrderServiceImpl implements OrderService {

    private OrderDAO orderDAO;

    private AccountService accountService;

    public Order create(String userId, String commodityCode, int orderCount) {

        int orderMoney = calculate(commodityCode, orderCount);

        accountService.debit(userId, orderMoney);

        Order order = new Order();
        order.userId = userId;
        order.commodityCode = commodityCode;
        order.count = orderCount;
        order.money = orderMoney;

        return orderDAO.insert(order);
    }
}

Seata 分布式事務(wù)解決方案

undefined

此處僅僅需要一行注解 @GlobalTransactional 寫在業(yè)務(wù)發(fā)起方的方法上:


    @GlobalTransactional
    public void purchase(String userId, String commodityCode, int orderCount) {
        ......
    }

Dubbo 與 Seata 結(jié)合的例子

Step 1: 安裝數(shù)據(jù)庫

  • 要求: MySQL (InnoDB 存儲引擎)。

提示: 事實上例子中3個微服務(wù)需要3個獨立的數(shù)據(jù)庫,但為了方便我們使用同一物理庫并配置3個邏輯連接串。

更改以下xml文件中的數(shù)據(jù)庫url、username和password

dubbo-account-service.xml dubbo-order-service.xml dubbo-storage-service.xml

    <property name="url" value="jdbc:mysql://x.x.x.x:3306/xxx" />
    <property name="username" value="xxx" />
    <property name="password" value="xxx" />

Step 2: 為 Seata 創(chuàng)建 undo_log 表

UNDO_LOG 此表用于 Seata 的AT模式。

-- 注意當 Seata 版本升級至 0.3.0+ 將由之前的普通索引變更為唯一索引。
CREATE TABLE `undo_log` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(100) NOT NULL,
  `context` varchar(128) 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`),
  UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Step 3: 創(chuàng)建相關(guān)業(yè)務(wù)表


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;


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;


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;

Step 4: 啟動 Seata-Server 服務(wù)

Usage: sh seata-server.sh(for linux and mac) or cmd seata-server.bat(for windows) [options]
  Options:
    --host, -h
      The host to bind.
      Default: 0.0.0.0
    --port, -p
      The port to listen.
      Default: 8091
    --storeMode, -m
      log store mode : file、db
      Default: file
    --help

e.g.

sh seata-server.sh -p 8091 -h 127.0.0.1 -m file

Step 5: 運行例子

相關(guān)項目


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號