原理介绍
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 "取消支付成功";
}
}