请帮助。 我有一个负责配置数据库的类:
@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;
}
}
我真的需要你的帮助。 谢谢。
答案 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();
}
}