首頁 > 軟體

Java詳細分析LCN框架分散式事務

2022-07-28 22:04:31

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=123456

mybatis.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
redisu5BC6u7801
#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框架內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com