微服务架构下,解决数据一致性问题的实践
隨著業(yè)務(wù)的快速發(fā)展,應(yīng)用單體架構(gòu)暴露出代碼可維護(hù)性差、容錯(cuò)率低、測(cè)試難度大和敏捷交付能力差等諸多問(wèn)題,微服務(wù)應(yīng)運(yùn)而生。微服務(wù)的誕生一方面解決了上述問(wèn)題,但是另一方面卻引入新的問(wèn)題,其中主要問(wèn)題之一就是:如何保證微服務(wù)間的業(yè)務(wù)數(shù)據(jù)一致性。
本文將通過(guò)一個(gè)商品采購(gòu)的業(yè)務(wù),來(lái)看看在Dubbo的微服務(wù)架構(gòu)下,如何通過(guò)Fescar來(lái)保障業(yè)務(wù)的數(shù)據(jù)一致性。本文所述的例子中,Dubbo 和 Fescar 的注冊(cè)配置服務(wù)中心均使用 Nacos。Fescar 0.2.1+ 開(kāi)始支持 Nacos 注冊(cè)配置服務(wù)中心。
?
業(yè)務(wù)描述
用戶(hù)采購(gòu)商品的業(yè)務(wù),包含3個(gè)微服務(wù):
-
庫(kù)存服務(wù): 扣減給定商品的庫(kù)存數(shù)量。
-
訂單服務(wù): 根據(jù)采購(gòu)請(qǐng)求生成訂單。
-
賬戶(hù)服務(wù): 用戶(hù)賬戶(hù)金額扣減。
業(yè)務(wù)結(jié)構(gòu)圖如下:
庫(kù)存服務(wù)(StorageService)
public interface StorageService { /** ? ? * deduct storage count ? ? */void deduct(String commodityCode, int count); }訂單服務(wù)(OrderService)
public interface OrderService { /** ? ? * create order ? ? */Order create(String userId, String commodityCode, int orderCount); }賬戶(hù)服務(wù)(AccountService)
public interface AccountService { /** ? ? * debit balance of user's account ? ? */void debit(String userId, int money); }說(shuō)明:?以上三個(gè)微服務(wù)均是獨(dú)立部署。
?
8個(gè)步驟實(shí)現(xiàn)數(shù)據(jù)一致性
Step 1:初始化 MySQL 數(shù)據(jù)庫(kù)(需要InnoDB 存儲(chǔ)引擎)
在?resources/jdbc.properties?修改StorageService、OrderService、AccountService 對(duì)應(yīng)的連接信息。
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.DriverStep 2:創(chuàng)建 undo_log(用于Fescar AT 模式)表和相關(guān)業(yè)務(wù)表
相關(guān)建表腳本可在?resources/sql/?下獲取,在相應(yīng)數(shù)據(jù)庫(kù)中執(zhí)行?dubbo_biz.sql?中的業(yè)務(wù)建表腳本,在每個(gè)數(shù)據(jù)庫(kù)執(zhí)行?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;說(shuō)明:?需要保證每個(gè)物理庫(kù)都包含 undo_log 表,此處可使用一個(gè)物理庫(kù)來(lái)表示上述三個(gè)微服務(wù)對(duì)應(yīng)的獨(dú)立邏輯庫(kù)。
Step 3:引入 Fescar、Dubbo 和 Nacos 相關(guān) POM 依賴(lài)
?
說(shuō)明:?由于當(dāng)前 apache-dubbo 與 dubbo-registry-nacos jar存在兼容性問(wèn)題,需要排除 fescar-dubbo 中的 apache.dubbo 依賴(lài)并手動(dòng)引入 alibaba-dubbo,后續(xù) apache-dubbo(2.7.1+) 將兼容 dubbo-registry-nacos。在Fescar 中 fescar-dubbo jar 支持 apache.dubbo,fescar-dubbo-alibaba jar 支持 alibaba-dubbo。
<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>Step 4:微服務(wù) Provider Spring配置
分別在三個(gè)微服務(wù)Spring配置文件(dubbo-account-service.xml、?dubbo-order-service?和?dubbo-storage-service.xml?)進(jìn)行如下配置:
-
配置 Fescar 代理數(shù)據(jù)源
此處需要使用 com.alibaba.fescar.rm.datasource.DataSourceProxy 包裝 Druid 數(shù)據(jù)源作為直接業(yè)務(wù)數(shù)據(jù)源,DataSourceProxy 用于業(yè)務(wù) SQL 的攔截解析并與 TC 交互協(xié)調(diào)事務(wù)操作狀態(tài)。
-
配置 Dubbo 注冊(cè)中心
-
配置 Fescar GlobalTransactionScanner
此處構(gòu)造方法的第一個(gè)參數(shù)為業(yè)務(wù)自定義 applicationId,若在單機(jī)部署多微服務(wù)需要保證 applicationId 唯一。
構(gòu)造方法的第二個(gè)參數(shù)為 Fescar 事務(wù)服務(wù)邏輯分組,此分組通過(guò)配置中心配置項(xiàng) service.vgroup_mapping.my_test_tx_group 映射到相應(yīng)的 Fescar-Server 集群名稱(chēng),然后再根據(jù)集群名稱(chēng).grouplist 獲取到可用服務(wù)列表。
Step 5:事務(wù)發(fā)起方配置
在?dubbo-business.xml?配置以下配置:
-
配置 Dubbo 注冊(cè)中心
? ? ? ?同 Step 4
-
配置 Fescar GlobalTransactionScanner
? ? ? ?同 Step 4
-
在事務(wù)發(fā)起方 service 方法上添加 @GlobalTransactional 注解
timeoutMills 為事務(wù)的總體超時(shí)時(shí)間默認(rèn)60s,name 為事務(wù)方法簽名的別名,默認(rèn)為空。注解內(nèi)參數(shù)均可省略。
Step 6:啟動(dòng) Nacos-Server
-
下載 Nacos-Server 最新?release?包并解壓
-
運(yùn)行 Nacos-server
Linux/Unix/Mac
sh startup.sh -m standaloneWindows
cmd startup.cmd -m standalone訪(fǎng)問(wèn) Nacos 控制臺(tái):http://localhost:8848/nacos/index.html#/configurationManagement?dataId=&group=&appName=&namespace
若訪(fǎng)問(wèn)成功說(shuō)明 Nacos-Server 服務(wù)運(yùn)行成功(默認(rèn)賬號(hào)/密碼: nacos/nacos)
Step 7:啟動(dòng) Fescar-Server
-
下載 Fescar-Server 最新?release?包并解壓
-
初始化 Fescar 配置
進(jìn)入到 Fescar-Server 解壓目錄 conf 文件夾下,確認(rèn)?nacos-config.txt?的配置值(一般不需要修改),確認(rèn)完成后運(yùn)行?nacos-config.sh?腳本初始化配置。
sh nacos-config.sh $Nacos-Server-IPeg:
sh nacos-config.sh localhost腳本執(zhí)行最后輸出 "init nacos config finished, please start fescar-server." 說(shuō)明推送配置成功。若想進(jìn)一步確認(rèn)可登陸Nacos 控制臺(tái) 配置列表 篩選 Group=FESCAR_GROUP 的配置項(xiàng)。
-
修改 Fescar-server 服務(wù)注冊(cè)方式為 nacos
進(jìn)入到 Fescar-Server 解壓目錄 conf 文件夾下?registry.conf?修改 type="nacos" 并配置 Nacos 的相關(guān)屬性。
registry { # file nacostype = "nacos"nacos { serverAddr = "localhost"namespace = "public"cluster = "default"}file { name = "file.conf"} }type: 可配置為 nacos 和 file,配置為 file 時(shí)無(wú)服務(wù)注冊(cè)功能
nacos.serverAddr: Nacos-Sever 服務(wù)地址(不含端口號(hào))
nacos.namespace: Nacos 注冊(cè)和配置隔離 namespace
nacos.cluster: 注冊(cè)服務(wù)的集群名稱(chēng)
file.name: type = "file" classpath 下配置文件名
-
運(yùn)行 Fescar-server
Linux/Unix/Mac
sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數(shù)可選)Windows
cmd fescar-server.bat $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA $IP(此參數(shù)可選)
$LISTEN_PORT: Fescar-Server 服務(wù)端口
$PATH_FOR_PERSISTENT_DATA: 事務(wù)操作記錄文件存儲(chǔ)路徑(已存在路徑)
$IP(可選參數(shù)): 用于多 IP 環(huán)境下指定 Fescar-Server 注冊(cè)服務(wù)的IP
eg: sh fescar-server.sh 8091 /home/admin/fescar/data/
運(yùn)行成功后可在 Nacos 控制臺(tái)看到 服務(wù)名 =serverAddr 服務(wù)注冊(cè)列表:
Step 8:啟動(dòng)微服務(wù)并測(cè)試
-
修改業(yè)務(wù)客戶(hù)端發(fā)現(xiàn)注冊(cè)方式為 nacos
? ? ? ?同Step 7 中[修改 Fescar-server 服務(wù)注冊(cè)方式為 nacos] 步驟
-
啟動(dòng)?DubboAccountServiceStarter
-
啟動(dòng)?DubboOrderServiceStarter
-
啟動(dòng)?DubboStorageServiceStarter
啟動(dòng)完成可在 Nacos 控制臺(tái)服務(wù)列表 看到啟動(dòng)完成的三個(gè) provider:
-
啟動(dòng)?DubboBusinessTester?進(jìn)行測(cè)試
注意:?在標(biāo)注 @GlobalTransactional 注解方法內(nèi)部顯示的拋出異常才會(huì)進(jìn)行事務(wù)的回滾。整個(gè) Dubbo 服務(wù)調(diào)用鏈路只需要在事務(wù)最開(kāi)始發(fā)起方的 service 方法標(biāo)注注解即可。
通過(guò)以上8個(gè)步驟,我們實(shí)現(xiàn)了用戶(hù)采購(gòu)商品的業(yè)務(wù)中庫(kù)存、訂單和賬戶(hù)3個(gè)獨(dú)立微服務(wù)之間的數(shù)據(jù)一致性。
?
參考鏈接:
本文 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
?
本文作者:清銘,社區(qū)昵稱(chēng) slievrly,Fescar 開(kāi)源項(xiàng)目發(fā)起人之一,阿里巴巴中件間 TXC/GTS 核心研發(fā)成員,長(zhǎng)期從事于分布式中間件核心研發(fā)工作,在分布式事務(wù)領(lǐng)域有著較豐富的技術(shù)積累。
總結(jié)
以上是生活随笔為你收集整理的微服务架构下,解决数据一致性问题的实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数据库不推荐使用外键的9个理由!
- 下一篇: 为什么美国互联网没有“运营”岗?