創(chuàng)新互聯(lián)公司專注于昌黎網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供昌黎營銷型網(wǎng)站建設(shè),昌黎網(wǎng)站制作、昌黎網(wǎng)頁設(shè)計、昌黎網(wǎng)站官網(wǎng)定制、成都微信小程序服務(wù),打造昌黎網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供昌黎網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
轉(zhuǎn)載本文需注明出處:微信公眾號EAWorld,違者必究。
引言:
目錄:
1.分布式事務(wù)講解
2.分布式事務(wù)解決方案-servicecomb-pack
3.分布式事務(wù)實戰(zhàn)講解
1. 分布式事務(wù)講解
1.1事務(wù)原理
在講分布式事務(wù)之前,先聊一下事務(wù)。簡單講事務(wù)是數(shù)據(jù)庫管理系統(tǒng)執(zhí)行過程中的一個邏輯單元,它能保證要么一組數(shù)據(jù)庫操作全部執(zhí)行成功,要么全部失敗,而做到這些的原理就是事務(wù)的ACID四大特性。
A. Atomic原子性的簡稱,事務(wù)作為一個整體來執(zhí)行,要么全部成功,要么全部失敗。
C. Consistency一致性的簡稱,事務(wù)應(yīng)確保數(shù)據(jù)從一個一致的狀態(tài)轉(zhuǎn)變?yōu)榱硪粋€一致的狀態(tài)。
I. Isolation隔離性的簡稱,多個事務(wù)并發(fā)執(zhí)行時,一個事務(wù)的執(zhí)行不影響其他事務(wù)的執(zhí)行。
D.Durability持久性的檢查,已提交的事務(wù)修改數(shù)據(jù)會被持久保存。
1.2傳統(tǒng)單機(jī)數(shù)據(jù)庫事務(wù)
在傳統(tǒng)單體應(yīng)用架構(gòu)中,我們的業(yè)務(wù)數(shù)據(jù)通常都是存儲在一個數(shù)據(jù)庫中的,應(yīng)用中的各個模塊對數(shù)據(jù)庫直接進(jìn)行操作。在這種場景中,事務(wù)是由數(shù)據(jù)庫提供的基于ACID特性來保證的。
例如,在一個用戶購物下單的場景中,涉及到用戶、訂單、支付、庫存等模塊的一系列協(xié)同操作,如果其中一個模塊出現(xiàn)問題,我們就可以通過數(shù)據(jù)庫提供的事務(wù)特性來保證本次下單操作要么都成功,要么都失敗。因為這些模塊用的是同一個數(shù)據(jù)庫,所處的是同一個事務(wù)管理器,不需要做額外的其他操作就能保證事務(wù)的特性。
從廣義上來講,分布式事務(wù)其實也是事務(wù),只是區(qū)別于單機(jī)事務(wù)不同之處是:由于業(yè)務(wù)上的定義和系統(tǒng)微服務(wù)架構(gòu)的設(shè)計,很多大型的業(yè)務(wù)流程都被拆分成了多個單一的基礎(chǔ)服務(wù),而為了保證每個微服務(wù)都能獨立進(jìn)行開發(fā)和部署運行,通常都會采用一個微服務(wù)一個數(shù)據(jù)庫的架構(gòu)配套,然后將內(nèi)部服務(wù)進(jìn)行封裝,以Rest api方式對外暴露。這樣以往基于數(shù)據(jù)庫來實現(xiàn)的數(shù)據(jù)操作,就變成了多個對外提供微服務(wù)的微服務(wù)系統(tǒng)之間的協(xié)同操作。在這種情況下,原有的單機(jī)事務(wù)方式已經(jīng)不能夠使用了,因為多個服務(wù)就意味著存在多個事務(wù)管理器和多個資源,單個微服務(wù)的本地事務(wù)管理器只能保證本地事務(wù)的ACID,為了在多個服務(wù)之間能保證業(yè)務(wù)的事務(wù)性,參與分布式事務(wù)的微服務(wù)通常會依托協(xié)調(diào)器來完成相關(guān)的一致性協(xié)調(diào)操作。
那我們在微服務(wù)系統(tǒng)實際開發(fā)中,如何去實現(xiàn)協(xié)調(diào)器以處理分布式事務(wù)呢,這里的解決方案是采用華為提供的servicecomb-pack框架來解決這一問題。
2. 分布式事務(wù)解決方案:
servicecomb-pack
2.1補(bǔ)償方式
在講servicecomb-pack之前先了解兩個概念:不完美補(bǔ)償(saga)和完美補(bǔ)償(tcc)。
saga:不完美補(bǔ)償,一般在系統(tǒng)中我們會專門為業(yè)務(wù)邏輯對應(yīng)寫一個補(bǔ)償邏輯,如果業(yè)務(wù)邏輯執(zhí)行失敗,就會去執(zhí)行這個補(bǔ)償邏輯,我們稱這個補(bǔ)償邏輯為反向操作,這個反向操作同樣會留下操作痕跡,例如:在銀行系統(tǒng)中,客戶去ATM取錢,銀行會先對用戶賬戶進(jìn)行扣款操作,如果本次取錢不成功,銀行系統(tǒng)會發(fā)出一筆沖正操作,將之前扣除的款項打回用戶賬戶,這個沖正操作在交易記錄里面是開源查詢到的。
tcc:完美補(bǔ)償,cancel階段會徹底清楚之前的業(yè)務(wù)邏輯操作,用戶是感知不到的。例如:在一個交易平臺去發(fā)起交易,首先在try階段不會直接去扣除賬戶余額,而且去檢查用戶的額度并刷新額度,然后在confirm階段才去真正操作賬戶。如果出現(xiàn)異常,那么在cancel階段就需要去執(zhí)行業(yè)務(wù)邏輯來取消try階段產(chǎn)生的后果,釋放在try階段被占用的額度。整個過程只有等confirm執(zhí)行完畢,交易才算完成。
2.2servicecomb-pack
servicecomb-pack出自于華為微服務(wù)框架servicecomb,是一個開源的分布式事務(wù)最終一致性解決方案,該項目已交由Apache軟件基金會孵化,目前已經(jīng)在apache畢業(yè)了。0.3.0版本之前叫servicecomb-saga,現(xiàn)版本已經(jīng)改名為servicecomb-pack。
servicecomb-pack架構(gòu)主要包含兩個組件:alpha和Omega
alpha:alpha其實就是一個server端,需要用戶自行編譯運行,它的作用就是上述中的分布式事務(wù)協(xié)調(diào)器,主要作用是和Omega客戶端進(jìn)行通訊,接收omega發(fā)過來的事務(wù)事件,然后進(jìn)行持久化存儲事務(wù)以及修改協(xié)調(diào)子事務(wù)的狀態(tài),從而保證全局事務(wù)中的所有子事務(wù)狀態(tài)都一致,即要么全執(zhí)行完成,要么全執(zhí)行失敗。
omega:Omega端其實可以看成是一個微服務(wù)中內(nèi)嵌的agent,主要作用是監(jiān)控本地子事務(wù)的執(zhí)行情況并向alpha-server端發(fā)送子事務(wù)執(zhí)行事件以及傳遞全局事務(wù)ID,并在異常情況下會根據(jù)alpha下發(fā)的操作事件進(jìn)行相應(yīng)的補(bǔ)償操作。
Omega會以切面編程的方式向應(yīng)用程序注入相關(guān)的處理模塊,幫助我們構(gòu)建分布式事務(wù)調(diào)用的上下文。Omega在事務(wù)處理初始階段處理事務(wù)的相關(guān)準(zhǔn)備的操作,在事務(wù)執(zhí)行完畢做一些清理的操作,例如創(chuàng)建分布式事務(wù)起始事件,以及相關(guān)的子事件,根據(jù)事務(wù)的執(zhí)行的成功或者失敗生產(chǎn)相關(guān)的事務(wù)終止或者失敗事件。這樣帶來的好處是用戶的代碼只需要添加幾個annotation 來描述分布式事務(wù)執(zhí)行范圍,以及與本地的事務(wù)處理恢復(fù)的相關(guān)函數(shù)信息,Omega就能通過切面注入的代碼能夠追蹤與本地事務(wù)的執(zhí)行情況。Omega會將本地事務(wù)執(zhí)行的情況以事件的方式通知給Alpha。由于單個Omega不可能知曉一個分布式事務(wù)下其他參與服務(wù)的執(zhí)行情況, 這樣就需要Alpha扮演一個十分重要的協(xié)調(diào)者的角色。Alpha將收集到的分布式事務(wù)事件信息整理匯總,通過分析這些事件之間的關(guān)系可以了解到分布式事務(wù)的執(zhí)行情況, Alpha通過向Omega下發(fā)相關(guān)的執(zhí)行指令由Omega執(zhí)行相關(guān)提交或恢復(fù)操作,實現(xiàn)分布式事務(wù)的最終一致性。
在了解的Pack實現(xiàn)的部分細(xì)節(jié)之后, 我們可以從下圖進(jìn)一步了解ServiceComb Pack架構(gòu)下,Alpha與Omega內(nèi)部各模塊之間的關(guān)系圖[1]。
整個架構(gòu)分為三個部分,一個是Alpha協(xié)調(diào)器,另外一個就是注入到微服務(wù)實例中的Omega,以及Alpha與Omega之間的交互協(xié)議, 目前ServiceComb Pack支持Saga 以及TCC兩種分布式事務(wù)協(xié)調(diào)協(xié)議實現(xiàn)。
Omega包含了與分析用戶分布式事務(wù)邏輯相關(guān)的事務(wù)注解模塊(Transaction Annotation)以及事務(wù)攔截器(Transaction Interceptor);分布式事務(wù)執(zhí)行相關(guān)的事務(wù)上下文(Transaction Context),事務(wù)回調(diào)(Transaction Callback) ,事務(wù)執(zhí)行器(Transaction Executor);以及負(fù)責(zé)與Alpha進(jìn)行通訊的事務(wù)傳輸(Transaction Transport)模塊。
事務(wù)注解模塊是分布式事務(wù)的用戶界面,用戶將這些標(biāo)注添加到自己的業(yè)務(wù)代碼之上用以描述與分布式事務(wù)相關(guān)的信息,這樣Omega就可以按照分布式事務(wù)的協(xié)調(diào)要求進(jìn)行相關(guān)的處理。如果大家擴(kuò)展自己的分布式事務(wù),也可以通過定義自己的事務(wù)標(biāo)注來實現(xiàn)。
事務(wù)攔截器這個模塊我們可以借助AOP手段,在用戶標(biāo)注的代碼基礎(chǔ)上添加相關(guān)的攔截代碼,獲取到與分布式事務(wù)以及本地事務(wù)執(zhí)行相關(guān)的信息,并借助事務(wù)傳輸模塊與Alpha進(jìn)行通訊傳遞事件。
事務(wù)上下文為Omega內(nèi)部提供了一個傳遞事務(wù)調(diào)用信息的一個手段,借助前面提到的全局事務(wù)ID以及本地事務(wù)ID的對應(yīng)關(guān)系,Alpha可以很容易檢索到與一個分布式事務(wù)相關(guān)的所有本地事務(wù)事件信息。
事務(wù)執(zhí)行器主要是為了處理事務(wù)調(diào)用超時設(shè)計的模塊。由于Alpha與Omega之間的連接有可能不可靠,Alpha端很難判斷Omega本地事務(wù)執(zhí)行超時是由Alpha與Omega直接的網(wǎng)絡(luò)引起的還是Omega自身調(diào)用的問題,因此設(shè)計了事務(wù)執(zhí)行器來監(jiān)控Omega的本地的執(zhí)行情況,簡化Omega的超時操作。目前Omega的缺省實現(xiàn)是直接調(diào)用事務(wù)方法,由Alpha的后臺服務(wù)通過掃描事件表的方式來確定事務(wù)執(zhí)行時間是否超時。
事務(wù)回調(diào)在Omega與Alpha建立連接的時候就會向Alpha進(jìn)行注冊,當(dāng)Alpha需要進(jìn)行相關(guān)的協(xié)調(diào)操作的時候,會直接調(diào)用Omega注冊的回調(diào)方法進(jìn)行通信。由于微服務(wù)實例在云化場景啟停會很頻繁,我們不能假設(shè)Alpha一直能找到原有注冊上的事務(wù)回調(diào), 因此我們建議微服務(wù)實例是無狀態(tài)的,這樣Alpha只需要根據(jù)服務(wù)名就能找到對應(yīng)的Omega進(jìn)行通信。
事務(wù)傳輸模塊負(fù)責(zé)Omega與Alpha之間的通訊,在具體的實現(xiàn)過程中,Pack通過定義相關(guān)的Grpc描述接口文件定義了TCC 以及Saga的事務(wù)交互方法, 同時也定義了與交互相關(guān)的事件[2]。
3. 分布式事務(wù)實戰(zhàn)
3.1 alpha-server配置
3.1.1編譯alpha-server
1. 環(huán)境準(zhǔn)備
JDK1.8
Maven3.x
2. 源碼獲取
Github地址:https://github.com/apache/servicecomb-pack
$ git clone:https://github.com/apache/servicecomb-pack.git
$ git checkout 0.4.0
3. 修改配置文件
找到alpha-server/src/main/resource/application.yaml,修改datasource信息為本地信息即可
4. 本地構(gòu)建alpha-server
$ cd servicecomb-pack
$ mvn clean install -DskipTests -Pspring-boot-2
在執(zhí)行完命令后,可在alpha/alpha-server/target/saga/alpha-server-${version}-exec.jar中找到alpha-server的可執(zhí)行jar包
5. 初始化數(shù)據(jù)庫
可在alpha\alpha-server\src\main\resources目錄下找到schema-MySQL.sql和schema-postgresql.sql兩個sql文件,可自行根據(jù)所選數(shù)據(jù)庫進(jìn)行初始化即可。
6. 啟動alpha-server
java -Dspring.profiles.active=prd -D"spring.datasource.url=jdbc:postgresql://${host_address}:5432/saga?useSSL=false" -jar alpha-server-${saga_version}-exec.jar
*注意:請在執(zhí)行命令前將${saga_version}和${host_address}更改為實際值
至此,alpha-server全局事務(wù)管理器已經(jīng)啟動成功。
3.1.2替換postgresql為mysql
目前alpha-server支持pg和mysql兩種數(shù)據(jù)庫,默認(rèn)為pg,如需改為mysql,需要進(jìn)行如下操作:
1. 安裝并運行mysql
2. 修改pom文件,添加依賴
alpha-server/pom.xml,添加mysql依賴
ependency>mysql mysql-connector-java runtime 8.0.15
3. 修改配置文件
找到alpha-server/src/main/resource/application.yaml,修改datasource信息為本地信息即可
spring: profiles: mysql datasource: username: ${username} password: ${password} url: jdbc:mysql://${host_address}:${port}/${database_name}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false platform: mysql continue-on-error: false driver-class-name: com.mysql.cj.jdbc.Driver
(左右滑動查看全部代碼)
4. 本地構(gòu)建alpha-server(和上面步驟一致)
5. 啟動alpha-server
java -Dspring.profiles.active=mysql -Dloader.path=./plugins -D"spring.datasource.url=jdbc:mysql://${host_address}:3306/${database_name}? serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false " -jar alpha-server-${saga_version}-exec.jar
3.2 Omega配置
配置完alpha-server之后,就相當(dāng)于分布式事務(wù)的協(xié)調(diào)器已經(jīng)配置完成,剩下的就是omega的配置,也就是在實際開發(fā)中如何運用servicecomb-pack去處理分布式事務(wù)。本次講解會結(jié)合一個實際案例:購物系統(tǒng)中的下單流程和刪除產(chǎn)品流程來分別講解saga模式和tcc模式如何使用的。
3.2.1 環(huán)境準(zhǔn)備
本次案例:購物系統(tǒng)是采用分布式微服務(wù)架構(gòu),整體分為三個微服務(wù)應(yīng)用:orderManage訂單管理應(yīng)用、productManage產(chǎn)品管理應(yīng)用、stockManage庫存管理應(yīng)用
1. 添加依賴
分別在三個應(yīng)用的pom文件中添加Omega所需的依賴:
org.apache.servicecomb.pack omega-spring-starter ${servicecomb-pack.version} org.apache.servicecomb.pack omega-transport-resttemplate ${servicecomb-pack.version} org.apache.servicecomb.pack omega-spring-cloud-consul-starter ${servicecomb-pack.version} org.apache.servicecomb.pack omega-spring-cloud-eureka-starter ${servicecomb-pack.version}
*注意:請將${servicecomb-pack.version}更改為實際的版本號(推薦版本為0.4.0)
*注意:如需做集群,omega-spring-cloud-consul-starter和omega-spring-cloud-eureka-starter二選一,視項目的注冊中心而定。
2. 修改配置文件
分別在三個應(yīng)用的application.yml配置文件中添加alpha-server配置,具體配置如下:
#配置alpha-server地址 alpha: cluster: address: 10.15.15.172:8080 omega: enabled: true
注意:application.name一定不要過長,因為instanceId的格式是application.name+IP,并且長度為36,否則alpha-server事務(wù)持久化會報錯
以上兩個屬性配置為必填,因為alpha-server會依據(jù)application.name去查找對應(yīng)的Omega,其他應(yīng)用配置自行添加,address可根據(jù)alpha-server中的配置實際添加
至此,環(huán)境準(zhǔn)備已經(jīng)完畢,下面開始進(jìn)行應(yīng)用代碼編寫。
3.2.2 saga模式代碼編寫
在本次案例中,我們以一個下單流程來講解saga模式下代碼是如何編寫的。下單流程包括:點擊下單、查詢庫存、支付、更新庫存;訂單應(yīng)用作為起始服務(wù),調(diào)用庫存應(yīng)用和產(chǎn)品應(yīng)用,這兩個應(yīng)用對應(yīng)的服務(wù)作為參與服務(wù)(子事務(wù)),在訂單應(yīng)用下單,訂單應(yīng)用使用rest template向產(chǎn)品應(yīng)用發(fā)起調(diào)用校驗產(chǎn)品庫存,然后訂單應(yīng)用向庫存應(yīng)用發(fā)起支付請求(子事務(wù)1),支付成功后訂單應(yīng)用再向庫存應(yīng)用發(fā)起請求更新庫存(子事務(wù)2)。
1. @SagaStart
首先需要在應(yīng)用代碼中描述出saga事務(wù)的邊界,作為分布式事務(wù)的起始點,因此我們需要在訂單應(yīng)用中的createOrder()方法上添加該注解@SagaStart:
2. @ Compensable
支付對應(yīng)補(bǔ)償方法:
更新庫存:
更新庫存補(bǔ)償方法:
*注意:實現(xiàn)的服務(wù)和補(bǔ)償方法必須滿足冪等的要求
*注意:默認(rèn)情況下,超時需要顯示聲明
*注意:若全局事務(wù)起點與子事務(wù)重合,需同時聲明@SagaStart和@Compensable注解
*注意:補(bǔ)償方法的入?yún)⒈仨毰ctry方法入?yún)⒁恢拢駝t啟動時會報錯(alpha-server找不到補(bǔ)償方法)
3.2.3 tcc模式代碼編寫
下面我們會以刪除庫存流程來講解tcc模式是如何編寫代碼的。刪除庫存流程:由產(chǎn)品應(yīng)用發(fā)起(分布式事務(wù)起始),調(diào)用庫存應(yīng)用刪除對應(yīng)產(chǎn)品的庫存信息(tcc子事務(wù))。
本次調(diào)用使用的是feign的方式,因此需要在產(chǎn)品應(yīng)用中的pom文件添加相應(yīng)的依賴:
1. @TccStart
我們以產(chǎn)品應(yīng)用中的delete方法作為分布式事務(wù)起始點,因此在該方法上添加注解@TccStart:
2. @ Participate
在子事務(wù)所處的方法上添加該注解,并通過confirmMethod 以及cancelMethod屬性定義相關(guān)確認(rèn)以及取消方法名。這里需要注意的是這里提到的confirm,cancel方法的參數(shù)必須和try方法的相同。
Cancel邏輯:
*注意:confirm和cancel方法的入?yún)⒈仨毢蛅ry方法一致
*注意:目前tcc模式還不支持timeout
3.2.4事件信息獲取
默認(rèn)情況下,8080端口用來處理Omega處發(fā)起的grpc請求,用來做事務(wù)上下文等操作;而8090端口則用于處理查詢alpha處的事件信息。
1. saga-事件信息查詢api
統(tǒng)計所有事件狀態(tài):
http://${alpha-server.address:port}/saga/stats
統(tǒng)計最近事件狀態(tài):
http://${alpha-server.address:port}/saga/recent
根據(jù)事件狀態(tài)查詢事件列表:
http://${alpha-server.address:port}/saga/transactions
根據(jù)服務(wù)名稱查詢對應(yīng)的分布式事件列表:
http://${alpha-server.address:port}/saga/findTransactions
2. tcc-事件信息查詢api
Tcc目前沒有提供正式的查詢接口。但是有測試接口,在AlphaTccEventController中,可自行根據(jù)測試接口修改源碼,重新編譯即可。
目前alpha-server提供的事件查詢api不多,若有其他需求,用戶可自行編寫接口對數(shù)據(jù)庫進(jìn)行查詢。
本文所有觀點都出自個人見解,疏漏、錯誤之處在所難免,歡迎大家指正,希望能夠與大家一起交流和進(jìn)步。
[1]引用自:
http://servicecomb.apache.org/cn/docs/distributed-transaction-of-services-1/
[2]引用自:
http://servicecomb.apache.org/cn/docs/distributed-transaction-of-services-1/
精選提問:
問1:TCC實現(xiàn)的是強(qiáng)一致事務(wù)么?
問2:全局事務(wù)起點與子事務(wù)重合這個怎么理解?
問3:cancel操作是怎么做到的?是通過undo log做的,還是通過補(bǔ)償語句呢?