如何使用多个数据源获得3个线程池?

时间:2019-06-26 21:50:04

标签: java spring-boot jdbc concurrency stream

请帮助。 我有一个负责配置数据库的类:

@Getter
@Setter
@ConfigurationProperties(value = "spring")
public class DatabaseConfig {

    @NotEmpty
    private Map<String, Database> databases;

    @Getter
    @Setter
    public static class Database {
    @NotEmpty
    private String url;

    @NotEmpty
    private String username;

    private String password;

    @NotEmpty
    private String driverClassName;
    }
}

我的 application.yml 文件中有3个数据库:

spring:
    H21:
      url: jdbc:h2:tcp://localhost/~/test
      username: sa
      password:
      driver-class-name: org.h2.Driver
    H22:
      url: jdbc:h2:tcp://localhost/~/test2
      username: sa
      password:
      driver-class-name: org.h2.Driver
    H23:
      url: jdbc:h2:tcp://localhost/~/test3
      username: sa
      password:
      driver-class-name: org.h2.Driver

我有一个Map,在其中初始化应用程序时(为此,我使用 @PostConstruct 方法),包括了application.yml文件中的所有数据源。 好。没关系。 但是我有一个问题。

在doCheck方法方法中,我创建了一个线程池= Map中的数据源数量(3个项目),并将Map和call方法分别传递给数据库。

必须为所有数据库并行调用 performQuery 方法中的行 template.query(query,ResultSet :: getRow) 按顺序。 对我来说很重要。

如果在application.yml文件3数据库中,它不起作用(应用程序启动,并且当我通过Postman发送请求时,程序等待了很长时间,没有任何反应)。 如果在application.yml文件2数据库中,则一切正常。 我在互联网上读到parallelStream()仅创建2个线程池。因此,当application.yml文件中只有2个数据库并且它们并行工作时,它对我有用。 但是,在application.yml文件中有3个碱基时,它对我不起作用。

@Service
@Log
@RequiredArgsConstructor
public class DBServiceImpl implements DBService {

    private final DatabaseConfig databaseConfig;

    private Map<String, JdbcTemplate> templates;

    @PostConstruct
    public void init() {
    templates = databaseConfig.getDatabases()
            .entrySet()
            .stream()
            .collect(Collectors.toMap(Map.Entry::getKey, entry -> createJdbcTemplate(entry.getValue())));
    }

    private JdbcTemplate createJdbcTemplate(DatabaseConfig.Database database) {
    return new JdbcTemplate(DataSourceBuilder
            .create()
            .url(database.getUrl())
            .username(database.getUsername())
            .password(database.getPassword())
            .driverClassName(database.getDriverClassName())
            .build());
    }

    public synchronized PerformanceCheckResult doCheck(String query) throws InterruptedException, ExecutionException {
    PerformanceCheckResult checkResult = PerformanceCheckResult
            .builder()
            .queryExecutionResults(new CopyOnWriteArrayList<>())
            .checkStartedAt(System.currentTimeMillis())
            .build();

    ExecutorService executor = Executors.newFixedThreadPool(templates.size());

    List<QueryExecutionResult> queryExecutionResults = executor.submit(() -> templates
            .entrySet()
            .parallelStream()
            .map(entry -> performQuery(entry.getKey(), entry.getValue(), query))
            .collect(Collectors.toList())).get();

    checkResult.setQueryExecutionResults(queryExecutionResults);
    checkResult.setCheckEndedAt(System.currentTimeMillis());
    checkResult.setTotalCheckDurationTime(getDurationMillis(checkResult.getCheckStartedAt(), checkResult.getCheckEndedAt()));
    return checkResult;
    }

    private QueryExecutionResult performQuery(String databaseName, JdbcTemplate template, String query) {
    QueryExecutionResult executionResult = QueryExecutionResult
            .builder()
            .databaseName(databaseName)
            .queryExecutionStartedAt(System.currentTimeMillis())
            .build();
    template.query(query, ResultSet::getRow);
    executionResult.setQueryExecutionEndedAt(System.currentTimeMillis());
    executionResult.setQueryExecutionDurationTime(
            getDurationMillis(executionResult.getQueryExecutionStartedAt(), executionResult.getQueryExecutionEndedAt()));
    return executionResult;
    }

    private Long getDurationMillis(Long start, Long end) {
    return end - start;
    }

}   

我真的需要你的帮助。 谢谢。

1 个答案:

答案 0 :(得分:0)

您应该在执行程序上使用invokeAll而不是submit。 这段代码只是将一个任务提交给执行者,执行者然后调用parallelStream

List<QueryExecutionResult> queryExecutionResults = executor.submit(() -> templates
        .entrySet()
        .parallelStream()
        .map(entry -> performQuery(entry.getKey(), entry.getValue(), query))
        .collect(Collectors.toList())).get();

您不需要parallelStream;只需创建任务集合并使用执行者的invokeAll方法,该方法将尝试尽可能多地并行化。

这是我的意思的草图:

class Query implements Callable<String> {
    public String call() {
        String s = ""; // do some database query that returns a string
        return s;
    }
}

public static void main(String[] args) {
    var queries = List.of(new Query(...), ...);
    var exec = Executors.newFixedThreadPool(queries.size());
    var futures = exec.invokeAll(queries);

    for (f : futures) {
        f.get();
    }
}