适用场景:APS / 排程系统、使用 ConstraintStream 或 Drools DRL、依赖实时规划(Real-time Planning)
参考来源:OptaPlanner 发展方向与问题
一、升级前评估(必做)
-
确认当前版本
-
当前 OptaPlanner 版本:
_______(例如 7.44.0.Final) -
是否使用 Drools DRL 评分:✅ 是 / ❌ 否
-
是否使用链式规划(
@PlanningChain/PlanningVariable(graphType = CHAINED)):✅ 是 / ❌ 否 -
是否使用
VariableListener:✅ 是 / ❌ 否 -
是否使用实时规划(
SolverManager.addProblemChange()):✅ 是 / ❌ 否
-
-
确认目标版本
-
计划升级到:
8.___.Final -
Spring Boot 版本是否 ≥ 2.7(8.x 最低要求)
-
-
阅读官方迁移指南
-
OptaPlanner 8 Migration Guide
-
重点关注 ConstraintStream 与 Drools 的生命周期说明
-
二、依赖与构建配置
- 升级 OptaPlanner BOM / Dependency
XML
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-core</artifactId>
<version>8.x.x.Final</version>
</dependency>
-
移除或升级 Drools 相关依赖
-
若计划保留 Drools:确认
optaplanner-drools版本一致 -
若计划迁移:移除
optaplanner-drools、kie-ci等依赖
-
-
清理本地构建缓存
-
Maven:
mvn clean -
Gradle:
gradle clean build --refresh-dependencies
-
三、API 迁移(核心修改)
1. ConstraintProvider(ApsConstraintProvider.java)
- penalize / reward 方法签名调整
java
// 7.x
.penalize("超时惩罚", HardSoftScore.ONE_HARD);
// 8.x
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("超时惩罚");
-
统一约束命名规则
-
所有约束均以
asConstraint("xxx")结尾 -
避免匿名约束,便于调试与分数分析
-
-
验证所有 ConstraintStream 流
-
from()/join()/ifExists()行为是否变化 -
是否存在因类型推断导致的编译错误
-
2. VariableListener 包路径变更
- 修改 import 语句
java
// 7.x
import org.optaplanner.core.impl.domain.variable.listener.VariableListener;
import org.optaplanner.core.impl.score.director.ScoreDirector;
// 8.x
import org.optaplanner.core.api.domain.variable.VariableListener;
import org.optaplanner.core.api.score.director.ScoreDirector;
-
检查所有自定义 VariableListener
-
文件名示例:
*VariableListener.java -
确认无
impl包残留引用
-
3. ScoreDirector API 变更
- 字符串属性名 → 方法引用
java
// 7.x
scoreDirector.beforeVariableChanged(entity, "planStartTime");
scoreDirector.afterVariableChanged(entity, "planStartTime");
// 8.x
scoreDirector.beforeVariableChanged(entity, Entity::getPlanStartTime);
scoreDirector.afterVariableChanged(entity, Entity::getPlanStartTime);
-
全局搜索关键词
-
beforeVariableChanged -
afterVariableChanged -
variableChanged
-
四、实时规划(Real-time Planning)适配
1. ProblemChange 接口统一
- 确认所有实时变更逻辑已迁移到
ProblemChange
java
public class AddTaskProblemChange implements ProblemChange<Schedule> {
@Override
public void doChange(Schedule schedule, ScoreDirector<Schedule> scoreDirector) {
// ...
}
}
-
移除旧版实时规划相关代码
-
Solver.addEntity() -
Solver.removeEntity() -
Solver.addProblemFact()
-
2. SolverManager Problem ID 治理(⚠️ 重要)
参考博文:SolverManager 存在 Problem ID 复用异常问题
-
平台侧接管 Problem ID 生成
-
客户端传入业务 ID(如
orderId) -
平台生成内部
problemId(UUID / 自增 ID)
-
-
接口层改造
-
启动规划:返回内部
problemId -
查询 / 终止 / 实时变更:使用内部
problemId
-
-
增加 Problem ID 清理机制
-
规划完成后主动调用
solverManager.close(solverJobId) -
避免异常中断导致状态残留
-
五、链式规划与新特性评估
1. 现有链模型(CHAINED)
-
暂不迁移到
@PlanningListVariable-
确认当前链模型运行稳定
-
8.x 新链模型尚未完全成熟(参考博文)
-
-
验证 Shadow Variable 行为
-
@CustomShadowVariable监听器是否正常触发 -
时间计算逻辑是否与之前一致
-
2. 新特性试用(可选)
-
@PlanningListVariable(仅限新模块)-
明确标注为"实验性功能"
-
准备回退方案
-
-
@IndexShadowVariable- 仅在非核心链路中试用
六、Drools DRL 迁移(长期必做)
Drools DRL 将在 OptaPlanner 9.x 移除
-
制定 DRL → ConstraintStream 迁移计划
- 优先级:核心约束 > 次要约束 > 调试约束
-
增量迁移
-
每次迁移 1--2 条约束
-
对比新旧分数结果一致性
-
-
或评估 Incremental Java Score Calculation
-
性能要求极高时考虑
-
实现成本较高(参考博文)
-
七、测试与验证
-
单元测试
-
所有 ConstraintProvider 测试通过
-
边界条件(空集合、单实体)验证
-
-
集成测试
-
典型业务场景全量规划
-
实时规划(添加 / 删除任务)验证
-
-
回归测试
-
与 7.x 在相同数据集上运行
-
对比解的质量与分数构成
-
允许分数结构变化,但业务结果应一致
-
-
性能测试
-
规划耗时是否显著增加
-
GC / CPU 使用率是否在可接受范围
-
八、部署与回滚预案
-
灰度发布
-
先在非核心业务线启用 8.x
-
观察 1--2 个迭代周期
-
-
回滚方案
-
Git tag / 分支保留 7.x 稳定版本
-
数据库 schema 无破坏性变更(如有需单独处理)
-
-
监控增强
-
Solver 执行时长监控
-
Problem ID 泄漏检测
-
异常 SolverJob 自动清理
-
九、升级完成确认
-
所有
javax.*/impl包引用已清除 -
所有约束均有
asConstraint()命名 -
实时规划功能经生产流量验证
-
团队已了解 8.x API 变化点
-
文档已更新(架构图、API 说明)
附录:常见搜索关键词(用于全局替换)
java
beforeVariableChanged
afterVariableChanged
VariableListener
ScoreDirector
penalize(
reward(
impl.domain.variable
impl.score.director
✅ 预计修改工作量:2--3 小时
✅ 主要风险点:SolverManager Problem ID 管理、新链模型成熟度
✅ 推荐节奏:先升级 + 稳定 API,再逐步迁移 Drools