乐趣区

关于仿真:工作流框架中的仿真引擎实现用户流程场景的仿真

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();
退出移动版