天天看點

微服務架構下,解決資料一緻性問題的實踐

随着業務的快速發展,應用單體架構暴露出代碼可維護性差、容錯率低、測試難度大和靈活傳遞能力差等諸多問題,微服務應運而生。微服務的誕生一方面解決了上述問題,但是另一方面卻引入新的問題,其中主要問題之一就是:如何保證微服務間的業務資料一緻性。

本文将通過一個商品采購的業務,來看看在Dubbo的微服務架構下,如何通過Fescar來保障業務的資料一緻性。本文所述的例子中,Dubbo 和 Fescar 的注冊配置服務中心均使用 Nacos。Fescar 0.2.1+ 開始支援 Nacos 注冊配置服務中心。

業務描述

使用者采購商品的業務,包含3個微服務:

  • 庫存服務: 扣減給定商品的庫存數量。
  • 訂單服務: 根據采購請求生成訂單。
  • 賬戶服務: 使用者賬戶金額扣減。

業務結構圖如下:

微服務架構下,解決資料一緻性問題的實踐

庫存服務(StorageService)

public interface StorageService {    
      /**     
       * deduct storage count     
       */
      void deduct(String commodityCode, int count);
}
           

訂單服務(OrderService)

public interface OrderService {    
      /**     
       * create order     
       */
     Order create(String userId, String commodityCode, int orderCount);
}
           

賬戶服務(AccountService)

public interface AccountService {    
      /**     
       * debit balance of user's account     
       */
     void debit(String userId, int money);
}           

說明: 以上三個微服務均是獨立部署。

8個步驟實作資料一緻性

Step 1:初始化 MySQL 資料庫(需要InnoDB 存儲引擎)

在 resources/jdbc.properties 修改StorageService、OrderService、AccountService 對應的連接配接資訊。

jdbc.account.url=jdbc:mysql://xxxx/xxxx
jdbc.account.username=xxxx
jdbc.account.password=xxxx
jdbc.account.driver=com.mysql.jdbc.Driver
# storage db config
jdbc.storage.url=jdbc:mysql://xxxx/xxxx
jdbc.storage.username=xxxx
jdbc.storage.password=xxxx
jdbc.storage.driver=com.mysql.jdbc.Driver
# order db config
jdbc.order.url=jdbc:mysql://xxxx/xxxx
jdbc.order.username=xxxx
jdbc.order.password=xxxx
jdbc.order.driver=com.mysql.jdbc.Driver
           

Step 2:建立 undo_log(用于Fescar AT 模式)表和相關業務表

相關建表腳本可在 resources/sql/ 下擷取,在相應資料庫中執行 dubbo_biz.sql 中的業務建表腳本,在每個資料庫執行 undo_log.sql 建表腳本。

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=1 DEFAULT CHARSET=utf8;

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;
           

說明: 需要保證每個實體庫都包含 undo_log 表,此處可使用一個實體庫來表示上述三個微服務對應的獨立邏輯庫。

Step 3:引入 Fescar、Dubbo 和 Nacos 相關 POM 依賴

<properties>
      <fescar.version>0.2.1</fescar.version>
      <dubbo.alibaba.version>2.6.5</dubbo.alibaba.version>
      <dubbo.registry.nacos.version>0.0.2</dubbo.registry.nacos.version>
   </properties>
    
   <dependency>
       <groupId>com.alibaba.fescar</groupId>
       <artifactId>fescar-spring</artifactId>
       <version>${fescar.version}</version>
   </dependency>
   <dependency>
       <groupId>com.alibaba.fescar</groupId>
       <artifactId>fescar-dubbo-alibaba</artifactId>
       <version>${fescar.version}</version>
       <exclusions>
           <exclusion>
               <artifactId>dubbo</artifactId>
               <groupId>org.apache.dubbo</groupId>
           </exclusion>
       </exclusions>
   </dependency>
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>dubbo</artifactId>
       <version>${dubbo.alibaba.version}</version>
   </dependency>
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>dubbo-registry-nacos</artifactId>
       <version>${dubbo.registry.nacos.version}</version>
   </dependency>           

說明: 由于目前 apache-dubbo 與 dubbo-registry-nacos jar存在相容性問題,需要排除 fescar-dubbo 中的 apache.dubbo 依賴并手動引入 alibaba-dubbo,後續 apache-dubbo(2.7.1+) 将相容 dubbo-registry-nacos。在Fescar 中 fescar-dubbo jar 支援 apache.dubbo,fescar-dubbo-alibaba jar 支援 alibaba-dubbo。

Step 4:微服務 Provider Spring配置

分别在三個微服務Spring配置檔案(dubbo-account-service.xml、 dubbo-order-service 和 dubbo-storage-service.xml )進行如下配置:

  • 配置 Fescar 代理資料源
    <bean id="accountDataSourceProxy" class="com.alibaba.fescar.rm.datasource.DataSourceProxy">
        <constructor-arg ref="accountDataSource"/>
    </bean>
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="accountDataSourceProxy"/>
    </bean>           
    此處需要使用 com.alibaba.fescar.rm.datasource.DataSourceProxy 包裝 Druid 資料源作為直接業務資料源,DataSourceProxy 用于業務 SQL 的攔截解析并與 TC 互動協調事務操作狀态。
  • 配置 Dubbo 注冊中心
    <dubbo:registry address="nacos://${nacos-server-ip}:8848"/>
               
  • 配置 Fescar GlobalTransactionScanner
    <bean class="com.alibaba.fescar.spring.annotation.GlobalTransactionScanner">
        <constructor-arg value="dubbo-demo-account-service"/>
        <constructor-arg value="my_test_tx_group"/>
    </bean>           
    此處構造方法的第一個參數為業務自定義 applicationId,若在單機部署多微服務需要保證 applicationId 唯一。

