分佈式事務

分佈式事務產生的背景

在微服務環境下,因爲會根據不同的業務會拆分成不同的服務,比如會員服務、訂單服務、商品服務等,讓專業的人做專業的事情,每個服務都有自己獨立的數據庫,並且是獨立運行,互不影響。

服務與服務之間通訊採用RPC遠程調用技術,但是每個服務中都有自己獨立的數據源,即自己獨立的本地事務。兩個服務相互通訊的時候,兩個本地事務互不影響,從而出現分佈式事務產生的原因。

解決分佈式事務基本思路

ACID酸鹼平衡理論

如何保證強一致性呢?計算機專業的童鞋在學習關係型數據庫的時候都學習了ACID原理,這裏對ACID做個簡單的介紹。如果想全面的學習ACID原理,請參考ACID
  關係型數據庫天生就是解決具有複雜事務場景的問題,關係型數據庫完全滿足ACID的特性。

數據庫管理系統中事務(transaction)的四個特性(分析時根據首字母縮寫依次解釋):

原子性(Atomicity)
一致性(Consistency)
隔離性(Isolation)
持久性(Durability)

所謂事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。(執行單個邏輯功能的一組指令或操作稱爲事務)

CAP(帽子 原理)

由於對系統或者數據進行了拆分,我們的系統不再是單機系統,而是分佈式系統,針對分佈式系統的CAP原理包含如下三個元素。

C:Consistency,致性。在分佈式系統中的所有數據 備份,在同一時刻具有同樣的值,所有節點在同一時刻讀取的數據都是最新的數據副本。

A:Availability,可用性,好的響應性能。完全的可用性指的是在任何故障模型下,服務都會在有限的時間內處理完成並進行響應。
P: Partition tolerance,分區容忍性。儘管網絡上有部分消息丟失,但系統仍然可繼續工作。

CAP原理證明,任何分佈式系統只可同時滿足以上兩點,無法三者兼顧。由於關係型數據庫是單節點無複製的,因此不具有分區容忍性,但是具有一致性和可用性,而分佈式的服務化系統都需要滿足分區容忍性,那麼我們必須在一致性和可用性之間進行權衡。如果在網絡上有消息丟失,也就是出現了網絡分區,則複製操作可能會被延後,如果這時我們的使用方等待複製完成再返回,則可能導致在有限時間內無法返回,就失去了可用性:而如果使用方不等待複製完成,而在主分片寫完後直接返回,則具有了可用性,但是失去了一致性。

Base(鹼)

BASE 是 Basically Available(基本可用)、Soft state(軟狀態)和 Eventually consistent(最終一致性)三個短語的簡寫,由 eBay 架構師 Dan Pritchett 於 2008 年在《BASE: An Acid Alternative》(論文地址點 這裏)論文中首次提出。BASE 思想與 ACID 原理截然不同,它滿足 CAP 原理,通過犧牲強一致性獲得可用性, 一般應用於服務化系統的應用層或者大數據處理系統中,通過達到最終一致性來儘量滿足業務的絕大多數需求。
BASE 模型包含如下三個元素:
• BA:(Basically Available ),基本可用。
• S:( Soft State),軟狀態,狀態可以在一段時間內不同步。
• E:(Eventually Consistent ),最終一致,在一定的時間窗口內, 最終數據達成一致即可。
關於最終一致的幾種變種參見上面,在實際系統實踐中,可以將若干變種結合起來,來實現各種業務需求。
柔性事務和剛性事務
柔性事務滿足BASE理論(基本可用,最終一致)
剛性事務滿足ACID理論
本文主要圍繞分佈式事務當中的柔性事務的處理方式進行討論。
柔性事務分爲

  1. 兩階段型
  2. 補償型
  3. 異步確保型
  4. 最大努力通知型幾種。 由於支付寶整個架構是SOA架構,因此傳統單機環境下數據庫的ACID事務滿足了分佈式環境下的業務需要,以上幾種事務類似就是針對分佈式環境下業務需要設定的。

分佈式事務常見解決方案
分佈式一致性協議
XA接口
XA是由X/Open組織提出的分佈式事務的規範。XA規範主要定義了(全局)事務管理器(Transaction Manager)和(局部)資源管理器(Resource Manager)之間的接口。XA接口是雙向的系統接口,在事務管理器(Transaction Manager)以及一個或多個資源管理器(Resource Manager)之間形成通信橋樑。XA之所以需要引入事務管理器是因爲,在分佈式系統中,從理論上講(參考Fischer等的論文),兩臺機器理論上無法達到一致的狀態,需要引入一個單點進行協調。事務管理器控制着全局事務,管理事務生命週期,並協調資源。資源管理器負責控制和管理實際資源(如數據庫或JMS隊列)

Jta規範

作爲java平臺上事務規範JTA(Java Transaction API)也定義了對XA事務的支持,實際上,JTA是基於XA架構上建模的,在JTA 中,事務管理器抽象爲javax.transaction.TransactionManager接口,並通過底層事務服務(即JTS)實現。像很多其他的java規範一樣,JTA僅僅定義了接口,具體的實現則是由供應商(如J2EE廠商)負責提供,目前JTA的實現主要由以下幾種:
1.J2EE容器所提供的JTA實現(JBoss)
2.獨立的JTA實現:如JOTM,Atomikos.這些實現可以應用在那些不使用J2EE應用服務器的環境裏用以提供分佈事事務保證。如Tomcat,Jetty以及普通的java應用。

兩段提交協議

交易中間件與數據庫通過 XA 接口規範,使用兩階段提交來完成一個全局事務, XA 規範的基礎是兩階段提交協議。
第一階段是表決階段,所有參與者都將本事務能否成功的信息反饋發給協調者;第二階段是執行階段,協調者根據所有參與者的反饋,通知所有參與者,步調一致地在所有分支上提交或者回滾。

