因为工作和其它原因,很长一段时间没有出新的、关于OptaPlanner的文章了,但工余时间并没有停止对该引擎的学习。与此同时Geoffrey大神带领的KIE项目团队并没有闲下来,尽管在工业可用性、易用性和使用门槛方面,OptaPlanner相对传统的求解器已经做得相当出色;特别是在规划过程交互、和各种操作接口方面,更是目前最为容易使用的规划求解器。

以第7版一系列子版本中,OptaPlanner很多子版只作了细微的更新,如优化规划性能,改善Business Center集成水平等。而在作为OptaPlanner直接使用者的我们而言,第7版的所有子版本中,目前本人认为最大最有意义的更新有2个。一个是7.9.0版本提供内了置的多线程规划,从而实现了规划过程中的多CPU同时对同一问题进行运算,大大地发挥了多CPU(核)服务器的并行运算能力。而今天本文需要详解的新增接口SolverManager则是在系统集成方面的另一次重大创新。SolverManager接口在7.32.0版本中发布。

规划服务的常见场景与异步服务

OptaPlanner的核心是一个运筹优化求解器,可以对各领域的规划问题(NPC, NP-Hard问题)进行规划求解,寻找出问题的近似最优解。OptaPlanner规划组件提供了相当完善的求解运算功能。但在实际的规划系统设计中,除了设计相应的规划模型,还需要考虑规划程序部署问题,便于与现有系统集成。这类部署问题并非OptaPlanner求解器自身的功能焦点。因此,对于我们软件开发、工程人员而言,还需要设计好相应的架构系统,才能实现规划程序与现有信息系统之间良好数据交互。

通常情况下规划运算需要使用大量的运算资源,也即CPU运算能力。我们会把基于OptaPlanner的规划程序部署成独立的规划服务,以接口方式与外界系统进行数据通讯。部署成独立的服务除了有利于降低系统模块间低耦合外,另一个重要原因是有利于运算资源的扩展。当问题规划增大时,程序所需的CPU运算能力也会大副提升;独立存在的规划服务更有利于硬件资源的更新。当然,需要在Client端进行即时规划的场景(例如手机导航软件)除外。

因为规划服务大多数情况下,需要一定的运算周期才能得到可行、且相对最优方案。若根据上述的场景需求,在常见的项目中,可以把规划程序做成一个轻量级的Jar包,再过Web和应用服务器,以Web服务的方式对外提供服务。例如使用Spring Boot进行封装,对外提供Web API服务。通过使Spring Boot的Controller与规划程序包在进程上相互独立,从而实现规划服务的异步性。当然也可以通过在Spring Boot程序中通过多线程方式实现异常调用的特性。不同的实现方法视实际需要而定。

SolverManager特性解决异步问题

对于上述场景,OptaPlanner是否可提供Out-Of-The-Box的解决方案呢?在7.32.0.Final版本之前,求解器规划问题时的接口方法是Solver.solve(),这个方法是同步的,需要规划完成后才能返回。若需要实现异步功能,就需要自己想办法实现了,例如上面提到的将服务进程与规划进程相互独立,或使用不同的线程来响应服务和启动规划,实现起来对软件架构设计需要有一定的经验才能做得相对完善。很幸运,在7.32.0.Final版本中,终于从OptaPlanner内置功能上实现了此特性,这个就是SolverManager。SolverManager是7.32.0.Final版本提供的新接口,通过此接口我们可以在调用规划核心程序进行问题求解时,调用线程即时返回,从而实现调用线程与规划线程异步执行。具体访求是:通过SolverManager.solve()方法可以启动一个异步规划方法,调用方可以即时返回,通过轮询的方式调用SolverManager的其它方法来查询规划状态(SolverManger.getSolverStatus)并获取结果(SoverJob.getFinalBestSolution)。SolverManager的基本用法如下:

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对象的solve方法有以下区别:

  1. 异步执行,当solve方法被调用后,方法会马上返回,而不待引擎运行结果。调用者需要通过轮询或回调方法(bestSolutionChanged事件)获取运行结果。
  2. 每个问题对应一个ID,因为SolverManager会启动线程池同一时间对多个问题进行求解,因此每个问题需要有一个唯一的标识做识别,在下一篇文章中的SolverManger批量求解中将会详解。

因此,在7.32.0.Final版本中,SolverManager的出现,将会在进行求解服务的设计过程中,大大简化引擎与服务的设计复杂度。希望在未来的应用过让OptaPlanner在工业场景的可能性上更胜一筹。

关于SolverManager接口的详细介绍见以下使用说明:

https://docs.optaplanner.org/7.33.0.Final/optaplanner-docs/html_single/index.html#solverManager​docs.optaplanner.org

原创不易,如果觉得文章对你有帮助,欢迎点赞、评论。文章有疏漏之处,欢迎批评指正。

本系列文章在公众号不定时连载,请关注公众号(让APS成为可能)及时接收,二维码:

 http://weixin.qq.com/r/WjtFXSvE2HanrW8_925I (二维码自动识别)

如需了解更多关于OptaPlanner的应用,请发电邮致:[email protected]
或到讨论组发表你的意见:https://groups.google.com/forum/#!forum/optaplanner-cn
若有需要可添加本人微信(13631823503)或QQ(12977379)实时沟通,但因本人日常工作繁忙,通过微信,QQ等工具可能无法深入沟通,较复杂的问题,建议以邮件或讨论组方式提出。(讨论组属于google邮件列表,国内网络可能较难访问,需自行解决)

05-11 15:38