構造方法的第二個參數為 Fescar 事務服務邏輯分組,此分組通過配置中心配置項 service.vgroup_mapping.my_test_tx_group 映射到相應的 Fescar-Server 叢集名稱,然後再根據叢集名稱.grouplist 擷取到可用服務清單。

Step 5:事務發起方配置

在 dubbo-business.xml 配置以下配置:

  • 同 Step 4
  • 在事務發起方 service 方法上添加 @GlobalTransactional 注解
    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")           
    timeoutMills 為事務的總體逾時時間預設60s,name 為事務方法簽名的别名,預設為空。注解内參數均可省略。

Step 6:啟動 Nacos-Server

  • 下載下傳 Nacos-Server 最新 release 包并解壓
  • 運作 Nacos-server

Linux/Unix/Mac

sh startup.sh -m standalone
           

Windows

cmd startup.cmd -m standalone
           

通路 Nacos 控制台:

http://localhost:8848/nacos/index.html#/configurationManagement?dataId=&group=&appName=&namespace

若通路成功說明 Nacos-Server 服務運作成功(預設賬号/密碼: nacos/nacos)

Step 7:啟動 Fescar-Server

  • 下載下傳 Fescar-Server 最新 release 包并解壓
  • 初始化 Fescar 配置

進入到 Fescar-Server 解壓目錄 conf 檔案夾下,确認 nacos-config.txt 的配置值(一般不需要修改),确認完成後運作 nacos-config.sh 腳本初始化配置。

sh nacos-config.sh $Nacos-Server-IP
           

eg:

sh nacos-config.sh localhost 
           

腳本執行最後輸出 "init nacos config finished, please start fescar-server." 說明推送配置成功。若想進一步确認可登陸Nacos 控制台 配置清單 篩選 Group=FESCAR_GROUP 的配置項。

微服務架構下,解決資料一緻性問題的實踐
  • 修改 Fescar-server 服務注冊方式為 nacos

進入到 Fescar-Server 解壓目錄 conf 檔案夾下 registry.conf 修改 type="nacos" 并配置 Nacos 的相關屬性。

registry {  
   # file nacos
   type = "nacos"

  nacos {    
    serverAddr = "localhost"
    namespace = "public"
    cluster = "default"
  }
  file {    
    name = "file.conf"
  }
}           

type: 可配置為 nacos 和 file,配置為 file 時無服務注冊功能

nacos.serverAddr: Nacos-Sever 服務位址(不含端口号)

nacos.namespace: Nacos 注冊和配置隔離 namespace

nacos.cluster: 注冊服務的叢集名稱

file.name: type = "file" classpath 下配置檔案名

  • 運作 Fescar-server
sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數可選)
           
cmd fescar-server.bat $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數可選)
           

$LISTEN_PORT:Fescar-Server 服務端口

$PATH_FOR_PERSISTENT_DATA: 事務操作記錄檔案存儲路徑(已存在路徑)

$IP(可選參數): 用于多 IP 環境下指定 Fescar-Server 注冊服務的IP

eg: sh fescar-server.sh 8091 /home/admin/fescar/data/

運作成功後可在 Nacos 控制台看到 服務名 =serverAddr 服務注冊清單:

微服務架構下,解決資料一緻性問題的實踐

Step 8:啟動微服務并測試

  • 修改業務用戶端發現注冊方式為 nacos

    同Step 7 中[修改 Fescar-server 服務注冊方式為 nacos] 步驟

  • 啟動 DubboAccountServiceStarter
  • 啟動 DubboOrderServiceStarter
  • 啟動 DubboStorageServiceStarter

啟動完成可在 Nacos 控制台服務清單 看到啟動完成的三個 provider:

微服務架構下,解決資料一緻性問題的實踐
  • 啟動 DubboBusinessTester 進行測試

注意: 在标注 @GlobalTransactional 注解方法内部顯示的抛出異常才會進行事務的復原。整個 Dubbo 服務調用鍊路隻需要在事務最開始發起方的 service 方法标注注解即可。

通過以上8個步驟,我們實作了使用者采購商品的業務中庫存、訂單和賬戶3個獨立微服務之間的資料一緻性。

參考連結:

本文 sample 位址:

https://github.com/fescar-group/fescar-samples/tree/master/nacos

Fescar:

https://github.com/alibaba/fescar

Dubbo:

https://github.com/apache/incubator-dubbo

Nacos:

https://github.com/alibaba/nacos
本文作者:清銘,社群昵稱 slievrly,Fescar 開源項目發起人之一,阿裡巴巴中件間 TXC/GTS 核心研發成員,長期從事于分布式中間件核心研發工作,在分布式事務領域有着較豐富的技術積累。

有關 Fescar 的更多資訊:

繼續閱讀