前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式事务系列--SpringCloud整合byteTCC框架0.5.x版本2

分布式事务系列--SpringCloud整合byteTCC框架0.5.x版本2

作者头像
IT云清
发布2019-03-06 16:29:47
1.1K0
发布2019-03-06 16:29:47
举报
文章被收录于专栏:IT云清IT云清IT云清

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逻辑。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.java4all.dao.CompanyDao">
  <resultMap id="resMap" type="com.java4all.entity.Company">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="id" jdbcType="INTEGER" property="id" />
    <result column="money" jdbcType="DECIMAL" property="money" />
    <result column="frozen" jdbcType="DECIMAL" property="frozen" />
  </resultMap>

  <!--try逻辑-->
  <update id="increaseMoney">
    UPDATE company SET frozen = frozen + #{money}
    WHERE id = #{id}
  </update>

  <!--confirm逻辑-->
  <update id="confirmIncreaseMoney">
    UPDATE company SET money = money + #{money},frozen = frozen - #{money}
    WHERE id = #{id}
  </update>

  <!--cancel逻辑-->
  <update id="cancelIncreaseMoney">
    UPDATE company SET frozen = frozen - #{money}
    WHERE id = #{id}
  </update>
</mapper>
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
 */
@Repository
public 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注解;

文档地址:https://github.com/liuyangming/ByteTCC/wiki/User-Guide-0.5.x

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逻辑。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年02月22日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 6.tcc业务逻辑编写
    • 6.1mappper.xml编写
      • 1.try操作
      • 2.confirm操作
      • 3.cancel操作
    • 6.2dao编写
      • 6.3service编写
        • 6.4serviceImpl编写
          • 1.CompanyServiceImpl
          • 2.CompanyServiceConfirm
          • CompanyServiceCancel
        • 6.5controller编写
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档