Java詳細分析LCN框架分佈式事務
2PC兩階段提交協議
分佈式事務通常采用2PC協議,全稱Two Phase Commitment Protocol。該協議主要為瞭解決在分佈式數據庫場景下,所有節點間數據一致性的問題。分佈式事務通過2PC協議將提交分成兩個階段:
- 階段一為準備(prepare)階段。即所有的參與者準備執行事務並鎖住需要的資源。參與者ready時,向transaction manager報告已準備就緒。
- 階段二為提交階段(commit)。當transaction manager確認所有參與者都ready後,向所有參與者發送commit命令。
2PC和3PC的區別就是解決參與者超時的問題和多加瞭一層詢問,保證數據的傳輸可靠性。
LCN
LCN並不生產事務,LCN隻是本地事務的協調工,TX-LCN定位於一款事務協調性框架,框架其本身並不操作事務,而是基於對事務的協調從而達到事務一致性的效果。
參考文檔:https://www.codingapi.com/docs/txlcn-preface/
LCN基本實現原理
- 發起方與參與方都與我們的 LCN 管理器一直保持長連接;
- 發起方在調用接口之前,先向 LCN 管理器申請一個全局的事務分組id;
- 發起方調用接口的時候在請求頭中傳遞事務分組id;
- 參與方獲取到請求頭中有事務分組的id的,則當前業務邏輯執行完實現假關閉,不會提交或者回滾當前的事務。
- 發起方調用完接口後,如果出現異常的情況下,在通知給事務協調者回滾事務,這時候事務協調則告訴給參與方回滾當前的事務。
搭建全局協調者
1、在github上面下載 Lcn 源代碼
倉庫地址:https://github.com/codingapi/tx-lcn,註意下載版本。
2、將項目導入idea中,啟動對應的項目
修改對應的配置文件:
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://www.kaicostudy.com:3306/transaction_lcn?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true# TxManager Host Ip
tx-lcn.manager.host=127.0.0.1
# TxClient連接請求端口
tx-lcn.manager.port=8070
# 心跳檢測時間(ms)
tx-lcn.manager.heart-time=15000
# 分佈式事務執行總時間
tx-lcn.manager.dtx-time=30000
#參數延遲刪除時間單位ms
tx-lcn.message.netty.attr-delay-time=10000
tx-lcn.manager.concurrent-level=128
# 開啟日志
tx-lcn.logger.enabled=true
logging.level.com.codingapi=debug#redis 連接信息
spring.redis.host=www.kaicostudy.com
spring.redis.port=6379
redis\u5BC6\u7801
#spring.redis.password=
將項目中提供的SQL語句在數據庫中執行,創建對應的表。
請求路徑:http://127.0.0.1:7970/admin/index.html
默認登錄密碼:codingapi
登錄成功頁面
端口介紹:
8070:TM事務消息端口
7970:後臺管理頁面登錄頁面
使用LCN解決分佈式事務問題
在分佈式系統A系統調用B系統服務接口的時候時候,兩個服務的都需要使用 LCN 來控制分佈式事務。
使用步驟:
1、引入lcn 相關的maven依賴
<dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-tc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.codingapi.txlcn</groupId> <artifactId>txlcn-txmsg-netty</artifactId> <version>5.0.2.RELEASE</version> </dependency>
2、yml 配置文件增加 lcn 的配置
tx-lcn: client: manager-address: www.kaicostudy.com:8070 logger: enabled: true
3、使用
springboot項目主類上加上註解:@EnableDistributedTransaction
參與方與發起方都要加上該註解
@LcnTransaction
@Transactional
調用案例:
A服務的方法,需要去調用B服務方法
@Service public class ServiceA { @Autowired private ValueDao valueDao; //本地db操作 @Autowired private ServiceB serviceB;//遠程B模塊業務 @LcnTransaction //分佈式事務註解 @Transactional //本地事務註解 public String execute(String value) throws BusinessException { // step1. call remote service B String result = serviceB.rpc(value); // (1) // step2. local store operate. DTX commit if save success, rollback if not. valueDao.save(value); // (2) valueDao.saveBackup(value); // (3) return result + " > " + "ok-A"; } }
B服務的方法,被A服務的方法調用
@Service public class ServiceB { @Autowired private ValueDao valueDao; //本地db操作 @LcnTransaction //分佈式事務註解 @Transactional //本地事務註解 public String rpc(String value) throws BusinessException { valueDao.save(value); // (4) valueDao.saveBackup(value); // (5) return "ok-B"; } }
源碼分析
一個請求一個線程
代碼執行邏輯:
1、判斷方法是否有加上@L cnTransaction, 如果有加上該註解則直接會走 切面類 TransactionAspect
2、判斷當前線程緩存中是否有事務分組id,如果沒有緩存則是為發起方,如果有緩存則是為參與方
3、隨機的創建分組的id,將該分組id註冊到協調者中。
4、本地 threadLock 緩存該事務分組id
5、A服務(發起方)調用B(參與方)服務的接口,重寫瞭 RequestInterceptor(該接口是feign框架提供的攔截器,基本上每個rpc框架都會提供類似的攔截器) feign 客戶端,將該事務分組id設置到請求中
6、執行到B服務接口,Spring TracingApplier實現,在請求之前攔截,從請求頭中獲取事務分組id,放入到當前線程緩存中
7、B服務接口走到aop裡面代碼時,會先判斷是發起方還是參與方。
8、從緩存中獲取該事務分組id,當前派單服務則是為參與方,在告訴給協調者加入該事務分組。.
Lcn 如何判斷自己是發起方還是參與方?
根據當前的線程threadlocal 中獲取事務分組id, 如果能夠成功獲取到則是為參與方,沒有能夠獲取到就是為發起方。
參與方如何加入LCN全局協調者?
發起方會把事務id註冊到協調者裡面去,參與方根據請求頭裡面的事務分組id加入該事務。
發起方如何通知全局回滾還是提交?
發起方的方法執行完成之後,會修改事務狀態,再根據全局協調者通知其他參與者事務執行完成。反之,如果發起方的方法執行方法異常,事務狀態改為錯誤狀態,再通過全局協調者發送給其他參與者,參與者再回滾事務即可。
A調用B,B調用C 到底會生產幾次事務id?
每次原遠程調用接口都會生成一個事務id,但是一條調用鏈上隻有一個事務分組id(全局id)。隻有A是發起方,B和C都是參與方。可以從請求頭中獲取到事務分組id就是參與方,表示加入到這個分組裡面去的。
入口:@LcnTransaction,TransactionAspect 切面類。
feign 重寫的攔截器,給請求頭添加信息,事務分組id
到此這篇關於Java詳細分析LCN框架分佈式事務的文章就介紹到這瞭,更多相關Java LCN框架內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Springboot整合Redis與數據持久化
- Springboot/Springcloud項目集成redis進行存取的過程解析
- springboot 整合fluent mybatis的過程,看這篇夠瞭
- 使用redis實現延遲通知功能(Redis過期鍵通知)
- Spring整合redis的操作代碼