springboot搭配TXmanager實現分散式事務
前言:寫這個文章主要是給自己做下筆記以免遺忘,同時也給各大碼農分享一下,互相學習。
分散式事務的需求來源於系統的服務化。在微服務系統中,無法使用傳統的事務達到資料庫的一致性。每個子服務都有自己獨立的資料來源,如果系統初期,沒有分表分庫,每個子服務的資料來源都是連線同一個資料庫主機,同一個資料庫,那麼還可以簡單的利用傳統的事務,增加程式碼的冗餘達到事務效果。而往往如果已經需要考慮事務的時候,我們的系統應該已經達到了一定的使用者量,開始了分表分庫。故而分散式事務還是需要了解和使用的。
分散式事務有很多種實現方式,我在這主要談論的是我熟悉的 “分三階段提交模式”,這種模式的原理比較簡單,當然了,效能上是有缺陷的。其他實現方式,可自行Google或者百度一下。
框架的特點
- 支援各種基於spring的db框架
- 相容SpringCloud、Dubbo、motan
- 使用簡單,低依賴,程式碼完全開源
- 基於切面的強一致性事務框架
- 高可用,模組可以依賴RPC模組做叢集化,TxManager也可以做叢集化
- 支援本地事務和分散式事務共存
- 支援事務補償機制,增加事務補償決策提醒
- 新增外掛拓展機制
原始碼目錄說明
- transaction-dubbo LCN dubbo rpc框架擴充套件支援
- transaction-springcloud LCN springcloud rpc框架擴充套件支援
- tx-client 是LCN核心tx模組端控制框架
- tx-manager 是LCN 分散式事務協調器
- tx-plugins-db 是LCN 對關係型資料庫的外掛支援
- tx-plugins-nodb 是LCN 對於無資料庫模組的外掛支援
不說太多了,直接上乾貨。(本文主要針對springcloud的)
1、去github.com下載tx-lcn-master原始碼
tx-lcn-master
目前更新到了4.1.0版本
LCN分散式事務框架的核心功能是對本地事務的協調控制,框架本身並不建立事務,只是對本地事務做協調控制。因此該框架與其他第三方的框架相容性強,支援所有的關係型資料庫事務,支援多資料來源,支援與第三方資料庫框架一塊使用(例如 sharding-jdbc),在使用框架的時候只需要新增分散式事務的註解即可,對業務的侵入性低。LCN框架主要是為微服務框架提供分散式事務的支援,在微服務框架上做了進一步的事務機制優化,在一些負載場景上LCN事務機制要比本地事務機制的效能更好,4.0以後框架開方了外掛機制可以讓更多的第三方框架支援進來。
更多詳情請參考官網
我處理過的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