Skip to content

User Guide 0.4.x

liuyangming edited this page Jun 16, 2017 · 12 revisions

一、使用约束

1.1、公共约束

  1. 仅支持使用@Transactional来配置事务,而不能通过xml来配置事务;
  2. JDK版本:7.0及以上版本;

1.2、使用Spring Cloud的约束

  1. 用@Compensable注解的TCC型服务Controller必须实现一个业务接口(业务系统自行指定,但需要与Compensable.interfaceClass一致);
  2. 业务系统不可使用随机端口(本约束与采用的负载均衡分发粒度相关,当使用ByteTCC按请求粒度负载均衡时可忽略该约束);

1.3、使用Dubbo的约束

  1. 必须且仅可指定一个<dubbo:application name="..." />元素,其name不能为空,且必须唯一;
  2. 必须且仅可指定一个<dubbo:protocol port="..." />元素,其port不能为空,也不能为-1;本约束与ByteTCC采用的负载均衡分发粒度相关,当使用ByteTCC按请求粒度负载均衡时可忽略该约束
  3. 定义dubbo服务提供者时(<dubbo:service />):a、filter必须为compensable;b、loadbalance必须为compensable;c 、cluster必须为failfast;d、retries必须为0;e、group必须为org.bytesoft.bytetcc;
  4. 定义dubbo服务消费者时(<dubbo:reference />):a、filter必须为compensable;b、loadbalance必须为compensable;c 、cluster必须为failfast;d、retries必须为0;e、group必须为org.bytesoft.bytetcc;
  5. 在每个参与tcc事务的数据库中创建bytejta表(ddl见bytetcc-supports.jar/bytetcc.sql);
  6. 可补偿型服务的Try/Confirm/Cancel实现类的方法必须定义Transactional注解,且propagation必须是Required, RequiresNew, Mandatory中的一种(即业务代码必须参与事务,从0.3.0开始强制要求);

二、配置ByteTCC

2.1、使用Spring Cloud

2.1.1、引入ByteTCC配置
@ImportResource({ "classpath:bytetcc-supports-springcloud.xml" })
2.1.2、配置扫描的应用包名
@SpringBootApplication(scanBasePackages = "com.yourcompany.service...")
2.1.3、配置数据源
	@Bean(name = "dataSource")
	public DataSource getDataSource() {
		LocalXADataSource dataSource = new LocalXADataSource();
		dataSource.setDataSource(this.invokeGetDataSource());
		return dataSource;
	}

	public DataSource invokeGetDataSource() {
	    // ......
	}

注意:补偿型service中应该使用org.bytesoft.bytejta.supports.jdbc.LocalXADataSource封装过的数据源(如本例中的'dataSource')。

2.2、使用Dubbo

2.2.1、引入ByteTCC配置
<import resource="classpath:bytetcc-supports-dubbo.xml" />
2.2.2、配置扫描的应用包名
<context:component-scan base-package="com.yourcompany.service..." />
2.2.3、配置数据源
<bean id="dontUseThisDataSourceDirectly" class="org.apache.commons.dbcp.BasicDataSource">
    <!-- ...... -->
</bean>

<bean id="dataSource" 
      class="org.bytesoft.bytejta.supports.jdbc.LocalXADataSource">
    <property name="dataSource" ref="dontUseThisDataSourceDirectly" />
</bean>

注意:补偿型service中应该使用org.bytesoft.bytejta.supports.jdbc.LocalXADataSource封装过的数据源(如本例中的'dataSource',而不是'dontUseThisDataSourceDirectly')。

三、可补偿型业务服务开发

TCC型服务可以有两种配置方式:简化配置、一般配置,业务开发者可视情况选择任意一种。

3.1、简化配置

@Service("accountService")
@Compensable(interfaceClass = IAccountService.class, simplified = true)
public class AccountServiceImpl implements IAccountService {

	@Transactional
	public void increaseAmount(String accountId, double amount) throws ServiceException {
	    // TODO ...
	}

	@Transactional
	public void confirmIncreaseAmount(String accountId, double amount) throws ServiceException {
	    // TODO ...
	}

