Camunda DeploymentCache内存占用问题及精细化管控需求
问题背景
堆转储分析显示,Camunda组件org.camunda.bpm.engine.impl.persistence.deploy.cache.DeploymentCache占用了69.47%的JVM内存,其中org.camunda.bpm.engine.impl.persistence.deploy.cache.BpmnModelInstanceCache占比高达69.03%。
原因在于Camunda启动时会全量加载数据库中所有已部署的流程定义、案例定义和决策定义,而当前系统存在数万条流程定义,导致内存占用过高。
当前采用org.camunda.bpm.engine.RepositoryService#deleteDeployment(java.lang.String, boolean)方法删除旧部署缓解内存问题,代码如下:
List<Deployment> oldDeployments = repositoryService.createDeploymentQuery() .deploymentBefore(date) .listPage(0, maxResult); boolean cascade = true; for (Deployment deployment : oldDeployments) { repositoryService.deleteDeployment(deployment.getId(), cascade); }
但该方案缺乏管控能力,核心支付类流程即使过期也不能被删除,需作为例外处理。以下是针对性的优化方案:
方案一:添加业务判断处理器,精准管控可删除部署
在现有删除逻辑中加入业务校验步骤,通过查询业务数据表的额外标识,判断部署是否允许被删除:
示例代码修改
List<Deployment> oldDeployments = repositoryService.createDeploymentQuery() .deploymentBefore(date) .listPage(0, maxResult); boolean cascade = true; for (Deployment deployment : oldDeployments) { // 1. 通过部署ID/关联业务标识查询是否为核心流程 boolean isCoreProcess = businessProcessService.isCorePaymentProcess(deployment.getId()); // 2. 核心流程直接跳过删除 if (isCoreProcess) { continue; } // 3. 非核心流程执行删除 repositoryService.deleteDeployment(deployment.getId(), cascade); }
其中businessProcessService.isCorePaymentProcess为自定义业务方法,可根据部署关联的流程key、业务标签等标识,判断是否属于不可删除的核心流程。
方案二:排除特定模式的部署,从查询阶段过滤
直接在部署查询时排除特定模式的部署,避免加载到待删除列表中:
示例代码修改
// 排除名称包含"payment-core"的核心支付部署,同时排除指定流程key的部署 List<Deployment> oldDeployments = repositoryService.createDeploymentQuery() .deploymentBefore(date) .deploymentNameNotLike("%payment-core%") .processDefinitionKeyNotIn("core-payment-flow") .listPage(0, maxResult); boolean cascade = true; for (Deployment deployment : oldDeployments) { repositoryService.deleteDeployment(deployment.getId(), cascade); }
可根据实际业务规则,通过部署名称、流程key、租户ID等多维度条件过滤,直接排除不可删除的部署。
方案三:自定义DeploymentCache,优化加载与缓存策略
针对Camunda启动全量加载的特性,通过自定义DeploymentCache实现按需加载或容量限制:
1. 设置缓存容量上限
Camunda 7.13.0支持通过配置参数限制缓存容量,在application.yml中添加:
camunda: bpm: deployment-cache: bpmn-model-cache-capacity: 5000 # 设置BpmnModelInstanceCache的最大容量 process-definition-cache-capacity: 5000 # 设置流程定义缓存容量
当缓存达到上限时,会自动淘汰最少使用的缓存条目,避免内存持续增长。
2. 自定义DeploymentCache实现按需加载
扩展DefaultDeploymentCache,重写初始化逻辑,启动时仅加载指定时间段内的部署,其余流程按需动态加载:
public class CustomDeploymentCache extends DefaultDeploymentCache { private final RepositoryService repositoryService; private final Date loadStartTime; public CustomDeploymentCache(CacheFactory cacheFactory, RepositoryService repositoryService, Date loadStartTime) { super(cacheFactory); this.repositoryService = repositoryService; this.loadStartTime = loadStartTime; } @Override protected void deploymentsAdded(List<Deployment> deployments) { // 仅加载指定时间范围内的部署 List<Deployment> filteredDeployments = deployments.stream() .filter(d -> d.getDeploymentTime().after(loadStartTime)) .collect(Collectors.toList()); super.deploymentsAdded(filteredDeployments); } // 重写流程定义获取方法,未缓存时动态加载 @Override public ProcessDefinition findProcessDefinitionById(String processDefinitionId) { ProcessDefinition definition = super.findProcessDefinitionById(processDefinitionId); if (definition == null) { // 从数据库查询并加载到缓存 definition = repositoryService.createProcessDefinitionQuery() .processDefinitionId(processDefinitionId) .singleResult(); if (definition != null) { deploy(repositoryService.getDeployment(definition.getDeploymentId())); } } return definition; } }
然后通过配置类替换默认的DeploymentCache:
@Configuration public class CamundaCacheConfig { @Bean public DeploymentCache customDeploymentCache(CacheFactory cacheFactory, RepositoryService repositoryService) { // 仅加载最近30天的部署 Date thirtyDaysAgo = DateUtils.addDays(new Date(), -30); return new CustomDeploymentCache(cacheFactory, repositoryService, thirtyDaysAgo); } }
注意:按需加载需权衡性能,建议配合缓存过期策略使用,避免频繁查询数据库。
内容的提问来源于stack exchange,提问作者leonaugust

