1. 程式人生 > >springboot搭配TXmanager實現分散式事務

springboot搭配TXmanager實現分散式事務

前言:寫這個文章主要是給自己做下筆記以免遺忘,同時也給各大碼農分享一下,互相學習。
分散式事務的需求來源於系統的服務化。在微服務系統中,無法使用傳統的事務達到資料庫的一致性。每個子服務都有自己獨立的資料來源,如果系統初期,沒有分表分庫,每個子服務的資料來源都是連線同一個資料庫主機,同一個資料庫,那麼還可以簡單的利用傳統的事務,增加程式碼的冗餘達到事務效果。而往往如果已經需要考慮事務的時候,我們的系統應該已經達到了一定的使用者量,開始了分表分庫。故而分散式事務還是需要了解和使用的。
分散式事務有很多種實現方式,我在這主要談論的是我熟悉的 “分三階段提交模式”,這種模式的原理比較簡單,當然了,效能上是有缺陷的。其他實現方式,可自行Google或者百度一下。

框架的特點

  1. 支援各種基於spring的db框架
  2. 相容SpringCloud、Dubbo、motan
  3. 使用簡單,低依賴,程式碼完全開源
  4. 基於切面的強一致性事務框架
  5. 高可用,模組可以依賴RPC模組做叢集化,TxManager也可以做叢集化
  6. 支援本地事務和分散式事務共存
  7. 支援事務補償機制,增加事務補償決策提醒
  8. 新增外掛拓展機制

原始碼目錄說明

  1. transaction-dubbo LCN dubbo rpc框架擴充套件支援
  2. transaction-springcloud LCN springcloud rpc框架擴充套件支援
  3. tx-client 是LCN核心tx模組端控制框架
  4. tx-manager 是LCN 分散式事務協調器
  5. tx-plugins-db 是LCN 對關係型資料庫的外掛支援
  6. tx-plugins-nodb 是LCN 對於無資料庫模組的外掛支援

不說太多了,直接上乾貨。(本文主要針對springcloud的)

1、去github.com下載tx-lcn-master原始碼
tx-lcn-master
目前更新到了4.1.0版本
LCN分散式事務框架的核心功能是對本地事務的協調控制,框架本身並不建立事務,只是對本地事務做協調控制。因此該框架與其他第三方的框架相容性強,支援所有的關係型資料庫事務,支援多資料來源,支援與第三方資料庫框架一塊使用(例如 sharding-jdbc),在使用框架的時候只需要新增分散式事務的註解即可,對業務的侵入性低。LCN框架主要是為微服務框架提供分散式事務的支援,在微服務框架上做了進一步的事務機制優化,在一些負載場景上LCN事務機制要比本地事務機制的效能更好,4.0以後框架開方了外掛機制可以讓更多的第三方框架支援進來。
更多詳情請參考官網

https://www.txlcn.org
我處理過的tx-lcn-master:https://pan.baidu.com/s/1M9SeulYe1j4ho9uuncWuBA
提取碼:3ss3
2、配置分散式事務協調器TXmanager
把TXmanager配置好(redis連線資訊,eureka註冊中心,開放的埠),打包部署到自己的微服務中
主要修改的有三個地方:

#服務埠
server.port=8899

#eureka 地址
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/
eureka.instance.prefer-ip-address=true

##redis 單點環境配置
#redis
#redis主機地址
spring.redis.host=127.0.0.1
#redis主機埠
spring.redis.port=6379
#redis連結密碼
spring.redis.password=
spring.redis.pool.maxActive=10
spring.redis.pool.maxWait=-1
spring.redis.pool.maxIdle=5
spring.redis.pool.minIdle=0
spring.redis.timeout=0

eureka註冊中心一定要配置自己系統的地址,關於TXmanager的執行機制在此不做太多的說明。
配置好了,打包釋出即可進入被呼叫的狀態。
3、服務中加入分散式事務管理(加入LCN事務管理)
1)把LCN框架原始碼編譯安裝到本地倉庫

由於LCN的maven中央倉庫不一定能下載到依賴,最好是自行把原始碼安裝到本地倉庫進行依賴。本人是這麼做的。當然你也可以先嚐試直接獲取中央倉庫的,如若不行再說。

2)pom.xml中新增LCN框架的依賴

     <properties>
        <!--lcn的版本-->
        <lcn.last.version>4.1.0</lcn.last.version>
    </properties>
    <!--LCN基於springcloud的分散式事務框架-->
    <dependency>
        <groupId>com.codingapi</groupId>
        <artifactId>transaction-springcloud</artifactId>
        <version>${lcn.last.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--LCN基於關係型資料模組的封裝-->
    <dependency>
        <groupId>com.codingapi</groupId>
        <artifactId>tx-plugins-db</artifactId>
        <version>${lcn.last.version}</version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>*</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

transaction-springcloud LCN springcloud rpc框架擴充套件支援,我們這裡使用的是springcloud
tx-plugins-db 是LCN 對關係型資料庫的外掛支援,我們這裡使用的是mysql資料庫

3)配置TXmanager的訪問地址和埠號