	@Transactional
	public void cancelIncreaseAmount(String accountId, double amount) throws ServiceException {
	    // TODO ...
	}
}

简化配置仅适合一个接口只提供一个方法的服务场景,当一个接口有多个方法时,必须使用一般配置。

3.2、一般配置

3.2.1、可补偿型service定义
@Service("accountService")
@Compensable(
  interfaceClass = IAccountService.class 
, confirmableKey = "accountServiceConfirm"
, cancellableKey = "accountServiceCancel"
)
public class AccountServiceImpl implements IAccountService {

	@Transactional
	public void increaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	}

	@Transactional
	public void decreaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	}

}

通过@Compensable注解定义的service为可补偿型service。@Compensable注解需要定义三个参数:
1)interfaceClass,必需。该值用于指定confirm/cancel针对的业务接口,该接口同时被用于校验confirm/cancel实现类。confirm/cancel实现类如果没有实现该业务接口则会被认为无效;
2)confirmableKey,可选。该值用于指定confirm实现类在容器中的beanId,若没有confirm逻辑则不必指定;
3)cancellableKey,可选。该值用于指定cancel实现类在容器中的beanId,若没有cancel逻辑则不必指定;注意:若try阶段执行了写操作则必须有相应的取消逻辑;

3.2.2、confirm实现类
@Service("accountServiceConfirm")
public class AccountServiceConfirm implements IAccountService {
	static final Logger logger = LoggerFactory.getLogger(AccountServiceConfirm.class);

	@Transactional
	public void increaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	    logger.info("done increase: acct= {}, amount= {}", accountId, amount);
	}

	@Transactional
	public void decreaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	    logger.info("done decrease: acct= {}, amount= {}", accountId, amount);
	}

}

注意:
1)全局事务决定提交时,可补偿型service的confirm逻辑总是会被执行;
2)全局事务决定提交时,可能会存在某个分支事务try操作没有执行成功的情况,此时该分支的confirm逻辑仍然会被调用。存在该情况的原因是:分支事务执行出错并抛出异常(如ServiceException),其业务逻辑通过Transactional定义了该异常应该回滚事务(或容器通过判断其异常类型最终决定回滚),因而导致分支的try阶段操作没有生效;然而发起方捕捉到了分支抛出的异常,此时如果发起方可以处理分支执行出错的逻辑,则不再向外抛出异常;最终发起方的容器认为执行成功,并决定提交全局事务,因此就会通知分支事务管理器提交分支事务,而分支事务会回调分支事务中涉及的所有service的confirm逻辑。
3)confirm逻辑被回调时,若不确定try阶段事务是否成功执行,则可以通过CompensableContext.isCurrentCompensableServiceTried()来确定。
4)confirm阶段仅负责本service的confirm逻辑,而不应该再执行远程调用。如果try阶段调用过远程服务,则事务上下文已传播至远程节点,全局事务提交时,将由其所在节点的事务管理器负责执行confirm逻辑。

3.2.3、cancel实现类
@Service("accountServiceCancel")
public class AccountServiceCancel implements IAccountService {
	static final Logger logger = LoggerFactory.getLogger(AccountServiceCancel.class);

	@Transactional
	public void increaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	}

	@Transactional
	public void decreaseAmount(String accountId, double amount) 
               throws ServiceException {
	    // TODO ...
	}
}

注意:
1) 全局事务决定回滚时,分支事务中可补偿型service的cancel逻辑总是会被执行;
2) 全局事务决定回滚时,主事务中可补偿型service的cancel逻辑并不一定会被执行;原因是:主事务控制着全局事务的最终完成方向,当其最终决定回滚全局事务时,有机会通过将自己本地Try阶段的事务直接rollback来完成撤销try阶段操作,而不必通过cancel逻辑来实现。
3) cancel阶段仅负责本service的cancel逻辑,而不应该再执行远程调用。如果try阶段调用过远程服务,则事务上下文已传播至远程节点,全局事务回滚时,将由其所在节点的事务管理器负责执行cancel逻辑。

// TODO

If you've found byteTCC useful, and would like to support future development of byteTCC, please consider donating. Any amount is appreciated.

WeiXin

Alipay

Clone this wiki locally