分布式事务系列--SpringCloud整合byteTCC框架0.5.x版本2
发布日期:2021-06-30 11:07:58 浏览次数:2 分类:技术文章

本文共 7130 字,大约阅读时间需要 23 分钟。

欢迎关注公众号:java4all

6.tcc业务逻辑编写

在使用tcc框架处理分布式事务时,需要我们自己来编写tcc业务代码。这里演示一个简单的加钱的操作。

一个tcc操作,分为try,confirm,cancel三个操作。

根据创建的company表,可以看到公司表有一个money金额字段,还有个frozen字段,在业务简单时,我们可以借助这个字段来实现tcc;如果业务复杂,修改多个字段时,我们可以不要这个字段,tcc的每一步,都直接操作目标字段。

6.1mappper.xml编写

业务的目标操作是:给money加钱。

这里分为三条sql,分别是:

1.try操作

这里先添加在frozen字段,暂不操作money字段。(如果业务对操作资源需要做判断,比如是不是够减,那就需要操作money字段)

2.confirm操作

如果try操作成功了,且各链路服务都没有问题,框架认为走confirm分支时,那我们就认为网络和服务等是没有问题的,本地事务就执行这个confirm操作,操作money字段,同时,把fronzen字段清零。

3.cancel操作

如果链路上有环节出现异常,那么就会进入cancel分支,执行cancel逻辑。

UPDATE company SET frozen = frozen + #{money} WHERE id = #{id}
UPDATE company SET money = money + #{money},frozen = frozen - #{money} WHERE id = #{id}
UPDATE company SET frozen = frozen - #{money} WHERE id = #{id}

6.2dao编写

package com.java4all.dao;import java.math.BigDecimal;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;/** * description: * * @author wangzhongxiang * @date 2019/2/14 14:39 */@Repositorypublic interface CompanyDao {  int increaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money);  int confirmIncreaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money);  int cancelIncreaseMoney(@Param("id") Integer id,@Param("money") BigDecimal money);}

6.3service编写

接口里就一个方法,加钱的方法。

package com.java4all.service;import java.math.BigDecimal;/** * description: * * @author wangzhongxiang * @date 2019/2/14 14:38 */public interface CompanyService {  int increaseMoney(Integer id, BigDecimal money);}

6.4serviceImpl编写

接口的加钱方法,对应过来是有3个实现方法的,confirm和cancel的逻辑分别写在对应的实现类中,try逻辑,我们可以直接写在controller中,也可以单独的写在一个实现类中,我这里写在实现类中,这样会更加清晰,实际开发中也推荐这样写,功能非常清晰,controller不应该承载业务逻辑,业务都在service接口的实现类中。

1.CompanyServiceImpl

CompanyServiceImpl 中对应的是try逻辑。

package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc try逻辑 * 1.@Service("companyServiceImpl")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceImpl")public class CompanyServiceImpl implements CompanyService{  @Autowired  private CompanyDao companyDao;  @Override  @Transactional  public int increaseMoney(Integer id, BigDecimal money) {    int line = companyDao.increaseMoney(id, money);    log.info("【try】 increaseMoney: id = "+id+",money ="+money);    return line;  }}
2.CompanyServiceConfirm

CompanyServiceConfirm中对应的是confirm逻辑。

package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc confirm逻辑 * 1.@Service("companyServiceConfirm")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceConfirm")public class CompanyServiceConfirm implements CompanyService{  @Autowired  private CompanyDao companyDao;  @Override  @Transactional  public int increaseMoney(Integer id, BigDecimal money) {    int line = companyDao.confirmIncreaseMoney(id, money);    log.info("【confirm】 increaseMoney: id = "+id+",money ="+money);    return line;  }}
CompanyServiceCancel

CompanyServiceCancel中是cancel的逻辑。

