第 9 章 OptaPlanner SolverManager
是一个或多个 Solver 实例的常见问题,旨在简化 REST 和其他企业服务中的规划问题。
Solver Manager
与 Solver.solve(…) 方法不同,SolverManager 具有以下特征:
-
SolverManager.solve(…)立即返回:它会调度一个问题,以便在不阻止调用线程的情况下进行异步解决。这可避免 HTTP 和其他技术的超时问题。 -
SolverManager.solve(…)可并行解决同一域的多个规划问题。
在内部,SolverManager 管理了一个临时解决方案程序线程的线程池,它调用 Solver.solve(…),以及处理最佳解决方案更改事件的线程池。
在 Quarkus 和 Spring Boot 中,SolverManager 实例会在您的代码中自动注入。如果您使用 Quarkus 或 Spring Boot 以外的平台,请使用 create(…) 方法构建 SolverManager 实例:
SolverConfig solverConfig = SolverConfig.createFromXmlResource(".../cloudBalancingSolverConfig.xml");
SolverManager<CloudBalance, UUID> solverManager = SolverManager.create(solverConfig, new SolverManagerConfig());
提交至 Solv erManager.solve(…)方法的 每个问题都必须有唯一的问题 ID。之后调用 getSolverStatus(problemId) 或终止Early(problemId) 使用问题 ID 来区分规划问题。问题 ID 必须是一个不可变的类,如 Long、String 或 java.util.UUID。
SolverManagerConfig 类有一个 parallelSolverCount 属性,用于控制并行运行了多少 solvers。例如,如果 parallelSolverCount 属性被设置为 4,您提交了五个问题,则四个问题开始立即解决,在第一个问题之一结束时启动第五个问题。如果这些问题每五分钟解决,则第五个问题需要 10 分钟完成。默认情况下,parallelSolverCount 设置为 AUTO,它解析为 CPU 内核的一半,无论解决者的 moveThreadCount 是什么。
要获得最佳解决方案,在解决终止后通常会使用 SolverJob.getFinalBestSolution() :
CloudBalance problem1 = ...;
UUID problemId = UUID.randomUUID();
// Returns immediately
SolverJob<CloudBalance, UUID> solverJob = solverManager.solve(problemId, problem1);
...
CloudBalance solution1;
try {
// Returns only after solving terminates
solution1 = solverJob.getFinalBestSolution();
} catch (InterruptedException | ExecutionException e) {
throw ...;
}
然而,在用户需要解决方案前解决批处理问题以及用户正在主动等待解决方案时进行实时解决问题。
当前的 SolverManager 实施在一个计算机节点上运行,但未来的工作旨在跨云分发解决方法。
9.1. 批量解决问题 复制链接链接已复制到粘贴板!
批量解决问题正在并行解决多个数据集。批量解决问题尤其有用:
- 夜之间通常没有问题变化,或者没有问题变化。例如,某些组织强制执行截止时间,在 午夜前提交所有关闭请求。
- 解决者会运行时间更长的时间,因为 nobody 正在等待结果,而 CPU 资源通常更便宜。
- 当员工到达下一工作日时,可以使用解决方案。
流程
为批量解决并行解决的问题,通过 parallelSolverCount 限制,每个数据集的 address (…) 被调用:
public class TimeTableService {
private SolverManager<TimeTable, Long> solverManager;
// Returns immediately, call it for every data set
public void solveBatch(Long timeTableId) {
solverManager.solve(timeTableId,
// Called once, when solving starts
this::findById,
// Called once, when solving ends
this::save);
}
public TimeTable findById(Long timeTableId) {...}
public void save(TimeTable timeTable) {...}
}