|
|
|
@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
package com.op.job.util;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.op.job.config.DbIdentityEnum;
|
|
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
|
|
|
import org.springframework.beans.factory.annotation.Qualifier;
|
|
|
|
|
|
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
|
|
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 多数据库Feign调用通用工具类
|
|
|
|
|
|
|
|
* 所有需要多库并行执行的定时任务,直接调用此工具的execute方法
|
|
|
|
|
|
|
|
* 核心:多线程+请求头传递+异常隔离+通用化
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@Component
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
|
|
public class MultiDbFeignExecutor {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
|
|
@Qualifier("multiDbTaskExecutor")
|
|
|
|
|
|
|
|
private ThreadPoolTaskExecutor multiDbTaskExecutor;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 通用执行方法:多线程并行调用Feign接口,为每个线程传入对应数据库的请求头
|
|
|
|
|
|
|
|
* @param taskName 定时任务名称(用于日志排查)
|
|
|
|
|
|
|
|
* @param feignCall 函数式接口:封装Feign调用逻辑(入参=数据库请求头,无返回值)
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public void execute(String taskName, FeignDbCall feignCall) {
|
|
|
|
|
|
|
|
// 1. 获取所有数据库的专属请求头(一个库一个独立Map,线程安全)
|
|
|
|
|
|
|
|
Map<String, Map<String, String>> allDbHeaders = DbIdentityEnum.getAllDbHeaders();
|
|
|
|
|
|
|
|
if (allDbHeaders.isEmpty()) {
|
|
|
|
|
|
|
|
log.warn("【多库执行-{}】无配置的数据库,直接返回", taskName);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("【多库执行-{}】开始并行执行,数据库数量:{}", taskName, allDbHeaders.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 多线程并行执行:每个数据库一个独立线程,异常隔离
|
|
|
|
|
|
|
|
allDbHeaders.entrySet().stream()
|
|
|
|
|
|
|
|
.map(entry -> {
|
|
|
|
|
|
|
|
String dbId = entry.getKey();
|
|
|
|
|
|
|
|
Map<String, String> dbHeader = entry.getValue();
|
|
|
|
|
|
|
|
// 提交异步任务,绑定专属线程池
|
|
|
|
|
|
|
|
return CompletableFuture.runAsync(() -> {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
log.info("【多库执行-{}】开始执行数据库:{}", taskName, dbId);
|
|
|
|
|
|
|
|
// 执行Feign调用,传入当前数据库的专属请求头
|
|
|
|
|
|
|
|
feignCall.call(dbHeader);
|
|
|
|
|
|
|
|
log.info("【多库执行-{}】数据库{}执行成功", taskName, dbId);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
// 单个数据库执行异常,隔离处理,不影响其他库
|
|
|
|
|
|
|
|
log.error("【多库执行-{}】数据库{}执行失败,异常原因:{}",
|
|
|
|
|
|
|
|
taskName, dbId, e.getMessage(), e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, multiDbTaskExecutor);
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.collect(Collectors.toList())
|
|
|
|
|
|
|
|
// 3. 等待所有数据库执行完成(保证定时任务执行完整性)
|
|
|
|
|
|
|
|
.forEach(CompletableFuture::join);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("【多库执行-{}】所有数据库执行完毕,总库数:{}", taskName, allDbHeaders.size());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 函数式接口:封装「数据库请求头→Feign调用」的逻辑
|
|
|
|
|
|
|
|
* 入参:当前数据库的专属请求头Map,适配所有Feign接口的请求头参数
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
@FunctionalInterface
|
|
|
|
|
|
|
|
public interface FeignDbCall {
|
|
|
|
|
|
|
|
void call(Map<String, String> dbHeader);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|