package com.java4all.service.impl;import com.java4all.dao.CompanyDao;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;/** * description: * tcc cancel逻辑 * 1.@Service("companyServiceCancel")这里必须指定此bean名称,tcc过程依靠此名称区分执行tcc中哪个逻辑 * 2.参与tcc的方法必须添加@Transactional注解 * 3.建议tcc每个步骤,方法执行后添加日志,方便问题排查 * * @author wangzhongxiang * @date 2019/2/14 15:06 */@Slf4j@Service("companyServiceCancel")public class CompanyServiceCancel implements CompanyService{  @Autowired  private CompanyDao companyDao;  @Override  @Transactional  public int increaseMoney(Integer id, BigDecimal money){    int line = companyDao.cancelIncreaseMoney(id, money);    log.info("【cancel】 increaseMoney: id = "+id+",money ="+money);    return line;  }}

6.5controller编写

controller中有一个需要注意的点,就是tcc的核心实现@Compensable注解。@Compensable注解有3三参数,这里引用下作者提供的文档中对此的说明:

通过@Compensable注解定义的service为可补偿型service。@Compensable注解需要定义三个参数:

1)interfaceClass,必需。该值用于指定confirm/cancel针对的业务接口,该接口同时被用于校验confirm/cancel实现类。confirm/cancel实现类如果没有实现该业务接口则会被认为无效;

2)confirmableKey,可选。该值用于指定confirm实现类在容器中的beanId,若没有confirm逻辑则不必指定;

3)cancellableKey,可选。该值用于指定cancel实现类在容器中的beanId,若没有cancel逻辑则不必指定;注意:若try阶段执行了写操作则必须有相应的取消逻辑;

4)服务提供方Controler必须添加@Compensable注解;

5)针对一个特定的可补偿型服务接口,业务系统提供的Try、Confirm、Cancel三个实现类,其Try实现类必须定义@Compensable注解,而Confirm、Cancel实现类则不能定义Compensable注解;

文档地址:

package com.java4all.controller;import com.java4all.service.CompanyService;import java.math.BigDecimal;import lombok.extern.slf4j.Slf4j;import org.bytesoft.compensable.Compensable;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.transaction.annotation.Transactional;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author wangzhongxiang * @date 2019/2/14 14:38 */@Compensable(    interfaceClass = CompanyService.class,    confirmableKey = "companyServiceConfirm",    cancellableKey = "companyServiceCancel")@Slf4j@RestController@RequestMapping("company")public class CompanyController {  /**   * 在调用此接口时,首先执行的是tcc中的try,   * 因此,我们需要明确指定引入的究竟是此接口的哪一个实现类,   * 首先执行try,try逻辑写在companyServiceImpl类中,我们就需要明确指定,   * @Autowired private CompanyService companyService   * 上述这种写法是不可以的,无法识别究竟是哪一个实现类,因此实现类,需要明确指定bean名称   * @Compensable注解扫描时,在哪一种状态执行哪个实现类方法,是   * */  @Autowired  private CompanyService companyServiceImpl;  @PostMapping("increaseMoney")  @Transactional  public void increaseMoney(Integer id,BigDecimal money){    int line = companyServiceImpl.increaseMoney(id, money);    log.info("修改行数为:"+line);  }}
  • description:
  • try逻辑有2种写法:
  • 1.直接写在controller中
  • 在controller中直接引入dao层,调用dao层方法,
  • 此时,@Compensable添加在controller中;
  • 2.写在接口的实现类中
  • 此时,在controller中引入实现类时,需要明确指定bean名称。
  • @Compensable注解,应该仅仅添加在controller中
  • 如果仅仅添加在try逻辑的实现类上,那么仅仅会执行try逻辑,cc逻辑不会执行;
  • 如果try逻辑的实现类和controller都添加,那么cc逻辑会执行两遍。
  • 在官方文档中,明确指出了:必须在try的实现类添加@Compensable,而controller建议添加。
  • 此说法经过验证有问题。可能是之前版本遗留问题,但是文档没有及时更新。

框架会根据每一步操作的结果来控制事务走向,选择是confirm还是cancel逻辑。

转载地址:https://it4all.blog.csdn.net/article/details/87878458 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:kafka安装教程
下一篇:分布式事务系列--SpringCloud整合byteTCC框架0.4.x版本

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年05月03日 19时13分17秒