- pom依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
- 数据源配置
@Configuration
@Slf4j
public class DataSourceConfiguration {
@Autowired(required = true)
private DataSourceProperties dataSourceProperties;
// private final static Logger logger = LoggerFactory.getLogger(SeataAutoConfig.class);
private DataSourceProxy dataSourceProxy;
@Bean(name = "dataSource") // 声明其为Bean实例
@Primary // 在同样的DataSource中,首先使用被标注的DataSource
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
log.info("dataSourceProperties.getUrl():{}", dataSourceProperties.getUrl());
druidDataSource.setUrl(dataSourceProperties.getUrl());
druidDataSource.setUsername(dataSourceProperties.getUsername());
druidDataSource.setPassword(dataSourceProperties.getPassword());
druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 1 from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
log.info("装载dataSource........");
dataSourceProxy = new DataSourceProxy(druidDataSource);
return dataSourceProxy;
}
/**
* init datasource proxy
*
* @Param: druidDataSource datasource bean instance
* @Return: DataSourceProxy datasource proxy
*/
@Bean
public DataSourceProxy dataSourceProxy() {
log.info("代理dataSource........");
return dataSourceProxy;
}
/**
* init global transaction scanner
*
* @Return: GlobalTransactionScanner
*/
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
log.info("配置seata........");
return new GlobalTransactionScanner("test-service", "test-group");
}
}
- yml
spring:
application:
name: server1
main:
allow-bean-definition-overriding: true
cloud:
alibaba:
seata:
tx-service-group: service-user-provider-group
- 测试例子
(1) server1调用测试代码
@RestController
@RequestMapping("/seata")
public class TestRest {
@Autowired
private GoodsService goodsService;
@Autowired
private TestFeign testFeign;
@GetMapping("/test")
@GlobalTransactional(rollbackFor = Exception.class)
public RestResponse tetSeata(Integer abc) throws Exception {
goodsService.incUv(24);
testFeign.tetSeata2(abc);
return ResultUtils.setOk();
}
}
feign
@FeignClient(name = "server2", contextId = "TestFeign")
public interface TestFeign {
@GetMapping("/seata/test2")
Response tetSeata2(@RequestParam("abc") Integer abc);
}
(2)server2调用测试代码
同server1类似配置数据源及yml的配置项
@RestController
@RequestMapping("/seata")
public class TestRest {
@Autowired
private GoodsService goodsService;
@Autowired
private Wo wo;
@GetMapping("/test2")
@GlobalTransactional(rollbackFor = Exception.class)
public Response tetSeata2(Integer abc) {
goodsService.incUv(24);
if(abc != null && abc > 0){
throw new ServiceIllegalException("123");
}
// //test222();
//wo.test222();
return ResultUtils.setOk();
}
@GlobalTransactional(rollbackFor = Exception.class)
private void test222(){
schemeGoodsService.incUv(24);
throw new ServiceIllegalException("123");
}
}
AOP
@Aspect
@Component
@Slf4j
public class TestAspect {
@Before("execution(* com.test.test.*.*(..))")
public void before(JoinPoint joinPoint) throws TransactionException {
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
tx.begin(300000, "test-client");
}
@AfterThrowing(throwing = "e", pointcut = "execution(* com.test.test.*.*(..))")
public void doRecoveryActions(Throwable e) throws TransactionException {
log.info("方法执行异常:{}", e.getMessage());
if (!StringUtils.isBlank(RootContext.getXID()))
GlobalTransactionContext.reload(RootContext.getXID()).rollback();
}
@AfterReturning(value = "execution(* com.test.test.*.*(..))", returning = "result")
public void afterReturning(JoinPoint point, Object result) throws TransactionException {
log.info("方法执行结束:{}", result);
if (result != null) {
if (!StringUtils.isBlank(RootContext.getXID())) {
log.info("分布式事务Id:{}", RootContext.getXID());
GlobalTransactionContext.reload(RootContext.getXID()).commit();
}
}
}
}
public interface Wo {
void test222();
}
@Service
public class WoImpl implements Wo {
@Autowired
private GoodsService goodsService;
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void test222() {
goodsService.incUv(24);
throw new ServiceIllegalException("123");
}
}
注意事项:
- 如果通过feign调用不同服务需要开启AOP的处理(都开启了GlobalTransactional)。未开启则不会回滚多个服务调用。
- 入口没有加GlobalTransactional,则直接内部方法调用不会回滚,需要使用接口。