Flowable流程暂停后从JMS监听器重启报错求助
先拆解你遇到的核心问题:激活流程时的FlowableObjectNotFoundException,以及为什么REST查询不到暂停的流程实例,再逐一给出解决方案和正确的操作方式。
1. 为什么REST查询不到暂停的流程实例?
Flowable的默认流程实例查询接口(包括官方REST API)默认只返回活跃(未挂起)的流程实例。如果要查看挂起的实例,必须明确指定筛选条件:
- 用REST API时,添加请求参数:
?suspended=true(或者suspended=all同时查询活跃和挂起的实例) - 用Java API时,需要在查询链中调用
.includeSuspended():
List<ProcessInstance> suspendedInstances = runtimeService.createProcessInstanceQuery() .includeSuspended() .list();
你之前REST查询为空,只是因为没筛选挂起状态,流程实例其实还在数据库里,只是处于暂停状态。
2. 为什么激活时提示找不到流程实例?
可能存在以下几个原因:
(1)硬编码PkgId导致查询匹配失败
你的ValidationDelegate里硬写了pkgMap.setPkgId(1235L);,这意味着所有暂停的流程实例都会关联同一个固定PkgId。如果JMS消息中的PkgId不是1235L,repo.findByPkgId(...)会返回null,后续调用idMap.getProcessInstanceId()要么抛出空指针,要么拿到无效的流程ID,最终触发FlowableObjectNotFoundException。
解决方法:从流程变量中动态获取业务关联的PkgId,比如:
Long pkgId = (Long) delegateExecution.getVariable("pkgId"); // 假设流程中已设置该变量 pkgMap.setPkgId(pkgId);
(2)事务未提交导致数据未持久化
ValidationDelegate中,暂停流程和保存ProcessInstancePkgMap的操作属于流程执行的同一个事务。如果JMS消息在事务提交前就被消费,此时数据库中还没有更新流程实例的挂起状态,也没有写入ProcessInstancePkgMap记录,自然查询不到对应数据。
解决方法:
- 确保JMS消息的发送逻辑在事务提交之后执行,可以借助Spring的事务同步机制实现;
- 在JMS监听器中添加重试逻辑,给事务提交留足时间,比如用
@Retryable注解实现重试。
(3)RuntimeService获取方式存在风险
你通过CommandContextUtil.getProcessEngineConfiguration().getRuntimeService()获取RuntimeService,在Spring环境下这种方式可能导致事务上下文不一致。更稳妥的做法是直接注入RuntimeService:
@Slf4j @Component public class ValidationDelegate extends AbstractBaseUtil { protected Expression restEndPoint; @Autowired private ProcessInstancePkgIdMapRepository repo; @Autowired private RuntimeService runtimeService; // 直接注入Spring管理的实例 @Override public void executeInternal(DelegateExecution delegateExecution) { runtimeService.suspendProcessInstanceById(delegateExecution.getProcessInstanceId()); ProcessInstancePkgMap pkgMap= new ProcessInstancePkgMap (); Long pkgId = (Long) delegateExecution.getVariable("pkgId"); pkgMap.setPkgId(pkgId); pkgMap.setProcessInstanceId(delegateExecution.getProcessInstanceId()); ProcessInstancePkgMap map = repo.save(pkgMap); log.info("saved map {}", map); } }
3. 暂停流程会导致其终止吗?
绝对不会。Flowable中的suspendProcessInstanceById()只是将流程实例标记为挂起状态,不会终止或删除实例。挂起的实例仍然完整保存在数据库中,只是会暂停执行后续流程节点,直到被主动激活。
真正会终止流程的操作是deleteProcessInstance()或endProcessInstance(),这类操作会彻底结束流程并清理相关运行时数据(历史数据按配置保留)。
4. 暂停/重启挂起工作流的正确方式
(1)正确暂停流程
在流程Delegate中,直接用注入的RuntimeService调用暂停方法即可,确保操作在事务内完成:
// 基础暂停操作 runtimeService.suspendProcessInstanceById(processInstanceId); // 可选:同时挂起流程中的所有任务,并添加暂停原因 runtimeService.suspendProcessInstanceById(processInstanceId, true, "等待外部验证结果");
(2)正确激活流程
在JMS监听器中,先查询确认流程实例存在且处于挂起状态,再执行激活操作,避免无效调用:
@Component public class JMSListener { @Autowired private RuntimeService runtimeService; @Autowired private ProcessInstancePkgIdMapRepository repo; @JmsListener(destination = "test.topic", containerFactory = "jmsListenerContainerFactory") public void receiveMessage(TextMessage message) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); ValidationMessage validationMessage = objectMapper.readValue(message.getText(), ValidationMessage.class); ProcessInstancePkgIdMap idMap = repo.findByPkgId(validationMessage.getPkgId()); if (idMap == null) { log.error("No process instance found for PkgId: {}", validationMessage.getPkgId()); return; } // 先查询确认流程实例存在且处于挂起状态 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() .processInstanceId(idMap.getProcessInstanceId()) .includeSuspended() .singleResult(); if (processInstance != null && processInstance.isSuspended()) { runtimeService.activateProcessInstanceById(processInstance.getId()); log.info("Activated process instance: {}", processInstance.getId()); } else { log.error("Process instance {} not found or not suspended", idMap.getProcessInstanceId()); } } }
(3)额外注意事项
- 给
ProcessInstancePkgIdMap表的pkgId字段添加索引,提升查询性能; - 在JMS监听器中添加幂等性处理,比如记录已处理的PkgId,避免重复激活同一个流程实例;
- 完善日志和异常捕获,方便排查问题。
内容的提问来源于stack exchange,提问作者Shenali Silva

