工作流框架Activiti中的仿真引擎CrystalBall使用分析

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本篇文章介绍了Activiti业务流程管理平台上的仿真引擎Activiti-Crystalball,可以使用这个仿真引擎对用户的工作流的场景进行模拟,生成不同的流程使用报告。根据流程的仿真模拟,进行历史分析,流程回放以及对工作流流程的调试。

Activiti-Crystalball简介

  • Activiti-Crystalball(CrystalBall)是Activiti业务流程管理平台的仿真引擎 .CrystalBall可以使用用用户模拟流程场景:

    • 决策支持: 对于生产流程, 比如是否应该向系统添加更多资料以达到截止日期
    • 优化和验证: 测试修改并验证影响
    • 培训: 模拟器可以用来在使用前培训员工
  • CrystalBall是独立的:

    • 不需要创建单独的模拟模型和引擎
    • 不需要为模拟创建不同的报告
    • 不需要为模拟引擎准备很多数据
  • CrystalBall模拟器是基于Activiti的:

    • 容易复制数据
    • 启动模拟器
    • 从历史中重播流程行为

CrystalBall内部

  • CrystalBall是一个离散事件模拟器
  • CrystalBall的一个实现是org.activiti.crystalball.simulator.SimpleSimulationRun:
init();

    SimulationEvent event = removeSimulationEvent();

    while (!simulationEnd(event)) {
      executeEvent(event);
      event = removeSimulationEvent();
    }

    close();
  • SimulationRun可以执行由不同源生成的模拟事件

历史分析

  • 模拟器可以使用的用例之一是分析历史
  • 生产环境没有提供任何重复和调试bug的机会,这就是为什么基本不可能把流程引擎恢复到生产环境出现问题时完全一样的状态.有以下原因:

    • 时间: 流程实例可能执行好几个月
    • 并发: 流程实例会和别的实例一起运行,问题可能只产生于并发执行的情况
    • 用户: 很多用户可以参与到流程实例中,流程实例会影响到出现问题的状态
  • 模拟器可以更好的暴露以上的问题:

    • 模拟过程是虚拟的,不会依赖真实环境
    • Activiti流程引擎本身是虚拟的,不需要创建虚拟流程引擎,作为模拟环境使用
    • 并发场景也是原生的
    • 用户行为都会记录日志,并可以从日志重现,根据需要进行预测和生成
  • 分析历史的最好办法是重现一次,真实环境很难实现重现,但是模拟器就可以实现重现

历史的事件

  • 重现历史最重要的事情是记录影响状态的事件
  • 流程是由用户事件驱动的,可以使用两种事件源:

    • 流程实例: 只支持原始的Activiti-Crystalball项目
    • ActivitiEvent日志: 可以向引擎添加想要记录日志的ActivitiEventListener. 事件日志可以保存下来,用于后续的分析
  • ActivitiEventListener的一个基本实现: org.activiti.crystalball.simulator.delegate.event.impl.InMemoryRecordActivitiEventListener
 @Override
  public void onEvent(ActivitiEvent event) {
    Collection<SimulationEvent> simulationEvents = transform(event);
    store(simulationEvents);
  }
  • 事件会被保存,可以对历史进行重现

回放

  • 回放的好处是可以一遍一遍播放,直到完全理解发生了什么
  • Crystalball模拟器是基于真实数据,真实用户行为

  • 示例: 理解回放工作的最好方法是一步一步解释

    • 基于JUnit的测试例子 :org.activiti.crystalball.simulator.delegate.event.PlaybackRunTest
<process id="theSimplestProcess" name="Without task Process">
    <documentation>This is a process for testing purposes</documentation>

    <startEvent id="theStart"/>
    <sequenceFlow id="flow1" sourceRef="theStart" targetRef="theEnd"/>
    <endEvent id="theEnd"/>

  </process>

流程发布,可以用于真实和模拟的运行:

  • 记录事件
// get process engine with record listener to log events
  ProcessEngine processEngine = (new RecordableProcessEngineFactory(THE_SIMPLEST_PROCESS, listener))
  .getObject();

  // start process instance with variables
  Map<String,Object> variables = new HashMap<String, Object>();
  variables.put(TEST_VARIABLE, TEST_VALUE);
  processEngine.getRuntimeService().startProcessInstanceByKey(SIMPLEST_PROCESS, BUSINESS_KEY,variables);

  // check process engine status - there should be one process instance in the history
  checkStatus(processEngine);

  // close and destroy process engine
  EventRecorderTestUtils.closeProcessEngine(processEngine, listener);
  ProcessEngines.destroy();

