
本文介绍在 spring 应用中通过 solvermanager 的回调机制与 solverjob api,精准捕获 optaplanner 求解结束时刻并获取最终最优解,实现结果自动处理与业务逻辑联动。
本文介绍在 spring 应用中通过 solvermanager 的回调机制与 solverjob api,精准捕获 optaplanner 求解结束时刻并获取最终最优解,实现结果自动处理与业务逻辑联动。
在基于 Spring 构建的 OptaPlanner 应用中,求解器(Solver)通常以异步方式运行,尤其当使用 SolverManager(推荐用于 Web/Spring 环境)时,其生命周期不由开发者直接阻塞控制。因此,不能依赖 solver.solve() 的同步返回来感知结束——该方法本身即返回 SolverJob 并立即退出。真正需要的是事件驱动的完成通知机制。
✅ 推荐方案:使用 SolverManager.solve() 或 solveAndListen() 配置完成回调
SolverManager 提供了两类核心异步求解方法,均支持传入 Consumer
// 方式一:仅关注最终解(简洁常用)
solverManager.solve(
1L, // problemId(用于跟踪与取消)
MyPlanningProblem::createProblem, // ProblemSupplier
finalBestSolution -> {
// ✅ 此处 guaranteed 执行一次,且仅在求解结束时调用
log.info("Optimization completed. Best score: {}", finalBestSolution.getScore());
resultService.saveOptimizedPlan(finalBestSolution);
notificationService.sendCompletionNotice();
}
);
// 方式二:同时监听中间解与最终解(适合需实时反馈的场景)
solverManager.solveAndListen(
1L,
MyPlanningProblem::createProblem,
bestSolution -> {
// ? 中间最优解更新时触发(可选)
metricsService.recordIntermediateScore(bestSolution.getScore());
},
finalBestSolution -> {
// ✅ 最终解到达时触发(必达)
auditService.logSolvingResult(finalBestSolution);
// 注意:此处 finalBestSolution 可能为 null(若求解被取消或无可行解)
if (finalBestSolution != null && finalBestSolution.getScore().isFeasible()) {
publishToMessageQueue(finalBestSolution);
}
},
null // 异常处理器:设为 null 使用默认日志记录;也可传入自定义 Consumer<throwable>
);</throwable>
✅ 替代方案:主动轮询 SolverJob.getFinalBestSolution()
若因架构限制无法使用回调(如需严格同步等待),可通过 SolverJob 的 getFinalBestSolution() 方法阻塞等待结果(内部使用 CompletableFuture.get()):
SolverJob<myplanningproblem long> job = solverManager.solve(
1L, MyPlanningProblem::createProblem, null // 不设 listener,后续手动取结果
);
try {
// ⚠️ 注意:此调用会阻塞当前线程,直到求解结束或超时
MyPlanningProblem finalSolution = job.getFinalBestSolution(30, TimeUnit.SECONDS);
if (finalSolution != null) {
handleOptimizedResult(finalSolution);
} else {
throw new IllegalStateException("No solution found within timeout");
}
} catch (TimeoutException e) {
job.cancel(); // 主动取消避免资源泄漏
throw new RuntimeException("Solver timed out", e);
} catch (ExecutionException | InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Solver execution failed", e);
}</myplanningproblem>
? 关键注意事项:
- finalBestSolutionListener 是唯一可靠且非阻塞的完成通知方式,强烈推荐用于 Spring Web MVC/REST 控制器等异步上下文;
- getFinalBestSolution() 的阻塞特性在高并发 Web 场景中易导致线程耗尽,严禁在请求线程中直接调用,仅适用于后台批处理或测试;
- 所有回调均在 SolverManager 内部线程池中执行,请确保业务逻辑线程安全,避免在回调中执行耗时 I/O(建议委托至 TaskExecutor);
- SolverJob 对象本身支持 cancel() 和 isCancelled(),可用于实现前端主动取消功能;
- 若使用 @SpringBootTest 测试,需确保 SolverManager 已正确注入(通常由 optaplanner-spring-boot-starter 自动配置)。
通过合理选用上述机制,您不仅能精确感知求解器停止时刻,还能无缝衔接结果处理、监控上报与用户通知,真正实现 OptaPlanner 在 Spring 生态中的生产级集成。










