分布式事务

目录

分布式事务产生的背景

分布式事务常见解决方案

解决分布式事务基本思路

ACID酸碱平衡理论

CAP(帽子 原理)

Base(碱)

柔性事务和刚性事务

分布式事务常见解决方案

两段提交协议

三段提交协议

2PC和3PC提交区别

传统项目 Jta+Atomikos

分布式事务实战

表结构

基于LCN框架解决分布式事务

LCN框架底层实现原理

SpringCloud2.0整合LCN

SpringCloud2.0客户端集成

Maven依赖信息 order项目加入

application.yml引入 订单项目和库存项目

LCN基本配置代码

分布式事务案例

源码分析参与方超时时间控制

LCN协调者服务集群

Nginx负载均衡配置

Yml配置文件连接


Java分布式/微服务/高并发/高可用/性能优化/消息中间件/JVM/缓存/并发编程培训,涵盖目前互联网高薪必备技能

 

分布式事务产生的背景

分布式事务常见解决方案

>>>1 传统模式使用Jta+Atomikos

>>>2 2PC与3PC实现的区别

>>>3 使用阿里巴巴TCC补偿框架

>>>4 使用可靠消息模式

>>>5 使用LCN框架解决分布式事务(重点)

>>>6 阿里GTS框架解决分布式事务

在微服务环境下,因为会根据不同的业务会拆分成不同的服务,比如会员服务、订单服务、商品服务等,让专业的人做专业的事情,每个服务都有自己独立的数据库,并且是独立运行,互不影响。

服务与服务之间通讯采用RPC远程调用技术,但是每个服务中都有自己独立的数据源,即自己独立的本地事务。两个服务相互通讯的时候,两个本地事务互不影响,从而出现分布式事务产生的原因。

1传统项目大部分情况下,不会产生分布式事务,但是在项目中如果采用多数据源方式。

2微服务因为会根据不同的业务会拆分成不同的服务

 

在电商系统中,下单和扣库存如何保持一致?

比如:用户先下单后,扣库存失败,那么将会导致超卖;如果下单不成功,扣库存成功,那么会导致少卖。这两种情况都会导致运营成本增加,在严重情况下需要赔付。

订单服务和库存服务

画图演示场景

 

下图不会产生分布式事务

解决分布式事务基本思路

ACID酸碱平衡理论

 如何保证强一致性呢?计算机专业的童鞋在学习关系型数据库的时候都学习了ACID原理,这里对ACID做个简单的介绍。如果想全面的学习ACID原理,请参考ACID
  关系型数据库天生就是解决具有复杂事务场景的问题,关系型数据库完全满足ACID的特性。

数据库管理系统中事务(transaction)的四个特性(分析时根据首字母缩写依次解释):

原子性(Atomicity)

一致性(Consistency)

隔离性(Isolation)

持久性(Durability)

所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。(执行单个逻辑功能的一组指令或操作称为事务)

 

在学习解决分布式事务基本思路之前,大家要熟悉一些基本解决分布式事务概念名词

比如:CAPBase理论、柔性事务与刚性事务、理解最终一致性思想,JTA+XA、两阶段与三阶段提交等。

这些名词在后期学习一些第三方分布式事务解决框架中用到,比如国产的LCN、阿里的GTS框架等。

CAP(帽子 原理)


由于对系统或者数据进行了拆分,我们的系统不再是单机系统,而是分布式系统,针对分布式系统的CAP原理包含如下三个元素。


C:Consistency,一致性。在分布式系统中的所有数据 备份,在同一时刻具有同样的值,所有节点在同一时刻读取的数据都是最新的数据副本。
A:Availability,可用性,好的响应性能。完全的可用性指的是在任何故障模型下,服务都会在有限的时间内处理完成并进行响应。  

P: Partition tolerance,分区容忍性。尽管网络上有部分消息丢失,但系统仍然可继续工作。  

一般都支持AP