startProcessInstanceByKey方法调用后,记录ActivitiEventType.ENTITY_CREATED

  • 开始模拟运行:
final SimpleSimulationRun.Builder builder = new SimpleSimulationRun.Builder();
  // init simulation run
  // get process engine factory - the only difference from RecordableProcessEngineFactory that log listener is not added
  DefaultSimulationProcessEngineFactory simulationProcessEngineFactory = new DefaultSimulationProcessEngineFactory(THE_SIMPLEST_PROCESS);
  // configure simulation run
  builder.processEngine(simulationProcessEngineFactory)
         // set playback event calendar from recorded events
         .eventCalendar(new PlaybackEventCalendarFactory(new SimulationEventComparator(), listener.getSimulationEvents()))
         // set handlers for simulation events
         .customEventHandlerMap(EventRecorderTestUtils.getHandlers());
  SimpleSimulationRun simRun = builder.build();

  simRun.execute(new NoExecutionVariableScope());

  // check the status - the same method which was used in record events method
  checkStatus(simulationProcessEngineFactory.getObject());

  // close and destroy process engine
  simRun.getProcessEngine().close();
  ProcessEngines.destroy();

  • 其它示例在org.activiti.crystalball.simulator.delegate.event.PlaybackProcessStartTest中

调试流程引擎

  • 回放限制执行所有模拟事件一次性
  • 调试器允许将流程事件自行拆分成更小的步骤,在步骤之间观察流程引擎的状态

    • SimpleSimulationRun实现了SimulationDebugger接口 .SimulationDebugger可以一步一步执行模拟事件,可以模拟特定时间的执行:
 /**
  * Allows to run simulation in debug mode
  */
  public interface SimulationDebugger {
  /**
  * initialize simulation run
  * @param execution - variable scope to transfer variables from and to simulation run
  */
  void init(VariableScope execution);

  /**
  * step one simulation event forward
  */
  void step();

  /**
  * continue in the simulation run
  */
  void runContinue();

  /**
  * execute simulation run till simulationTime
  */
  void runTo(long simulationTime);

  /**
  * execute simulation run till simulation event of the specific type
  */
  void runTo(String simulationEventType);

  /**
  * close simulation run
  */
  void close();
  }
  • 执行SimpleSimulationRunTest来观察流程引擎调试器的运行

重播

  • 回放需要创建另一个流程引擎实例,模拟环境配置
  • 重播工作在真实的流程引擎之上,重播在运行的流程引擎中执行模拟事件:

    • 结论是重播是实时运行的,实时意味着会被立即执行**

重播一个流程实例示例: ReplyRunTest

  • 第一部分 :初始化流程引擎,启动一个流程实例,完成流程实例的任务
ProcessEngine processEngine = initProcessEngine();

  TaskService taskService = processEngine.getTaskService();
  RuntimeService runtimeService = processEngine.getRuntimeService();

  Map<String, Object> variables = new HashMap<String, Object>();
  variables.put(TEST_VARIABLE, TEST_VALUE);
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(USERTASK_PROCESS, BUSINESS_KEY,
  variables);

  Task task = taskService.createTaskQuery().taskDefinitionKey("userTask").singleResult();
  TimeUnit.MILLISECONDS.sleep(50);
  taskService.complete(task.getId());

使用的流程引擎是基础的InMemoryStandaloneProcessEngine: 配置了InMemoryRecordActivitiEventListener(记录Activiti事件,并转换为模拟事件)和UserTaskExecutionListener(当创建新用户任务时,新任务会重播流程实例,把任务完成事件放到事件日历中)

  • 第二部分 :在原始流程相同的引擎引擎上启动模拟调试器

    • 重播事件处理器使用StartReplayProcessEventHandler替换StartProcessEventHandler
    • StartReplayProcessEventHandler获取流程实例Id来重播,在流程实例启动的初始位置处理
    • StartProcessEventHandler在开始阶段,会创建一个新流程实例,包含一个变量.变量名为 _replay.processInstanceId. 变量用来保存重播的流程实例Id
    • SimpleSimulationRun不同 ,ReplaySimulationRun:

      • 不会创建和关闭流程引擎实例
      • 不会修改模拟时间
