问题描述:
这impl文件上已经注明了@DS("tms")
,但是在时间执行过程中还是使用的别的数据源。
dart
public Map<String, List<String>> queryMonitoringMetricsOnSevenDays(QueryResourceTo projectInfo) {
if(projectInfo.getServiceNameList()==null ||projectInfo.getServiceNameList().isEmpty()){
return null;
}
// 创建三个 CompletableFuture 任务
CompletableFuture<Map<Object, Object>> slowInterfaceFuture = CompletableFuture.supplyAsync(
() -> tradeLogMapper.querySlowInterfaceNumOnSevenDays(projectInfo.getServiceNameList()), taskExecutor);
CompletableFuture<Map<Object, Object>> slowSqlFuture = CompletableFuture.supplyAsync(
() -> sqlLogMapper.querySlowSqlNumOnSevenDays(projectInfo.getServiceNameList()), taskExecutor);
CompletableFuture<Map<Object, Object>> errorInfoFuture = CompletableFuture.supplyAsync(
() -> errorLogMapper.queryErrorNumOnSevenDays(projectInfo.getServiceNameList()), taskExecutor);
// 在需要打印数据源的地方添加以下代码
DataSource dataSource = applicationContext.getBean(DataSource.class);
System.out.println("当前使用的数据源为:" + dataSource);
// 等待所有任务完成
CompletableFuture.allOf(slowInterfaceFuture).join();
try {
// 获取三个任务的结果
Map<Object, Object> slowInterface = slowInterfaceFuture.get();
Map<Object, Object> slowSql = slowSqlFuture.get();
Map<Object, Object> errorInfo = errorInfoFuture.get();
HashMap<String, List<String>> objectObjectHashMap = new HashMap<>();
// 在这里处理获取到的结果
// ...
return null;
} catch (InterruptedException | ExecutionException e) {
// 处理异常
e.printStackTrace();
return null;
}
}
在并发环境下,使用指定的数据源可能会出现问题,这通常与以下几个原因有关:
-
数据源上下文的线程本地变量:很多数据源切换机制依赖于线程本地变量(ThreadLocal)来存储当前的数据源信息。在并发环境下,线程池中的线程复用可能导致数据源上下文信息丢失或不一致。
-
CompletableFuture的线程池问题 :
CompletableFuture.supplyAsync
默认使用的是ForkJoinPool.commonPool
,这个线程池中的线程是全局共享的,因此无法保证每个线程都使用正确的数据源上下文。 -
异步任务的AOP切面问题 :
@DS
注解依赖 AOP 切面来切换数据源,而异步任务中的切面可能不会被正确触发,导致数据源没有切换。
解决方法
- 使用自定义的线程池:通过自定义线程池并配置正确的数据源上下文,可以确保异步任务中数据源的正确切换。
- 在任务中手动设置数据源:在每个异步任务中显式地设置数据源。
示例代码
下面是一些可能的解决方案:
1. 自定义线程池
创建一个自定义的线程池,并确保线程池中的线程可以正确传递数据源上下文。
java
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("async-");
executor.setTaskDecorator(new ContextCopyingDecorator());
executor.initialize();
return executor;
}
自定义 TaskDecorator
用于复制数据源上下文:
java
public class ContextCopyingDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, Object> context = ContextHolder.getContext(); // 假设ContextHolder用于获取当前线程的数据源上下文
return () -> {
ContextHolder.setContext(context); // 设置数据源上下文
try {
runnable.run();
} finally {
ContextHolder.clearContext(); // 清除上下文,避免泄露
}
};
}
}
完整代码
完整示例
以下是完整的代码示例:
java
typescript
// ContextHolder.java
public class ContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setContext(String context) {
contextHolder.set(context);
}
public static String getContext() {
return contextHolder.get();
}
public static void clearContext() {
contextHolder.remove();
}
}
// ContextCopyingDecorator.java
public class ContextCopyingDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
String context = ContextHolder.getContext(); // 获取当前线程的数据源上下文
return () -> {
ContextHolder.setContext(context); // 设置数据源上下文
try {
runnable.run();
} finally {
ContextHolder.clearContext(); // 清除上下文,避免泄露
}
};
}
}
// Configuration class to create a custom thread pool
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("async-");
executor.setTaskDecorator(new ContextCopyingDecorator());
executor.initialize();
return executor;
}
}
// In your service class where you run asynchronous tasks
public class MonitoringService {
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Autowired
private TradeLogMapper tradeLogMapper;
@Autowired
private SqlLogMapper sqlLogMapper;
@Autowired
private ErrorLogMapper errorLogMapper;
public Map<String, List<String>> queryMonitoringMetricsOnSevenDays(QueryResourceTo projectInfo) {
if (projectInfo.getServiceNameList() == null || projectInfo.getServiceNameList().isEmpty()) {
return null;
}
CompletableFuture<Map<Object, Object>> slowInterfaceFuture = CompletableFuture.supplyAsync(() -> {
ContextHolder.setContext("tms");
try {
return tradeLogMapper.querySlowInterfaceNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
ContextHolder.clearContext();
}
}, taskExecutor);
CompletableFuture<Map<Object, Object>> slowSqlFuture = CompletableFuture.supplyAsync(() -> {
ContextHolder.setContext("tms");
try {
return sqlLogMapper.querySlowSqlNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
ContextHolder.clearContext();
}
}, taskExecutor);
CompletableFuture<Map<Object, Object>> errorInfoFuture = CompletableFuture.supplyAsync(() -> {
ContextHolder.setContext("tms");
try {
return errorLogMapper.queryErrorNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
ContextHolder.clearContext();
}
}, taskExecutor);
CompletableFuture.allOf(slowInterfaceFuture, slowSqlFuture, errorInfoFuture).join();
try {
Map<Object, Object> slowInterface = slowInterfaceFuture.get();
Map<Object, Object> slowSql = slowSqlFuture.get();
Map<Object, Object> errorInfo = errorInfoFuture.get();
HashMap<String, List<String>> result = new HashMap<>();
// 在这里处理获取到的结果
// ...
return result;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
}
}
通过以上配置和代码,你可以在并发环境下正确使用指定的数据源。希望这些步骤能够解决你的问题。
2. 在任务中手动设置数据源
在异步任务中手动切换数据源:
java
CompletableFuture<Map<Object, Object>> slowInterfaceFuture = CompletableFuture.supplyAsync(() -> {
DynamicDataSourceContextHolder.push("tms");
try {
return tradeLogMapper.querySlowInterfaceNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
DynamicDataSourceContextHolder.poll();
}
}, taskExecutor);
CompletableFuture<Map<Object, Object>> slowSqlFuture = CompletableFuture.supplyAsync(() -> {
DynamicDataSourceContextHolder.push("tms");
try {
return sqlLogMapper.querySlowSqlNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
DynamicDataSourceContextHolder.poll();
}
}, taskExecutor);
CompletableFuture<Map<Object, Object>> errorInfoFuture = CompletableFuture.supplyAsync(() -> {
DynamicDataSourceContextHolder.push("tms");
try {
return errorLogMapper.queryErrorNumOnSevenDays(projectInfo.getServiceNameList());
} finally {
DynamicDataSourceContextHolder.poll();
}
}, taskExecutor);
通过以上方式,可以确保在并发环境下也能正确使用指定的数据源。你可以选择其中一种方式来解决你的问题,具体选择取决于你的项目结构和上下文管理方式。