第 8 章 OptaPlanner SolverManager
是用于一个或多个 Solver 实例的传真,简化了 REST 和其他企业服务中的规划问题。
Solver Manager
与 Solver.solve (…) 方法不同,SolverManager 具有以下特征:
-
SolverManager.solve (…)立即返回:它会调度问题以异步解决,而不阻止调用线程。这可避免 HTTP 和其他技术的超时问题。 -
SolverManager.solve (…)可以并行解决同一域的多个规划问题。
在内部,SolverManager 管理一个 solver 线程的线程池,该线程调用 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());
提交到 SolverManager.solve (…) 方法的每个问题都必须具有唯一的问题 ID。之后调用 getSolverStatus (problemId) 或 terminateEarly (problemId) 使用问题 ID 来区分计划问题。问题 ID 必须是不可变类,如 Long、String 或 java.util.UUID。
SolverManagerConfig 类具有一个 parallelSolverCount 属性,用于控制并行运行多少个 solvers。例如,如果 parallelSolverCount 属性设置为 4,并且您提交五个问题,则四个问题会立即解决,并且第五个问题在其中一个问题结束时开始。如果这些问题解决五分钟,则第五个问题需要花费 10 分钟才能完成。默认情况下,parallelSolverCount 设置为 AUTO,它解析为一半的 CPU 内核,而不考虑 solvers 的 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 实施在单个计算机节点上运行,但未来的工作旨在在云之间分发 solver 负载。
8.1. 批处理解决问题
批量解决正在并行解决多个数据集。批量解决在夜间特别有用:
- 夜中通常有一些或没有问题更改。有些机构强制使用截止时间,例如,在 午夜前提交所有日期的请求。
- solvers 可以运行更长的时间(通常小时),因为 nobody 正在等待结果和 CPU 资源通常更便宜。
- 当员工达到下一个工作时,可以使用解决方案。
流程
要并行解决问题,请受 parallelSolverCount 的限制,请对每个数据集调用 solve (…) 创建了以下类:
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) {...}
}