spring.application.name = demo1
server.port = 8081
#${random.int[9000,9999]}
eureka.client.service-url.defaultZone=http://192.168.0.150:8761/eureka/

#txmanager地址
tm.manager.url=http://127.0.0.1:8899/tx/manager/

這裡配置的txmanager地址就是我們在上面第二步配置的配置分散式事務協調器TXmanager並且部署到微服務環境中的子服務地址。我們發現,它還是使用傳統的ip+port去訪問的。雖然txmanager也作為子服務註冊到了eureka上,但目前我還不確定是否可以根據applicationName訪問到,讀者可以試試。
4)分散式事務發起方
需要實現TxManagerTxUrlService和TxManagerHttpRequestService這兩個介面,並作為bean注入到spring中。處理http請求和對伺服器的連線

實現 TxManagerTxUrlService

import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * create by lorne on 2018/11/18
 */
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{

    @Value("${tm.manager.url}") //分散式事務協調器TXmanager訪問地址
    private String url;

    @Override
    public String getTxUrl() {
        System.out.println("load tm.manager.url ");
        return url;
    }
}

實現 TxManagerHttpRequestService

    import com.codingapi.tx.netty.service.TxManagerHttpRequestService;
    import com.lorne.core.framework.utils.http.HttpUtils;
    import org.springframework.stereotype.Service;
    /**
     * create by lorne on 2018/11/18
     */
    @Service
    public class TxManagerHttpRequestServiceImpl implements TxManagerHttpRequestService{

    @Override
    public String httpGet(String url) {
        System.out.println("httpGet-start");
        String res = HttpUtils.get(url);
        System.out.println("httpGet-end");
        return res;
    }

    @Override
    public String httpPost(String url, String params) {
        System.out.println("httpPost-start");
        String res = HttpUtils.post(url,params);
        System.out.println("httpPost-end");
        return res;
    }
}

加入分散式事務註解@TxTransaction(isStart = true),開啟分散式事務。isStart = true宣告為分散式事務發起方

@Service
public class DemoServiceImpl implements DemoService {

    @Autowired
    private Demo2Feign demo2Feign;//遠端呼叫,同一資料庫,有事務
    @Autowired
    private Demo3Feign demo3Feign;//遠端呼叫,不同資料庫,有事務
    @Autowired
    private TestMapper testMapper;

    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public List<Test> list() {
        return testMapper.findAll();
    }

    @Override
    @TxTransaction(isStart = true)
    @Transactional
    public int save() {
        int rs1 = testMapper.save("mybatis-hello-1", new Date());
        logger.info("本地服務資料插入成功");
        int rs2 = demo2Feign.save();
        logger.info("遠端服務2號資料插入成功");
        int rs3 = demo3Feign.save();
        logger.info("遠端服務3號資料插入成功");
        logger.info("插入" + (rs1 + rs2 + rs3) + "條記錄");
        logger.info("製造異常");
        int v = 100 / 0;
        return rs1 + rs2 + rs3;
    }
}

如上程式碼執行完成以後所有參與此分散式事務模組都將回滾事務。

5)分散式事務被呼叫方
需要實現TxManagerTxUrlService個介面,並作為bean注入到spring中。處理對伺服器的連線

import com.codingapi.tx.config.service.TxManagerTxUrlService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * create by lorne on 2018/11/18
 */
@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService{

    @Value("${tm.manager.url}") //分散式事務協調器TXmanager訪問地址
    private String url;

    @Override
    public String getTxUrl() {
        System.out.println("load tm.manager.url ");
        return url;
    }
}

加入分散式事務註解@TxTransaction,或者實現ITxTransaction介面,開啟分散式事務。等待起調方發起事務

@Service
public class DemoServiceImpl implements DemoService, ITxTransaction {

    @Autowired
    private TestMapper testMapper;

    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public List<Test> list() {
        return testMapper.findAll();
    }

    @Override
    @Transactional
    public int save() {
        int rs = testMapper.save("mybatis-hello-2", new Date());
        logger.info("插入" + rs + "條記錄");
        return rs;
    }
}

6)說明:在使用LCN分散式事務時,只需要將事務的開始方法新增@TxTransaction(isStart=true)註解即可,在參與方新增@TxTransaction或者實現ITxTransaction介面即可。詳細檢視官網說明
7)demo
https://pan.baidu.com/s/1IaTilRxmFQBNj9H0W6BFzg
提取碼:979b