TCC事务模型

原理介绍

TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。

模式特点

  • 该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
  • 该模式对有无本地事务控制都可以支持使用面广。
  • 数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

使用场景

没有带事务的中间件,如:redis

项目实战

pom.xml和application.yml文与LCN的一致

模拟Tcc组合redis

项目组合:

  • lcn-tm:事务管理者
  • lcn-order:事务发起者
  • lcn-pay:事务参与者
  • cloud-eureka:注册中心

事务发起者和事务参与者都是TCC

发起者代码

package com.dandan.lcnorder.controller;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.codingapi.txlcn.tc.annotation.TccTransaction;
import com.dandan.lcnorder.dao.TblOrderDao;
import com.dandan.lcnorder.entity.TblOrder;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * 模拟Tcc组合redis
 */
@RestController
public class OrderTccRedisController {

    @Autowired
    private TblOrderDao tblOrderDao;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @PostMapping("/add-order-tcc-redis")
    @Transactional(rollbackFor = Exception.class)
    @TccTransaction
    public String add(@RequestBody TblOrder bean){

        JSONObject date = new JSONObject();
        date.put("payName",bean.getOrderName()+"pay");
        restTemplate.postForEntity("http://lcn-pay/add-pay-tcc-redis",date,String.class);

        TblOrder tblOrder = new TblOrder();
        tblOrder.setId(1);
        tblOrder.setOrderName("新");

        BoundValueOperations<String, String> order = redisTemplate.boundValueOps("order");
        order.set("order-value");

        tblOrderDao.updateByPrimaryKey(tblOrder);
        int i = 1/0;
        return "新增订单成功";
    }

    public String confirmAdd(TblOrder bean){
        System.out.println("add 确认线程名:"+Thread.currentThread().getName());
        System.out.println("order confirm ");
        return "confirm 订单成功";
    }

////    private static Map<String,Integer> maps = new HashMap<>();
//
//    private ThreadLocal<Integer> ids = new ThreadLocal<>();

    public String cancelAdd(TblOrder bean){
        System.out.println("add 取消线程名:"+Thread.currentThread().getName());
        TblOrder tblOrder = new TblOrder();
        tblOrder.setId(1);
        tblOrder.setOrderName("旧");

        tblOrderDao.updateByPrimaryKey(tblOrder);

        redisTemplate.delete("order");
        System.out.println("order cancel ");
        return "cancel 订单成功";
    }
}

参与者代码

package com.dandan.lcnpay.controller;

import com.codingapi.txlcn.tc.annotation.TccTransaction;
import com.dandan.lcnpay.dao.TblPayDao;
import com.dandan.lcnpay.entity.TblPay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

/**
 * 模拟Tcc组合mysql,不建议用,代码复杂度太高,使用LCN更简单
 */
@RestController
public class PayTccController {


    @Autowired
    private TblPayDao tblPayDao;

    @PostMapping("/add-pay-tcc")
    @Transactional(rollbackFor = Exception.class)
    @TccTransaction
    public String addPay(@RequestBody TblPay bean){
        tblPayDao.insert(bean);
        Integer id = bean.getId();
        maps.put("a",id);
//        int i = 1/0;
        return "新增支付成功";

    }
    public String confirmAddPay(TblPay bean){
        System.out.println("pay confirm");
        return "新增支付成功";

    }
    private static Map<String,Integer> maps = new HashMap<>();

    /**
     * 逆sql
     * @param bean
     * @return
     */
    public String cancelAddPay(TblPay bean){
        Integer a = maps.get("a");
        System.out.println("a:"+a);
        System.out.println("pay cancel");
        tblPayDao.deleteByPrimaryKey(a);
        return "取消支付成功";

    }
}

事务发起者是LCN,参与者是TCC

发起者代码

package com.dandan.lcnorder.controller;

import com.codingapi.txlcn.tc.annotation.LcnTransaction;
import com.dandan.lcnorder.dao.TblOrderDao;
import com.dandan.lcnorder.entity.TblOrder;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * 模拟Tcc组合redis,LCN组合sql
 * client1使用LCN
 * client2使用TCC
 */
@RestController
public class OrderTccRedisController2 {

    @Autowired
    private TblOrderDao tblOrderDao;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @PostMapping("/add-order-tcc-redis2")
    @Transactional(rollbackFor = Exception.class)
    @LcnTransaction
    public String add(@RequestBody TblOrder bean){

        JSONObject date = new JSONObject();
        date.put("payName",bean.getOrderName()+"pay");
        restTemplate.postForEntity("http://lcn-pay/add-pay-tcc-redis",date,String.class);

        TblOrder tblOrder = new TblOrder();
        tblOrder.setId(1);
        tblOrder.setOrderName("新");

        tblOrderDao.updateByPrimaryKey(tblOrder);
        int i = 1/0;
        return "新增订单成功";
    }
}

参与者代码

package com.dandan.lcnpay.controller;

import com.dandan.lcnpay.dao.TblPayDao;
import com.dandan.lcnpay.entity.TblPay;
import com.dandan.lcnpay.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PayTccRedisController {


    @Autowired
    private TblPayDao tblPayDao;

    @Autowired
    private RedisService redisService;

    @PostMapping("/add-pay-tcc-redis")
    @Transactional(rollbackFor = Exception.class)
    public String addPay(@RequestBody TblPay bean){
        redisService.addPay(null);
//        int i = 1/0;
        return "新增支付成功";
    }
}

RedisService代码

package com.dandan.lcnpay.service;

import com.codingapi.txlcn.tc.annotation.TccTransaction;
import com.dandan.lcnpay.entity.TblPay;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.HashMap;

@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @TccTransaction
    public String addPay(@RequestBody TblPay bean){
        BoundValueOperations<String, String> pay = redisTemplate.boundValueOps("pay");
        pay.set("pay-value");
//        int i = 1/0;
        return "新增支付成功";
    }
    public String confirmAddPay(TblPay bean){
        System.out.println("pay confirm");
        return "confirm 支付成功";

    }
    private static java.util.Map<String,Integer> maps = new HashMap<>();

    /**
     * 逆sql
     * @param bean
     * @return
     */
    public String cancelAddPay(TblPay bean){
        redisTemplate.delete("pay");
        System.out.println("pay cancel");
        return "取消支付成功";
    }
}
上一篇:2021-11-07


下一篇:java的SPI机制与简单实例