问题描述:
这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);
        通过以上方式,可以确保在并发环境下也能正确使用指定的数据源。你可以选择其中一种方式来解决你的问题,具体选择取决于你的项目结构和上下文管理方式。