项目中碰到一个签章的业务,签章是调第三方接口,调用成功后更新自己项目的数据状态为签章成功。
- 大致思考了下逻辑,考虑数据量庞大问题,就采用分批异步的方式进行签章。确认了下处理方案,异步采用CompletableFuture大致的逻辑代码如下
public void signVoucher() {
// 模拟的处理数据
List<String> ids = Arrays.asList("1","2","3","4","5","6","7","8","9","10");
// 3条每组进行分批
List<List<String>> lists = cn.hutool.core.collection.CollectionUtil.splitList(ids, 3);
List<CompletableFuture<String>> futures = new ArrayList<>();
// 每组数据异步处理
for(List<String> list: lists){
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 更新本项目的签章数据为签章成功
studentMapper.updateByIds(list);
// 业务代码, 调第三方接口签章
// ...doSth(list)
return "200";
}).exceptionally(ex -> {
// 如果失败返回异常信息
return ex.getMessage();
});
futures.add(future);
}
// 收集异步的执行结果
List<String> resList = new ArrayList<>();
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
futures.forEach(future -> resList.add(future.getNow(StringUtils.EMPTY)));
}).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
log.info("请求结果如下:{}", resList);
}
- 进一步思考一下,这样的代码是会有事务问题的,异步里面的业务分为2步如下截图
第1步是更新本项目的数据库信息,第2步是调第三方签章接口更新其他系统数据库的信息。这里注意点是第三方接口调用如果成功了,是不可逆的,我们没办法改变第三方的数据库数据,所以我将本项目的数据库更新写在其前面执行,这样如果我们这边的数据库更新失败了,就不会继续调第三方接口进行入库。话说回来,之所以说会有事务问题可以从以下罗列的情况进行分析:
a. 第一步和第二步都成功,不会出现事务问题
b. 第一步成功,第二步失败会产生事务问题
c. 第一步失败,第二步不会继续执行 - 解决上述的事务问题,将自动提交事务改成手动提交事务,代码改造如下
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
@Autowired
private StudentMapper studentMapper;
@Override
public void signVoucher() {
// 模拟的处理数据
List<String> ids = Arrays.asList("1","2","3","4","5","6","7","8","9","10");
// 3条每组进行分批
List<List<String>> lists = cn.hutool.core.collection.CollectionUtil.splitList(ids, 3);
List<CompletableFuture<String>> futures = new ArrayList<>();
// 每组数据异步处理
for(List<String> list: lists){
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 签章操作不可回滚,先更新工资库数据
log.info("runAsync线程执行, 线程名:{}", Thread.currentThread().getName());
// 手动开启事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 事物隔离级别,开启新事务,这样会比较安全些。
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 获得事务状态
TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
try {
// 更新本项目的签章数据为签章成功
studentMapper.updateByIds(list);
// 业务代码, 调第三方接口签章
// ...doSth(list)
dataSourceTransactionManager.commit(status);
} catch (Exception ex) {
// 如果失败返回异常信息
dataSourceTransactionManager.rollback(status);
return ex.getMessage();
}
return "200";
});
futures.add(future);
}
// 收集异步的执行结果
List<String> resList = new ArrayList<>();
try {
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
futures.forEach(future -> resList.add(future.getNow(StringUtils.EMPTY)));
}).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
log.info("请求结果如下:{}", resList);
}
以上就是本次描述的所有内容