Java詳細(xì)分析LCN框架分布式事務(wù)
2PC兩階段提交協(xié)議
分布式事務(wù)通常采用2PC協(xié)議,全稱Two Phase Commitment Protocol。該協(xié)議主要為了解決在分布式數(shù)據(jù)庫(kù)場(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)庫(kù)地址: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ù)庫(kù)中執(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)文章
spring容器啟動(dòng)實(shí)現(xiàn)初始化某個(gè)方法(init)
這篇文章主要介紹了spring容器啟動(dòng)實(shí)現(xiàn)初始化某個(gè)方法(init),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java class文件格式之方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java class文件格式之方法的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
MyBatis結(jié)果映射(ResultMap)的使用
在MyBatis中,結(jié)果映射是實(shí)現(xiàn)數(shù)據(jù)庫(kù)結(jié)果集到Java對(duì)象映射的核心,它不僅支持簡(jiǎn)單的字段映射,還能處理字段名不一致、嵌套對(duì)象和集合映射等復(fù)雜場(chǎng)景,通過ResultMap,開發(fā)者可以靈活定義映射關(guān)系,以適應(yīng)各種需求,感興趣的可以了解一下2024-09-09
解決mybatis 中collection嵌套collection引發(fā)的bug
這篇文章主要介紹了解決mybatis 中collection嵌套collection引發(fā)的bug,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Java Synchronized鎖升級(jí)原理及過程剖析
這篇文章主要為大家詳細(xì)介紹一下Java中Synchronized鎖升級(jí)原理及過程,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)學(xué)習(xí)2022-08-08
圖解Eclipse j2ee開發(fā)環(huán)境的搭建過程
這篇文章以圖文結(jié)合的方式介紹了Eclipse j2ee開發(fā)環(huán)境的搭建過程,內(nèi)容很詳細(xì),每一個(gè)步驟都有對(duì)應(yīng)的操作截圖,需要的朋友可以參考下2015-08-08

