-
-
Notifications
You must be signed in to change notification settings - Fork 911
User Guide 0.4.x
- 仅支持使用@Transactional来配置事务,而不能通过xml来配置事务;
- JDK版本:7.0及以上版本;
- 用@Compensable注解的TCC型服务Controller必须实现一个业务接口(业务系统自行指定,但需要与Compensable.interfaceClass一致);
- 业务系统不可使用随机端口(本约束与采用的负载均衡分发粒度相关,当使用ByteTCC按请求粒度负载均衡时可忽略该约束);
- 必须且仅可指定一个<dubbo:application name="..." />元素,其name不能为空,且必须唯一;
- 必须且仅可指定一个<dubbo:protocol port="..." />元素,其port不能为空,也不能为-1;本约束与ByteTCC采用的负载均衡分发粒度相关,当使用ByteTCC按请求粒度负载均衡时可忽略该约束;
- 定义dubbo服务提供者时(<dubbo:service />):a、filter必须为compensable;b、loadbalance必须为compensable;c 、cluster必须为failfast;d、retries必须为0;e、group必须为org.bytesoft.bytetcc;
- 定义dubbo服务消费者时(<dubbo:reference />):a、filter必须为compensable;b、loadbalance必须为compensable;c 、cluster必须为failfast;d、retries必须为0;e、group必须为org.bytesoft.bytetcc;
- 在每个参与tcc事务的数据库中创建bytejta表(ddl见bytetcc-supports.jar/bytetcc.sql);
- 可补偿型服务的Try/Confirm/Cancel实现类的方法必须定义Transactional注解,且propagation必须是Required, RequiresNew, Mandatory中的一种(即业务代码必须参与事务,从0.3.0开始强制要求);
@ImportResource({ "classpath:bytetcc-supports-springcloud.xml" })
@SpringBootApplication(scanBasePackages = "com.yourcompany.service...")
@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')。
<import resource="classpath:bytetcc-supports-dubbo.xml" />
<context:component-scan base-package="com.yourcompany.service..." />
<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型服务可以有两种配置方式:简化配置、一般配置,业务开发者可视情况选择任意一种。
@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 ...
}
}
简化配置仅适合一个接口只提供一个方法的服务场景,当一个接口有多个方法时,必须使用一般配置。
@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阶段执行了写操作则必须有相应的取消逻辑;
@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逻辑。
@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