从Drools 5.5.0.Final迁移至9.44.0.Final时的调用死锁问题
Drools 5.5.0.Final 迁移至 9.44.0.Final:规则调用Java方法触发Query导致会话挂起
问题现象
将Drools从5.5.0.Final升级到9.44.0.Final后,原有规则中调用Java方法、Java方法内再调用Drools Query的逻辑出现执行挂起问题。旧版本中该逻辑可正常运行,但新版本执行时会陷入无响应状态。
调试发现:KieSession的Agenda处于FIRING状态,Query调用会在Agenda中新增一个被永久阻塞的任务,无法返回代码。
相关代码示例
Drools 规则代码
//// // DROOLS //// query javaToDrools ves : Data() end rule "INSERT: New Item" when $data: MyData($id:id) then System.out.println("Insert Data: " + $id); insert(new Data($data)); myJavaClass.droolsToJava(); end
Java 代码
//// // JAVA //// public void droolsToJava() { QueryResults results = session.getQueryResults("javaToDrools"); results.forEach(res -> { res.toString(); }); session.fireUntilHalt(); }
问题调用链分析
挂起发生在以下调用链中:
StatefulKnowledgeSessionImpl.getQueryResults -> StatefulKnowledgeSessionImpl.internalGetQueryResult -> StatefulKnowledgeSessionImpl.evalQuery -> StatefulKnowledgeSessionImpl.addPropagation -> 添加了Propagation任务
在evalQuery方法中,executeQuery.getResult()始终无法获取结果,陷入无限循环,无法在向KieSession插入事实的同时调用Query。
原因分析
Drools 新版本(尤其是7.x之后的Kie架构重构版本)对会话的并发和状态管理做了严格限制:
- 当Agenda处于FIRING状态(规则正在执行then块逻辑)时,调用
getQueryResults会触发新的规则传播任务,但此时会话正处于规则执行的临界区,新任务无法被调度执行,导致死锁。 - 旧版本的StatefulKnowledgeSession对这种嵌套调用的限制较宽松,允许在规则执行过程中同步执行Query并触发新的规则传播,但新版本的KieSession引入了更严格的状态检查和锁机制,避免并发问题的同时,也阻止了这种不安全的嵌套操作。
解决方案
方案1:异步执行fireUntilHalt,避免阻塞当前规则线程
将Java方法中的fireUntilHalt改为异步执行,跳出当前规则执行的临界区:
public void droolsToJava() { QueryResults results = session.getQueryResults("javaToDrools"); results.forEach(res -> { res.toString(); }); // 异步启动规则持续触发,不阻塞当前规则执行线程 new Thread(() -> session.fireUntilHalt()).start(); }
方案2:重构逻辑,将Query操作移至规则内部
如果Query的目的是获取刚插入的Data对象,可直接在规则then块中处理,无需调用Java方法:
rule "INSERT: New Item" when $data: MyData($id:id) then System.out.println("Insert Data: " + $id); Data newData = new Data($data); insert(newData); // 直接在规则中处理Data对象,替代Java层的Query逻辑 System.out.println("Processed Data: " + newData); end
方案3:在规则执行完成后再触发Query和规则传播
调整执行顺序,确保Query调用时Agenda处于IDLE状态:
// 先插入事实并执行所有规则 session.insert(new MyData(1)); session.fireAllRules(); // 此时规则执行完毕,可安全执行Query QueryResults results = session.getQueryResults("javaToDrools"); results.forEach(res -> { res.toString(); }); session.fireUntilHalt();
内容的提问来源于stack exchange,提问作者Marc Gil Sendra
相关产品推荐
相关产品推荐

