隨着業務的快速發展,應用單體架構暴露出代碼可維護性差、容錯率低、測試難度大和敏捷交付能力差等諸多問題,微服務應運而生。微服務的誕生一方面解決了上述問題,可是另外一方面卻引入新的問題,其中主要問題之一就是:如何保證微服務間的業務數據一致性。html
本文將經過一個商品採購的業務,來看看在Dubbo的微服務架構下,如何經過Fescar來保障業務的數據一致性。本文所述的例子中,Dubbo 和 Fescar 的註冊配置服務中心均使用 Nacos。Fescar 0.2.1+ 開始支持 Nacos 註冊配置服務中心。mysql
用戶採購商品的業務,包含3個微服務:git
業務結構圖以下:github
庫存服務(StorageService)spring
public interface StorageService { /** * deduct storage count */ void deduct(String commodityCode, int count); }
訂單服務(OrderService)sql
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); }
說明: 以上三個微服務均是獨立部署。apache
Step 1:初始化 MySQL 數據庫(須要InnoDB 存儲引擎)架構
在 resources/jdbc.properties 修改StorageService、OrderService、AccountService 對應的鏈接信息。app
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 配置如下配置:
在事務發起方 service 方法上添加 @GlobalTransactional 註解
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
timeoutMills 爲事務的整體超時時間默認60s,name 爲事務方法簽名的別名,默認爲空。註解內參數都可省略。
Step 6:啓動 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 解壓目錄 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 解壓目錄 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 下配置文件名
Linux/Unix/Mac
sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數可選)
Windows
cmd fescar-server.bat $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數可選)
服務端口 PATH_FOR_PERSISTENT_DATA: 事務操做記錄文件存儲路徑(已存在路徑)
$IP(可選參數): 用於多 IP 環境下指定 Fescar-Server 註冊服務的IP
eg: sh fescar-server.sh 8091 /home/admin/fescar/data/
運行成功後可在 Nacos 控制檯看到 服務名 =serverAddr 服務註冊列表:
Step 8:啓動微服務並測試
啓動完成可在 Nacos 控制檯服務列表 看到啓動完成的三個 provider:
注意: 在標註 @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 的更多信息:
本文爲雲棲社區原創內容,未經容許不得轉載。