Java詳細(xì)分析LCN框架分布式事務(wù)
2PC兩階段提交協(xié)議
分布式事務(wù)通常采用2PC協(xié)議,全稱Two Phase Commitment Protocol。該協(xié)議主要為了解決在分布式數(shù)據(jù)庫場(chǎng)景下,所有節(jié)點(diǎn)間數(shù)據(jù)一致性的問題。分布式事務(wù)通過2PC協(xié)議將提交分成兩個(gè)階段:
- 階段一為準(zhǔn)備(prepare)階段。即所有的參與者準(zhǔn)備執(zhí)行事務(wù)并鎖住需要的資源。參與者ready時(shí),向transaction manager報(bào)告已準(zhǔn)備就緒。
- 階段二為提交階段(commit)。當(dāng)transaction manager確認(rèn)所有參與者都ready后,向所有參與者發(fā)送commit命令。
2PC和3PC的區(qū)別就是解決參與者超時(shí)的問題和多加了一層詢問,保證數(shù)據(jù)的傳輸可靠性。
LCN
LCN并不生產(chǎn)事務(wù),LCN只是本地事務(wù)的協(xié)調(diào)工,TX-LCN定位于一款事務(wù)協(xié)調(diào)性框架,框架其本身并不操作事務(wù),而是基于對(duì)事務(wù)的協(xié)調(diào)從而達(dá)到事務(wù)一致性的效果。
參考文檔:https://www.codingapi.com/docs/txlcn-preface/
LCN基本實(shí)現(xiàn)原理
- 發(fā)起方與參與方都與我們的 LCN 管理器一直保持長(zhǎng)連接;
- 發(fā)起方在調(diào)用接口之前,先向 LCN 管理器申請(qǐng)一個(gè)全局的事務(wù)分組id;
- 發(fā)起方調(diào)用接口的時(shí)候在請(qǐng)求頭中傳遞事務(wù)分組id;
- 參與方獲取到請(qǐng)求頭中有事務(wù)分組的id的,則當(dāng)前業(yè)務(wù)邏輯執(zhí)行完實(shí)現(xiàn)假關(guān)閉,不會(huì)提交或者回滾當(dāng)前的事務(wù)。
- 發(fā)起方調(diào)用完接口后,如果出現(xiàn)異常的情況下,在通知給事務(wù)協(xié)調(diào)者回滾事務(wù),這時(shí)候事務(wù)協(xié)調(diào)則告訴給參與方回滾當(dāng)前的事務(wù)。
搭建全局協(xié)調(diào)者
1、在github上面下載 Lcn 源代碼
倉(cāng)庫地址:https://github.com/codingapi/tx-lcn,注意下載版本。
2、將項(xiàng)目導(dǎo)入idea中,啟動(dòng)對(duì)應(yīng)的項(xiàng)目
修改對(duì)應(yīng)的配置文件:
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連接請(qǐng)求端口
tx-lcn.manager.port=8070
# 心跳檢測(cè)時(shí)間(ms)
tx-lcn.manager.heart-time=15000
# 分布式事務(wù)執(zhí)行總時(shí)間
tx-lcn.manager.dtx-time=30000
#參數(shù)延遲刪除時(shí)間單位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=
將項(xiàng)目中提供的SQL語句在數(shù)據(jù)庫中執(zhí)行,創(chuàng)建對(duì)應(yīng)的表。
請(qǐng)求路徑:http://127.0.0.1:7970/admin/index.html
默認(rèn)登錄密碼:codingapi
登錄成功頁面
端口介紹:
8070:TM事務(wù)消息端口
7970:后臺(tái)管理頁面登錄頁面
使用LCN解決分布式事務(wù)問題
在分布式系統(tǒng)A系統(tǒng)調(diào)用B系統(tǒng)服務(wù)接口的時(shí)候時(shí)候,兩個(gè)服務(wù)的都需要使用 LCN 來控制分布式事務(wù)。
使用步驟:
1、引入lcn 相關(guān)的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項(xiàng)目主類上加上注解:@EnableDistributedTransaction
參與方與發(fā)起方都要加上該注解
@LcnTransaction
@Transactional
調(diào)用案例:
A服務(wù)的方法,需要去調(diào)用B服務(wù)方法
@Service public class ServiceA { @Autowired private ValueDao valueDao; //本地db操作 @Autowired private ServiceB serviceB;//遠(yuǎn)程B模塊業(yè)務(wù) @LcnTransaction //分布式事務(wù)注解 @Transactional //本地事務(wù)注解 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服務(wù)的方法,被A服務(wù)的方法調(diào)用
@Service public class ServiceB { @Autowired private ValueDao valueDao; //本地db操作 @LcnTransaction //分布式事務(wù)注解 @Transactional //本地事務(wù)注解 public String rpc(String value) throws BusinessException { valueDao.save(value); // (4) valueDao.saveBackup(value); // (5) return "ok-B"; } }
源碼分析
一個(gè)請(qǐng)求一個(gè)線程
代碼執(zhí)行邏輯:
1、判斷方法是否有加上@L cnTransaction, 如果有加上該注解則直接會(huì)走 切面類 TransactionAspect
2、判斷當(dāng)前線程緩存中是否有事務(wù)分組id,如果沒有緩存則是為發(fā)起方,如果有緩存則是為參與方
3、隨機(jī)的創(chuàng)建分組的id,將該分組id注冊(cè)到協(xié)調(diào)者中。
4、本地 threadLock 緩存該事務(wù)分組id
5、A服務(wù)(發(fā)起方)調(diào)用B(參與方)服務(wù)的接口,重寫了 RequestInterceptor(該接口是feign框架提供的攔截器,基本上每個(gè)rpc框架都會(huì)提供類似的攔截器) feign 客戶端,將該事務(wù)分組id設(shè)置到請(qǐng)求中
6、執(zhí)行到B服務(wù)接口,Spring TracingApplier實(shí)現(xiàn),在請(qǐng)求之前攔截,從請(qǐng)求頭中獲取事務(wù)分組id,放入到當(dāng)前線程緩存中
7、B服務(wù)接口走到aop里面代碼時(shí),會(huì)先判斷是發(fā)起方還是參與方。
8、從緩存中獲取該事務(wù)分組id,當(dāng)前派單服務(wù)則是為參與方,在告訴給協(xié)調(diào)者加入該事務(wù)分組。.
Lcn 如何判斷自己是發(fā)起方還是參與方?
根據(jù)當(dāng)前的線程threadlocal 中獲取事務(wù)分組id, 如果能夠成功獲取到則是為參與方,沒有能夠獲取到就是為發(fā)起方。
參與方如何加入LCN全局協(xié)調(diào)者?
發(fā)起方會(huì)把事務(wù)id注冊(cè)到協(xié)調(diào)者里面去,參與方根據(jù)請(qǐng)求頭里面的事務(wù)分組id加入該事務(wù)。
發(fā)起方如何通知全局回滾還是提交?
發(fā)起方的方法執(zhí)行完成之后,會(huì)修改事務(wù)狀態(tài),再根據(jù)全局協(xié)調(diào)者通知其他參與者事務(wù)執(zhí)行完成。反之,如果發(fā)起方的方法執(zhí)行方法異常,事務(wù)狀態(tài)改為錯(cuò)誤狀態(tài),再通過全局協(xié)調(diào)者發(fā)送給其他參與者,參與者再回滾事務(wù)即可。
A調(diào)用B,B調(diào)用C 到底會(huì)生產(chǎn)幾次事務(wù)id?
每次原遠(yuǎn)程調(diào)用接口都會(huì)生成一個(gè)事務(wù)id,但是一條調(diào)用鏈上只有一個(gè)事務(wù)分組id(全局id)。只有A是發(fā)起方,B和C都是參與方??梢詮恼?qǐng)求頭中獲取到事務(wù)分組id就是參與方,表示加入到這個(gè)分組里面去的。
入口:@LcnTransaction,TransactionAspect 切面類。
feign 重寫的攔截器,給請(qǐng)求頭添加信息,事務(wù)分組id
到此這篇關(guān)于Java詳細(xì)分析LCN框架分布式事務(wù)的文章就介紹到這了,更多相關(guān)Java LCN框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis執(zhí)行錯(cuò)誤但sql執(zhí)行正常問題
這篇文章主要介紹了mybatis執(zhí)行錯(cuò)誤但sql執(zhí)行正常問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01SkyWalking?自定義插件(Spring?RabbitMQ)具體分析過程
這篇文章主要介紹了SkyWalking?自定義插件(Spring?RabbitMQ)具體分析過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02mybatis注解之@Mapper和@MapperScan的使用
這篇文章主要介紹了mybatis注解之@Mapper和@MapperScan的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10詳解SpringMVC的攔截器鏈實(shí)現(xiàn)及攔截器鏈配置
攔截器(Interceptor)是一種動(dòng)態(tài)攔截方法調(diào)用的機(jī)制,在SpringMVC中動(dòng)態(tài)攔截控制器方法的執(zhí)行。本文將詳細(xì)講講SpringMVC中攔截器參數(shù)及攔截器鏈配置,感興趣的可以嘗試一下2022-08-08Java獲取環(huán)境變量(System.getenv)的方法
本文主要介紹了Java獲取環(huán)境變量(System.getenv)的方法,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05Java利用opencv實(shí)現(xiàn)用字符展示視頻或圖片的方法
這篇文章主要介紹了Java利用opencv實(shí)現(xiàn)用字符展示視頻或圖片的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Maven中plugins與pluginManagement的區(qū)別說明
這篇文章主要介紹了Maven中plugins與pluginManagement的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09JSP服務(wù)器端和前端出現(xiàn)亂碼問題解決方案
這篇文章主要介紹了JSP服務(wù)器端和前端出現(xiàn)亂碼問題解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02