final SimulationDebugger simRun = new ReplaySimulationRun(processEngine,
getReplayHandlers(processInstance.getId()));
  • 开始重播流程实例:

    • 一开始, 没有运行的流程实例
    • 只有一个已完成的,在历史中的流程实例
    • 在初始化后,会在事件日历中添加一个模拟事件-用来启动流程实例,重播已经完成的流程实例
 simRun.init();

  // original process is finished - there should not be any running process instance/task
  assertEquals(0, runtimeService.createProcessInstanceQuery().processDefinitionKey(USERTASK_PROCESS).count());
  assertEquals(0, taskService.createTaskQuery().taskDefinitionKey("userTask").count());

  simRun.step();

  // replay process was started
  assertEquals(1, runtimeService.createProcessInstanceQuery().processDefinitionKey(USERTASK_PROCESS).count());
  // there should be one task
  assertEquals(1, taskService.createTaskQuery().taskDefinitionKey("userTask").count());
  • 任务创建时,UserTaskExecutionListener会创建一个新模拟事件来结束用户任务:
  simRun.step();

  // userTask was completed - replay process was finished
  assertEquals(0, runtimeService.createProcessInstanceQuery().processDefinitionKey(USERTASK_PROCESS).count());
  assertEquals(0, taskService.createTaskQuery().taskDefinitionKey("userTask").count());
  • 模拟结束.这时可以继续启动另一个流程实例或者事件,然后关闭simRun和流程引擎:
  simRun.close();
  processEngine.close();
  ProcessEngines.destroy();
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
XML 数据库 数据格式
嵌入式工作流程开发!工作流 Activiti 框架中子流程的使用指南
本篇文章对工作流Activiti框架中的子流程进行的详尽的分析和说明,在工作流Activiti集成到项目中开发时,可以嵌入子流程进行使用。子流程包括了事件子流程,事务子流程以及调用活动子流程。通过对内嵌子流程的方式的学习,可以帮助我们将工作流框架很好地应用在继承式建模的流程场景下。
1046 0
嵌入式工作流程开发!工作流 Activiti 框架中子流程的使用指南
|
5月前
|
XML 关系型数据库 MySQL
工作流框架--Activiti6.0(二)
工作流框架--Activiti6.0(二)
331 1
|
5月前
|
架构师 Java 关系型数据库
工作流框架--Activiti6.0(一)
工作流框架--Activiti6.0(一)
126 0
|
6月前
|
XML JavaScript 前端开发
基于jeecgboot的flowable流程支持服务任务的功能
基于jeecgboot的flowable流程支持服务任务的功能
155 0
01activiti - 工作流概念
01activiti - 工作流概念
52 0
|
SQL 存储 缓存
BPMN工作流的基本概念!详解工作流框架Activiti
本文介绍了工作流的基本概念,对工作流中的相关术语,例如工作流引擎,BPM,BPMN以及流对象进行说明。着重介绍了当下非常流行的工作流框架Activiti,重点介绍了工作流框架Activiti的服务结构,工作流组件,流程虚拟机PVM以及工作流框架的架构和使用特点。通过这篇文章,可以对工作流有一个基本的认识,为后续工作流框架Activiti的学习打下坚实的基础。
1047 0
BPMN工作流的基本概念!详解工作流框架Activiti
主流工作流引擎 flowable 三种方式部署流程
主流工作流引擎 flowable 三种方式部署流程
387 0
|
存储 SQL XML
工作流引擎Activiti使用进阶!详细解析工作流框架中高级功能的使用示例
本篇文章介绍了Activiti的几个高级用例。主要包括监听流程解析,使用UUID生成器,多租户,执行自定义的SQL,实现流程引擎配置,安全的BPMN 2.0结构以及事件日志的使用。使用这些高级功能,可以使得集成工作流Activiti的项目具有更多的可操作性。
1201 0
工作流引擎Activiti使用进阶!详细解析工作流框架中高级功能的使用示例
|
存储 Java API
工作流中的数据持久化详解!Activiti框架中JPA的使用分析
本文介绍了工作流Activiti框架中流程数据的持久化,在工作流Acitiviti框架中JPA的使用方式。介绍了使用JPA实体的基本要求,JPA的使用配置以及JPA的具体用法。最后通过一个示例展示了工作流中的流程数据的持久化以及如何查询JPA变量和在Spring Bean中集成JPA使用。
347 0
工作流中的数据持久化详解!Activiti框架中JPA的使用分析
|
XML 数据格式
详细解析Activiti Modeler!工作流建模组件的使用说明
本篇文章对工作流Activiti中的建模组件Activiti Modeler的使用进行的详细的说明。Activiti Modeler主要负责对工作流的流程模型进行操作,包括编辑流程模型,导入流程模型,转换流程定义到流程模型,流程模型部署到Activiti的流程引擎。通过这篇文章的学习,可以帮助我们熟练的学会使用工作流建模组件Activiti Modeler。
1985 0
详细解析Activiti Modeler!工作流建模组件的使用说明