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 "取消支付成功";
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章