欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

分布式事务解决方案

程序员文章站 2022-03-15 18:16:14
...

分布式一致性协议

  • XA接口
    XA规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。事务管理器控制着全局事务,管理事务生命周期,并协调资源。资源管理器负责控制和管理实际资源(如数据库或JMS队列)
  • JTA规范
    作为java平台上事务规范JTA(Java Transaction API)也定义了对XA事务的支持. 目前JTA的实现主要由以下几种:
  1. J2EE容器所提供的JTA实现, 如JBoss
  2. 独立的JTA实现, 如JOTM,Atomikos.

二阶段提交协议

  • 含义
  1. 表决阶段,所有参与者都将本事务能否成功的信息反馈发给协调者;
  2. 执行阶段,协调者根据所有参与者的反馈,通知所有参与者,步调一致地在所有分支上提交或者回滚。
    分布式事务解决方案
  • 优点
    尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于 MySQL 是从 5.5 开始支持。
  • 缺点
  1. 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机, 资源管理器就会一直阻塞,导致数据库无法使用。
  2. 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
  3. 数据不一致:比如在第二阶段中,假设协调者发出了事务 Commit 的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了 Commit 操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
  • 总结
    二阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题。

三阶段提交协议

  1. TCC
  • 含义
    TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。
  • 特点
    该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
    该模式对有无本地事务控制都可以支持使用面广。
    数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
  1. 异步回调模式
  2. 最终一致性模式
  3. 可靠消息模式

代码实现(基于LCN框架4.1.2)

  • 架构图
    分布式事务解决方案
  • 步骤
  1. 由于lcn暂时不支持SpringCloud2.0,因此我们需要把某位大牛改的lcn安装到本地库,打包时将maven-javadoc-plugin插件注掉, 调整redis配置,不然会报错。
  2. 在订单服务中集成lcn。
// 引入依赖
<!-- https://mvnrepository.com/artifact/com.codingapi/transaction-springcloud -->
		<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>

		<!-- https://mvnrepository.com/artifact/com.codingapi/tx-plugins-db -->
		<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>
// 配置lcn
tm: 
  manager: 
     url: http://lcn.zxkj.com/tx/manager/
// 添加两个service
@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;
    }
}

@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;
	}
}
// 测试用例
@RestController
public class IOrderServiceImpl extends BaseApiService implements IOrderService {
	
	@Autowired
	private OrderMapper orderMapper;
	
	@Autowired
	private StockFeign stockFeign;

	@TxTransaction(isStart = true)
	@Transactional
	@GetMapping(value = "/addOrderAndStock")
	public ResponseBase addOrderAndStock(int i) throws Exception {
		OrderEntity orderEntity = new OrderEntity();
		orderEntity.setName("订单1");
		orderEntity.setOrderCreatetime(new Date());
		orderEntity.setOrderMoney(300d);
		orderEntity.setOrderState(0);
		Long commodityId = 30l;
		orderEntity.setCommodityId(30l);
		int orderResult = orderMapper.addOrder(orderEntity);
		System.out.println("orderResult:" + orderResult);
		if (orderResult <= 0) {
			return setResultError("下单失败!");
		}
		
		ResponseBase inventoryReduction = stockFeign.inventoryReduction(commodityId);
		if (inventoryReduction.getRtnCode() != 200) {
			throw new Exception("调用库存服务接口失败,开始回退订单事务代码");
		}
		int reuslt = 1 / i;
		System.out.println("reuslt:" + reuslt);
		return setResultSuccess("下单成功!");
	}

}
  1. 在库存服务中集成lcn, 同上。
  2. 配置nginx
    upstream  backServer{
	    server 127.0.0.1:8899;
	    server 127.0.0.1:8898;
	}
     server {
        listen       80;
        server_name  lcn.zxkj.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;
        }
	}
  1. 初始化测试脚本
CREATE TABLE `stock`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `commodity_id` int(11) DEFAULT NULL COMMENT '商品ID',
  `stock` int(11) DEFAULT NULL COMMENT '库存余额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

CREATE TABLE `order`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '订单名称',
  `order_createtime` datetime(0) DEFAULT NULL COMMENT '下单时间',
  `order_state` int(11) DEFAULT NULL COMMENT '订单状态 0 已经未支付 1已经支付 2已退单',
  `order_money` double(10, 0) DEFAULT NULL COMMENT '订单价格',
  `commodity_id` int(10) DEFAULT NULL COMMENT '商品ID',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 17 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  1. 依次启动EurekaServer、两个TxManager、库存服务、订单服务.
  • 测试结果
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
    分布式事务解决方案
相关标签: 分布式