CAP原理证明,任何分布式系统只可同时满足以上两点,无法三者兼顾。由于关系型数据库是单节点无复制的,因此不具有分区容忍性,但是具有一致性和可用性,而分布式的服务化系统都需要满足分区容忍性,那么我们必须在一致性和可用性之间进行权衡。如果在网络上有消息丢失,也就是出现了网络分区,则复制操作可能会被延后,如果这时我们的使用方等待复制完成再返回,则可能导致在有限时间内无法返回,就失去了可用性:而如果使用方不等待复制完成,而在主分片写完后直接返回,则具有了可用性,但是失去了一致性。

Base(碱)

BASE理论是指,Basically Available(基本可用)、Soft-state( 软状态/柔性事务)、Eventual Consistency(最终一致性)。是基于CAP定理演化而来,是对CAP中一致性和可用性权衡的结果。核心思想:即使无法做到强一致性,但每个业务根据自身的特点,采用适当的方式来使系统达到最终一致性。

1、基本可用:指分布式系统在出现故障的时候,允许损失部分可用性,保证核心可用。但不等价于不可用。比如:搜索引擎0.5秒返回查询结果,但由于故障,2秒响应查询结果;网页访问过大时,部分用户提供降级服务,等。

2、软状态:软状态是指允许系统存在中间状态,并且该中间状态不会影响系统整体可用性。即允许系统在不同节点间副本同步的时候存在延时。

3、最终一致性:

系统中的所有数据副本经过一定时间后,最终能够达到一致的状态,不需要实时保证系统数据的强一致性。最终一致性是弱一致性的一种特殊情况。BASE理论面向的是大型高可用可扩展的分布式系统,通过牺牲强一致性来获得可用性。ACID是传统数据库常用的概念设计,追求强一致性模型。

外部接口如下做

柔性事务和刚性事务

柔性事务满足BASE理论(基本可用,最终一致)

刚性事务满足ACID理论

本文主要围绕分布式事务当中的柔性事务的处理方式进行讨论。

柔性事务分为

1. 两阶段型

2. 补偿型

3. 异步确保型

4. 最大努力通知型几种。 由于支付宝整个架构是SOA架构,因此传统单机环境下数据库的ACID事务满足了分布式环境下的业务需要,以上几种事务类似就是针对分布式环境下业务需要设定的。

分布式事务常见解决方案

两段提交协议

第一阶段:

准备阶段:协调者向参与者发起指令,参与者评估自己的状态,如果参与者评估指令可以完成,则会写redo或者undo日志,让后锁定资源,执行操作,但并不提交。

第二阶段:如果每个参与者明确返回准备成功,则协调者向参与者发送提交指令,参与者释放锁定的资源,如何任何一个参与者明确返回准备失败,则协调者会发送中指指令,参与者取消已经变更的事务,释放锁定的资源。

 

两阶段提交方案应用非常广泛,几乎所有商业OLTP数据库都支持XA协议。但是两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

缺点:如果协调者宕机,参与者没有协调者指挥,则会一直阻塞。

三段提交协议

三阶段提交协议是两阶段提交协议的改进版本。它通过超时机制解决了阻塞的问题,并且把两个阶段增加为三个阶段

询问阶段:协调者询问参与者是否可以完成指令,协调者只需要回答是还是不是,而不需要做真正的操作,这个阶段超时导致中止

准备阶段:如果在询问阶段所有的参与者都返回可以执行操作,协调者向参与者发送预执行请求,然后参与者写redo和undo日志,执行操作,但是不提交操作;如果在询问阶段任何参与者返回不能执行操作的结果,则协调者向参与者发送中止请求,这里的逻辑与两阶段提交协议的的准备阶段是相似的,这个阶段超时导致成功

提交阶段:如果每个参与者在准备阶段返回准备成功,也就是预留资源和执行操作成功,协调者向参与者发起提交指令,参与者提交资源变更的事务,释放锁定的资源;如果任何一个参与者返回准备失败,也就是预留资源或者执行操作失败,协调者向参与者发起中止指令,参与者取消已经变更的事务,执行undo日志,释放锁定的资源,这里的逻辑与两阶段提交协议的提交阶段一致

2PC和3PC提交区别