在這裏插入圖片描述

兩階段提交方案應用非常廣泛,幾乎所有商業OLTP數據庫都支持XA協議。但是兩階段提交方案鎖定資源時間長,對性能影響很大,基本不適合解決微服務事務問題。

三段提交協議
TCC
異步回調模式

最終一致性模式
可靠消息模式

基於LCN框架解決分佈式事務

LCN官網 https://www.txlcn.org/
“LCN並不生產事務,LCN只是本地事務的搬運工”
兼容 dubbo、springcloud、motan 框架,支持各種關係型數據庫
LCN框架底層實現原理
詳細參考: https://github.com/codingapi/tx-lcn/wiki/LCN%E5%8E%9F%E7%90%86

SpringCloud2.0整合LCN

目前LCN版本已經升級爲4.0了,但是官方沒有SpringCloud2.0的demo案例。
因爲LCN本身是開源的,網上有大牛對LCN框架源碼做修改,可以支持SpringCloud2.0版本。

SpringCloud2.0客戶端集成

Maven依賴信息

	<dependency>
		<groupId>com.codingapi</groupId>
		<artifactId>transaction-springcloud</artifactId>
		<version>4.1.2</version>
		<exclusions>
			<exclusion>
				<groupId>org.slf4j</groupId>
				<artifactId>*</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>com.codingapi</groupId>
		<artifactId>tx-plugins-db</artifactId>
		<version>4.1.2</version>
		<exclusions>
			<exclusion>
				<groupId>org.slf4j</groupId>
				<artifactId>*</artifactId>
			</exclusion>
		</exclusions>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

application.yml引入

tm: 
  manager: 
     url: http://127.0.0.1:8899/tx/manager/

LCN基本配置代碼

@Service
public class TxManagerTxUrlServiceImpl implements TxManagerTxUrlService {

	@Value("${tm.manager.url}")
	private String url;

	@Override
	public String getTxUrl() {
		System.out.println("load tm.manager.url ");
		return url;
	}
}
@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)
	@Transactional
	@GetMapping(value = "/addOrderAndStock")
	public ResponseBase addOrderAndStock(int i) {
		OrderEntity orderEntity = new OrderEntity();
		orderEntity.setName("==================Test");
		orderEntity.setOrderCreatetime(new Date());
		// 價格是300元
		orderEntity.setOrderMoney(300d);
		// 狀態爲 未支付
		orderEntity.setOrderState(0);
		Long commodityId = 30l;
		// 商品id
		orderEntity.setCommodityId(commodityId);
		// 1.先下單,創建訂單
		int orderResult = orderMapper.addOrder(orderEntity);
		System.out.println("orderResult:" + orderResult);
		// 2.下單成功後,調用庫存服務
		ResponseBase inventoryReduction = stockFeign.inventoryReduction(commodityId);
		// if (inventoryReduction.getRtnCode() != 200) {
		// // 手動回滾事務
		// }
		int reuslt = 1 / i;
		return setResultSuccess("下單成功!");
	}

使用@TxTransaction解決分佈式事務 isStart true 是:是發起方 false 否:是參與方

同理生產者 同樣配置集成即可,只需要把Transaction 中的isStart改爲fasle即可。

LCN協調者服務集羣

官方文檔: https://github.com/codingapi/tx-lcn/wiki/TxManager%E9%9B%86%E7%BE%A4%E8%AF%B4%E6%98%8E
核心原理 通過該實現類
NettyDistributeServiceImpl 38行獲取服務器集羣地址

private void getTxServer() {
        //獲取負載均衡服務地址
        String json = null;
        while (StringUtils.isEmpty(json)) {
            json = txManagerService.httpGetServer();
            logger.info("get txManager ->" + json);
            if (StringUtils.isEmpty(json)) {
                logger.error("TxManager服務器無法訪問.");
                try {
                    Thread.sleep(1000 * 2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        TxServer txServer = TxServer.parser(json);
        if (txServer != null) {
            logger.debug("txServer -> " + txServer);
            logger.info(txServer.toString());
            Constants.txServer = txServer;
            logger.info(Constants.txServer.toString());
            connectCont = 0;
        }

    }

LCN協調者服務集羣原理

1.首先通過nginx配置多個tm協調者負載均衡配置,讓後 LCN客戶端啓動項目的時候訪問nginx負載均衡地址獲取lcn協議通訊IP地址和端口號,並且對該連接保持長連接。
2.因爲LCN客戶端與TM協調者保持的是長連接,當tm協調者宕機之後,LCN會客戶端會立即重新進入到獲取負載均衡地址lcn協議通訊IP地址和端口號。
Nginx負載均衡配置

上游服務器 集羣 默認輪訓機制

upstream  backServer{
    server 127.0.0.1:8899;
    server 127.0.0.1:8898;
}
server {
    listen       80;
    server_name  wg.test.com;
    location / {
        ### 指定上游服務器負載均衡服務器
	    proxy_pass http://backServer/;
		###nginx與上游服務器(真實訪問的服務器)超時時間 後端服務器連接的超時時間_發起握手等候響應超時時間
		proxy_connect_timeout 5s;
		###nginx發送給上游服務器(真實訪問的服務器)超時時間
        proxy_send_timeout 5s;
		### nginx接受上游服務器(真實訪問的服務器)超時時間
        proxy_read_timeout 5s;
        index  index.html index.htm;
	    

    }

Yml配置文件連接

tm: 
  manager: 
#     url: http://127.0.0.1:8899/tx/manager/
      url: http://lcn.test.com/tx/manager/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章