分散式事務實戰
轉載本文需註明出處:微信公眾號EAWorld,違者必究。
引言:
目錄:
1.分散式事務講解
2.分散式事務解決方案-servicecomb-pack
3.分散式事務實戰講解
1. 分散式事務講解
1.1事務原理
在講分散式事務之前,先聊一下事務。簡單講事務是資料庫管理系統執行過程中的一個邏輯單元,它能保證要麼一組資料庫操作全部執行成功,要麼全部失敗,而做到這些的原理就是事務的ACID四大特性。
A. Atomic原子性的簡稱,事務作為一個整體來執行,要麼全部成功,要麼全部失敗。
C. Consistency一致性的簡稱,事務應確保資料從一個一致的狀態轉變為另一個一致的狀態。
I. Isolation隔離性的簡稱,多個事務併發執行時,一個事務的執行不影響其他事務的執行。
D.Durability永續性的檢查,已提交的事務修改資料會被持久儲存。
1.2傳統單機資料庫事務
在傳統單體應用架構中,我們的業務資料通常都是儲存在一個資料庫中的,應用中的各個模組對資料庫直接進行操作。在這種場景中,事務是由資料庫提供的基於ACID特性來保證的。
例如,在一個使用者購物下單的場景中,涉及到使用者、訂單、支付、庫存等模組的一系列協同操作,如果其中一個模組出現問題,我們就可以通過資料庫提供的事務特性來保證本次下單操作要麼都成功,要麼都失敗。因為這些模組用的是同一個資料庫,所處的是同一個事務管理器,不需要做額外的其他操作就能保證事務的特性。
從廣義上來講,分散式事務其實也是事務,只是區別於單機事務不同之處是:由於業務上的定義和系統微服務架構的設計,很多大型的業務流程都被拆分成了多個單一的基礎服務,而為了保證每個微服務都能獨立進行開發和部署執行,通常都會採用一個微服務一個資料庫的架構配套,然後將內部服務進行封裝,以Rest api方式對外暴露。這樣以往基於資料庫來實現的資料操作,就變成了多個對外提供微服務的微服務系統之間的協同操作。在這種情況下,原有的單機事務方式已經不能夠使用了,因為多個服務就意味著存在多個事務管理器和多個資源,單個微服務的本地事務管理器只能保證本地事務的ACID,為了在多個服務之間能保證業務的事務性,參與分散式事務的微服務通常會依託協調器來完成相關的一致性協調操作。
那我們在微服務系統實際開發中,如何去實現協調器以處理分散式事務呢,這裡的解決方案是採用華為提供的servicecomb-pack框架來解決這一問題。
2. 分散式事務解決方案:
servicecomb-pack
2.1補償方式
在講servicecomb-pack之前先了解兩個概念:不完美補償(saga)和完美補償(tcc)。
saga:不完美補償,一般在系統中我們會專門為業務邏輯對應寫一個補償邏輯,如果業務邏輯執行失敗,就會去執行這個補償邏輯,我們稱這個補償邏輯為反向操作,這個反向操作同樣會留下操作痕跡,例如:在銀行系統中,客戶去ATM取錢,銀行會先對使用者賬戶進行扣款操作,如果本次取錢不成功,銀行系統會發出一筆衝正操作,將之前扣除的款項打回使用者賬戶,這個衝正操作在交易記錄裡面是開源查詢到的。
tcc:完美補償,cancel階段會徹底清楚之前的業務邏輯操作,使用者是感知不到的。例如:在一個交易平臺去發起交易,首先在try階段不會直接去扣除賬戶餘額,而且去檢查使用者的額度並重新整理額度,然後在confirm階段才去真正操作賬戶。如果出現異常,那麼在cancel階段就需要去執行業務邏輯來取消try階段產生的後果,釋放在try階段被佔用的額度。整個過程只有等confirm執行完畢,交易才算完成。
2.2servicecomb-pack
servicecomb-pack出自於華為微服務框架servicecomb,是一個開源的分散式事務最終一致性解決方案,該專案已交由Apache軟體基金會孵化,目前已經在apache畢業了。0.3.0版本之前叫servicecomb-saga,現版本已經改名為servicecomb-pack。
servicecomb-pack架構主要包含兩個元件:alpha和Omega
alpha:alpha其實就是一個server端,需要使用者自行編譯執行,它的作用就是上述中的分散式事務協調器,主要作用是和Omega客戶端進行通訊,接收omega發過來的事務事件,然後進行持久化儲存事務以及修改協調子事務的狀態,從而保證全域性事務中的所有子事務狀態都一致,即要麼全執行完成,要麼全執行失敗。
omega:Omega端其實可以看成是一個微服務中內嵌的agent,主要作用是監控本地子事務的執行情況並向alpha-server端傳送子事務執行事件以及傳遞全域性事務ID,並在異常情況下會根據alpha下發的操作事件進行相應的補償操作。
Omega會以切面程式設計的方式嚮應用程式注入相關的處理模組,幫助我們構建分散式事務呼叫的上下文。Omega在事務處理初始階段處理事務的相關準備的操作,在事務執行完畢做一些清理的操作,例如建立分散式事務起始事件,以及相關的子事件,根據事務的執行的成功或者失敗生產相關的事務終止或者失敗事件。這樣帶來的好處是使用者的程式碼只需要新增幾個annotation 來描述分散式事務執行範圍,以及與本地的事務處理恢復的相關函式資訊,Omega就能通過切面注入的程式碼能夠追蹤與本地事務的執行情況。Omega會將本地事務執行的情況以事件的方式通知給Alpha。由於單個Omega不可能知曉一個分散式事務下其他參與服務的執行情況, 這樣就需要Alpha扮演一個十分重要的協調者的角色。Alpha將收集到的分散式事務事件資訊整理彙總,通過分析這些事件之間的關係可以瞭解到分散式事務的執行情況, Alpha通過向Omega下發相關的執行指令由Omega執行相關提交或恢復操作,實現分散式事務的最終一致性。
在瞭解的Pack實現的部分細節之後, 我們可以從下圖進一步瞭解ServiceComb Pack架構下,Alpha與Omega內部各模組之間的關係圖[1]。
整個架構分為三個部分,一個是Alpha協調器,另外一個就是注入到微服務例項中的Omega,以及Alpha與Omega之間的互動協議, 目前ServiceComb Pack支援Saga 以及TCC兩種分散式事務協調協議實現。
Omega包含了與分析使用者分散式事務邏輯相關的事務註解模組(Transaction Annotation)以及事務攔截器(Transaction Interceptor);分散式事務執行相關的事務上下文(Transaction Context),事務回撥(Transaction Callback) ,事務執行器(Transaction Executor);以及負責與Alpha進行通訊的事務傳輸(Transaction Transport)模組。
事務註解模組是分散式事務的使用者介面,使用者將這些標註新增到自己的業務程式碼之上用以描述與分散式事務相關的資訊,這樣Omega就可以按照分散式事務的協調要求進行相關的處理。如果大家擴充套件自己的分散式事務,也可以通過定義自己的事務標註來實現。
事務攔截器這個模組我們可以藉助AOP手段,在使用者標註的程式碼基礎上新增相關的攔截程式碼,獲取到與分散式事務以及本地事務執行相關的資訊,並藉助事務傳輸模組與Alpha進行通訊傳遞事件。
事務上下文為Omega內部提供了一個傳遞事務呼叫資訊的一個手段,藉助前面提到的全域性事務ID以及本地事務ID的對應關係,Alpha可以很容易檢索到與一個分散式事務相關的所有本地事務事件資訊。
事務執行器主要是為了處理事務呼叫超時設計的模組。由於Alpha與Omega之間的連線有可能不可靠,Alpha端很難判斷Omega本地事務執行超時是由Alpha與Omega直接的網路引起的還是Omega自身呼叫的問題,因此設計了事務執行器來監控Omega的本地的執行情況,簡化Omega的超時操作。目前Omega的預設實現是直接呼叫事務方法,由Alpha的後臺服務通過掃描事件表的方式來確定事務執行時間是否超時。
事務回撥在Omega與Alpha建立連線的時候就會向Alpha進行註冊,當Alpha需要進行相關的協調操作的時候,會直接呼叫Omega註冊的回撥方法進行通訊。由於微服務例項在雲化場景啟停會很頻繁,我們不能假設Alpha一直能找到原有註冊上的事務回撥, 因此我們建議微服務例項是無狀態的,這樣Alpha只需要根據服務名就能找到對應的Omega進行通訊。
事務傳輸模組負責Omega與Alpha之間的通訊,在具體的實現過程中,Pack通過定義相關的Grpc描述介面檔案定義了TCC 以及Saga的事務互動方法, 同時也定義了與互動相關的事件[2]。
3. 分散式事務實戰
3.1 alpha-server配置
3.1.1編譯alpha-server
1. 環境準備
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. 本地構建alpha-server
$ cd servicecomb-pack
$ mvn clean install -DskipTests -Pspring-boot-2
在執行完命令後,可在alpha/alpha-server/target/saga/alpha-server-${version}-exec.jar中找到alpha-server的可執行jar包
5. 初始化資料庫
可在alpha\alpha-server\src\main\resources目錄下找到schema-mysql.sql和schema-postgresql.sql兩個sql檔案,可自行根據所選資料庫進行初始化即可。
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
*注意:請在執行命令前將${saga_version}和${host_address}更改為實際值
至此,alpha-server全域性事務管理器已經啟動成功。
3.1.2替換postgresql為mysql
目前alpha-server支援pg和mysql兩種資料庫,預設為pg,如需改為mysql,需要進行如下操作:
1. 安裝並執行mysql
2. 修改pom檔案,新增依賴
alpha-server/pom.xml,新增mysql依賴
ependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.15</version> </dependency>
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. 本地構建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之後,就相當於分散式事務的協調器已經配置完成,剩下的就是omega的配置,也就是在實際開發中如何運用servicecomb-pack去處理分散式事務。本次講解會結合一個實際案例:購物系統中的下單流程和刪除產品流程來分別講解saga模式和tcc模式如何使用的。
3.2.1 環境準備
本次案例:購物系統是採用分散式微服務架構,整體分為三個微服務應用:orderManage訂單管理應用、productManage產品管理應用、stockManage庫存管理應用
1. 新增依賴
分別在三個應用的pom檔案中新增Omega所需的依賴:
<dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-starter</artifactId> <version>${servicecomb-pack.version}</version> </dependency> <dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-transport-resttemplate</artifactId> <version>${servicecomb-pack.version}</version> </dependency> <!--非必需 --> <dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-consul-starter</artifactId> <version>${servicecomb-pack.version}</version> </dependency> <dependency> <groupId>org.apache.servicecomb.pack</groupId> <artifactId>omega-spring-cloud-eureka-starter</artifactId> <version>${servicecomb-pack.version}</version> </dependency>
*注意:請將${servicecomb-pack.version}更改為實際的版本號(推薦版本為0.4.0)
*注意:如需做叢集,omega-spring-cloud-consul-starter和omega-spring-cloud-eureka-starter二選一,視專案的註冊中心而定。
2. 修改配置檔案
分別在三個應用的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事務持久化會報錯
以上兩個屬性配置為必填,因為alpha-server會依據application.name去查詢對應的Omega,其他應用配置自行新增,address可根據alpha-server中的配置實際新增
至此,環境準備已經完畢,下面開始進行應用程式碼編寫。
3.2.2 saga模式程式碼編寫
在本次案例中,我們以一個下單流程來講解saga模式下程式碼是如何編寫的。下單流程包括:點選下單、查詢庫存、支付、更新庫存;訂單應用作為起始服務,呼叫庫存應用和產品應用,這兩個應用對應的服務作為參與服務(子事務),在訂單應用下單,訂單應用使用rest template向產品應用發起呼叫校驗產品庫存,然後訂單應用向庫存應用發起支付請求(子事務1),支付成功後訂單應用再向庫存應用發起請求更新庫存(子事務2)。
1. @SagaStart
首先需要在應用程式碼中描述出saga事務的邊界,作為分散式事務的起始點,因此我們需要在訂單應用中的createOrder()方法上新增該註解@SagaStart:
2. @ Compensable
支付對應補償方法:
更新庫存:
更新庫存補償方法:
*注意:實現的服務和補償方法必須滿足冪等的要求
*注意:預設情況下,超時需要顯示宣告
*注意:若全域性事務起點與子事務重合,需同時宣告@SagaStart和@Compensable註解
*注意:補償方法的入參必須與try方法入參一致,否則啟動時會報錯(alpha-server找不到補償方法)
3.2.3 tcc模式程式碼編寫
下面我們會以刪除庫存流程來講解tcc模式是如何編寫程式碼的。刪除庫存流程:由產品應用發起(分散式事務起始),呼叫庫存應用刪除對應產品的庫存資訊(tcc子事務)。
本次呼叫使用的是feign的方式,因此需要在產品應用中的pom檔案新增相應的依賴:
1. @TccStart
我們以產品應用中的delete方法作為分散式事務起始點,因此在該方法上新增註解@TccStart:
2. @ Participate
在子事務所處的方法上新增該註解,並通過confirmMethod 以及cancelMethod屬性定義相關確認以及取消方法名。這裡需要注意的是這裡提到的confirm,cancel方法的引數必須和try方法的相同。
Cancel邏輯:
*注意:confirm和cancel方法的入參必須和try方法一致
*注意:目前tcc模式還不支援timeout
3.2.4事件資訊獲取
預設情況下,8080埠用來處理Omega處發起的grpc請求,用來做事務上下文等操作;而8090埠則用於處理查詢alpha處的事件資訊。
1. saga-事件資訊查詢api
統計所有事件狀態:
http://${alpha-server.address:port}/saga/stats
統計最近事件狀態:
http://${alpha-server.address:port}/saga/recent
根據事件狀態查詢事件列表:
http://${alpha-server.address:port}/saga/transactions
根據服務名稱查詢對應的分散式事件列表:
http://${alpha-server.address:port}/saga/findTransactions
2. tcc-事件資訊查詢api
Tcc目前沒有提供正式的查詢介面。但是有測試介面,在AlphaTccEventController中,可自行根據測試介面修改原始碼,重新編譯即可。
目前alpha-server提供的事件查詢api不多,若有其他需求,使用者可自行編寫介面對資料庫進行查詢。
本文所有觀點都出自個人見解,疏漏、錯誤之處在所難免,歡迎大家指正,希望能夠與大家一起交流和進步。
[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實現的是強一致事務麼?
問2:全域性事務起點與子事務重合這個怎麼理解?
問3:cancel操作是怎麼做到的?是通過undo log做的,還是通過補償語句呢?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31562043/viewspace-2669331/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分散式事務理論加實戰分散式
- 分散式事務(3)---RocketMQ實現分散式事務原理分散式MQ
- 分散式事務(4)---RocketMQ實現分散式事務專案分散式MQ
- 微服務分散式事務4種解決方案實戰微服務分散式
- 分散式事務(一)—分散式事務的概念分散式
- kratos分散式事務實踐分散式
- go-zero微服務實戰系列(十、分散式事務如何實現)Go微服務分散式
- 實戰與原理:如何基於RocketMQ實現分散式事務?MQ分散式
- 分散式事務和分散式hash分散式
- 理解分散式事務分散式
- 分散式事務概述分散式
- 聊聊分散式事務分散式
- seata 分散式事務分散式
- php基於dtm分散式事務管理器實現tcc模式分散式事務demoPHP分散式模式
- 分散式系統(三)——分散式事務分散式
- 基於RocketMQ實現分散式事務MQ分散式
- 使用Spring Boot實現分散式事務Spring Boot分散式
- go-zero分散式事務實踐Go分散式
- SpringCloud+RocketMQ實現分散式事務SpringGCCloudMQ分散式
- Apache ShardingSphere 如何實現分散式事務Apache分散式
- 分散式事務~從seata例項來學習分散式事務分散式
- 分散式事務之資料庫事務與JDBC事務實現(一)分散式資料庫JDBC
- 分散式系列七: 分散式事務理論分散式
- 從 Oracle 轉型 MySQL 分散式事務資料庫的實戰旅途OracleMySql分散式資料庫
- SpringCloudAlibaba分散式事務解決方案Seata實戰與原始碼分析-上SpringGCCloud分散式原始碼
- SpringCloudAlibaba分散式事務解決方案Seata實戰與原始碼分析-中SpringGCCloud分散式原始碼
- 分散式事務之Spring事務與JMS事務(二)分散式Spring
- 分散式事務的幾種實現方式分散式
- AspNetCore&MassTransit Courier實現分散式事務NetCore分散式
- Golang 實現 Redis(8): TCC分散式事務GolangRedis分散式
- 分散式事務介紹分散式
- 分散式事務總結分散式
- 分散式事務Saga模式分散式模式
- SpringCloud(六)分散式事務SpringGCCloud分散式
- 分散式訊息佇列RocketMQ--事務訊息--解決分散式事務的最佳實踐分散式佇列MQ
- PHP 微服務之 [分散式事務]PHP微服務分散式
- PHP 微服務之【分散式事務】PHP微服務分散式
- 本地事務和分散式事務的區別分散式