增加了一个询问阶段,询问阶段可以确保尽可能早的发现无法执行操作而需要中止的行为,但是它并不能发现所有的这种行为,只会减少这种情况的发生在准备阶段以后,协调者和参与者执行的任务中都增加了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据概率统计上超时后默认成功的正确性最大

 

三阶段提交协议与两阶段提交协议相比,具有如上的优点,但是一旦发生超时,系统仍然会发生不一致,只不过这种情况很少见罢了,好处就是至少不会阻塞和永远锁定资源。

传统项目 Jta+Atomikos

传统项目中,比如项目中使用到多数据源的时候大多数采用jta+Atomikos解决分布式事务问题,jta+Atomikos底层是基于XA协议的两阶段提交方案。

XA协议:XA 事务的基础是两阶段提交协议。需要有一个事务协调者来保证所有的事务参与者都完成了准备工作(第一阶段)。如果协调者收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。Mysql 在这个XA事务中扮演的是参与者的角色,而不是协调者(事务管理器)。

 

JTA:JTA(java Transaction API)是JavaEE 13 个开发规范之一。java 事务API,允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。事务最简单最直接的目的就是保证数据的有效性,数据的一致性

 

Atomikos:Atomikos TransactionsEssentials 是一个为Java平台提供增值服务的并且开源类事务管理器

演示jta+atomikos项目

分布式事务解决方案采用Atomikos 后台管理系统

下图四位

 

分布式事务实战

表结构

基于LCN框架解决分布式事务

LCN官网 https://www.txlcn.org/

"LCN并不生产事务,LCN只是本地事务的搬运工"

兼容 dubbospringcloudmotan 框架,支持各种关系型数据库

LCN先启动TxManager

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版本。

使用LCN官方提供的@TxTransaction注解解决分布式事务难题,isStart参数是否LCN事务发起方  true 是:是发起方 false 否:是参与方

下载4.0版本

 

 

SpringCloud2.0客户端集成

事务分组ID存与redis

Maven依赖信息 order项目加入

官方是4.1.1 下面引入有人该的4.1.2  为了支持spring boot2.0


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

以下两个复制到订单项目和库存项目

package com.antmeite.api.order.impl;

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

/**
 * create by lorne on 2017/11/18
 */
@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;
	}
}

 

package com.antmeite.api.order.impl;

import com.codingapi.tx.netty.service.TxManagerHttpRequestService;
import com.lorne.core.framework.utils.http.HttpUtils;
import org.springframework.stereotype.Service;

/**
 * create by lorne on 2017/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;
	}
}

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("蚂蚁课堂永久会员充值");
		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行获取服务器集群地址

1.首先通过nginx配置多个tm协调者负载均衡配置,让后 LCN客户端启动项目的时候访问nginx负载均衡地址获取lcn协议通讯IP地址和端口号,并且对该连接保持长连接

 2.因为LCN客户端与TM协调者保持的是长连接,当tm协调者宕机之后,LCN会客户端会立即重新进入到获取负载均衡地址lcn协议通讯IP地址和端口号。

官方文档: 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;//底层lcn地址
        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;
        }

    }


 

Nginx负载均衡配置

 

#### 上游服务器 集群 默认轮训机制
    upstream  backServer{
	    server 127.0.0.1:8899;
	    server 127.0.0.1:8898;
	}
    server {
        listen       80;
        server_name  lcn.itmayiedu.com;
        location / {
            ### 指定上游服务器负载均衡服务器
		    proxy_pass http://backServer/;
			###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
			proxy_connect_timeout 5s; ###1s 时间太短,故障转移不行,断开再访问其他会超过1s
			###nginx发送给上游服务器(真实访问的服务器)超时时间
            proxy_send_timeout 5s; ###1s
			### nginx接受上游服务器(真实访问的服务器)超时时间
            proxy_read_timeout 5s; ###1s
            index  index.html index.htm;
		    

        }

        

Yml配置文件连接

tm: 
  manager: 
#     url: http://127.0.0.1:8899/tx/manager/
      url: http://lcn.itmayiedu.com/tx/manager/

 

 

路径访问

看源码 

下面路径正确 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值