Activiti7异步定时作业源码解析篇

异步作业的配置

1. 初始化时钟

DefaultClockImpl.java
public void initClock() {
        if (this.clock == null) {
            this.clock = new DefaultClockImpl();
        }
 }

2. 初始化作业处理器

Activiti7采用HashMap : <type -> JobHandler>维护全局的作业处理器集合jobHandlers,作业处理器会实现JobHandler接口。 在initJobHandlers中添加6个默认的作业处理器,如下:

JobHandler.java
public interface JobHandler {
    String getType();

    void execute(JobEntity var1, String var2, ExecutionEntity var3, CommandContext var4);
     }
ProcessEngineConfigurationImpl.java
 public void initJobHandlers() {
        this.jobHandlers = new HashMap();
        AsyncContinuationJobHandler asyncContinuationJobHandler = new AsyncContinuationJobHandler();
        this.jobHandlers.put(asyncContinuationJobHandler.getType(), asyncContinuationJobHandler);
        TriggerTimerEventJobHandler triggerTimerEventJobHandler = new TriggerTimerEventJobHandler();
        this.jobHandlers.put(triggerTimerEventJobHandler.getType(), triggerTimerEventJobHandler);
        TimerStartEventJobHandler timerStartEvent = new TimerStartEventJobHandler();
        this.jobHandlers.put(timerStartEvent.getType(), timerStartEvent);
        TimerSuspendProcessDefinitionHandler suspendProcessDefinitionHandler = new TimerSuspendProcessDefinitionHandler();
        this.jobHandlers.put(suspendProcessDefinitionHandler.getType(), suspendProcessDefinitionHandler);
        TimerActivateProcessDefinitionHandler activateProcessDefinitionHandler = new TimerActivateProcessDefinitionHandler();
        this.jobHandlers.put(activateProcessDefinitionHandler.getType(), activateProcessDefinitionHandler);
        ProcessEventJobHandler processEventJobHandler = new ProcessEventJobHandler();
        this.jobHandlers.put(processEventJobHandler.getType(), processEventJobHandler);
        if (this.getCustomJobHandlers() != null) {
            Iterator var7 = this.getCustomJobHandlers().iterator();

            while(var7.hasNext()) {
                JobHandler customJobHandler = (JobHandler)var7.next();
                this.jobHandlers.put(customJobHandler.getType(), customJobHandler);
            }
        }

    }

  1. 默认作业处理器 :
    • AsyncContinuationJobHandler : 异步节点作业处理器
    • TriggerTimerEventJobHandler : 触发时间事件作业处理器
    • TimerStartEventJobHandler : 定时启动流程实例作业处理器
    • TimerSuspendProcessDefinitionHandler : 定时挂起流程定义处理器
    • TimerActivateProcessDefinitionHandler : 定时激活流程定义处理器
    • ProcessEventJobHandler : 流程事件作业处理器
  2. 自定义作业处理器
    customJobHandlers : 这是引擎配置留的扩展点,可以添加自定义作业处理器,如果类型和默认的作业处理器相同,则默认作业处理器将被替换。

3. 初始化作业管理器

ProcessEngineConfigurationImpl.java
public void initJobManager() {
			        if (this.jobManager == null) {
			            this.jobManager = new DefaultJobManager(this);
			        }
			        this.jobManager.setProcessEngineConfiguration(this);
}

  • DefaultJobManager持有引擎配置实例ProcessEngineConfigurationImpl,它可以很方便获取配置中的异步作业执行器AsyncExecutor , 执行流管理器ExecutionEntityManager , 其他的就是对 JobManager接口的实现.

JobManager.java
public interface JobManager {
	    void execute(Job var1);
	
	    void unacquire(Job var1);

	    JobEntity createAsyncJob(ExecutionEntity var1, boolean var2);
	
	    void scheduleAsyncJob(JobEntity var1);
	
	    TimerJobEntity createTimerJob(TimerEventDefinition var1, boolean var2, ExecutionEntity var3, String var4, String var5);
	
	    void scheduleTimerJob(TimerJobEntity var1);
	
	    JobEntity moveTimerJobToExecutableJob(TimerJobEntity var1);
	
	    TimerJobEntity moveJobToTimerJob(AbstractJobEntity var1);
	
	    SuspendedJobEntity moveJobToSuspendedJob(AbstractJobEntity var1);
	
	    AbstractJobEntity activateSuspendedJob(SuspendedJobEntity var1);
	
	    DeadLetterJobEntity moveJobToDeadLetterJob(AbstractJobEntity var1);
	
	    JobEntity moveDeadLetterJobToExecutableJob(DeadLetterJobEntity var1, int var2);
	
	    void setProcessEngineConfiguration(ProcessEngineConfigurationImpl var1);
}

DefaultJobManager.java
	public class DefaultJobManager implements JobManager {
	    private static Logger logger = LoggerFactory.getLogger(DefaultJobManager.class);
	    protected ProcessEngineConfigurationImpl processEngineConfiguration;
	    //for simplicity, some method codes are omitted here.
	    ......
    }

4. 初始化异步作业执行器

  • 这里设为初始化为默认的异步作业执行器DefaultAsyncJobExecutor ,可以看到initAsyncExecutor中很多setXooo(), 在DefaultAsyncJobExecutor中的确很多属性需要初始化。

  • 我们可以看到在引擎配置类中有很多属性和 DefaultAsyncJobExecutor中的属性相对应,比如ProcessEngineConfigurationImpl中的asyncExecutorThreadPoolQueueDefaultAsyncJobExecutor中的threadPoolQueue对应,前者可以看做引擎留给开发者的开关属性, 如果在引擎配置中配置了asyncExecutorThreadPoolQueue,即不为空,就会设置到异步作业执行器中。

    public void initAsyncExecutor() {
        if (this.asyncExecutor == null) {
            DefaultAsyncJobExecutor defaultAsyncExecutor = new DefaultAsyncJobExecutor();
            defaultAsyncExecutor.setMessageQueueMode(this.asyncExecutorMessageQueueMode);
            defaultAsyncExecutor.setCorePoolSize(this.asyncExecutorCorePoolSize);
            defaultAsyncExecutor.setMaxPoolSize(this.asyncExecutorMaxPoolSize);
            defaultAsyncExecutor.setKeepAliveTime(this.asyncExecutorThreadKeepAliveTime);
            if (this.asyncExecutorThreadPoolQueue != null) {
                defaultAsyncExecutor.setThreadPoolQueue(this.asyncExecutorThreadPoolQueue);
            }
    
            defaultAsyncExecutor.setQueueSize(this.asyncExecutorThreadPoolQueueSize);
            defaultAsyncExecutor.setDefaultTimerJobAcquireWaitTimeInMillis(this.asyncExecutorDefaultTimerJobAcquireWaitTime);
            defaultAsyncExecutor.setDefaultAsyncJobAcquireWaitTimeInMillis(this.asyncExecutorDefaultAsyncJobAcquireWaitTime);
            defaultAsyncExecutor.setDefaultQueueSizeFullWaitTimeInMillis(this.asyncExecutorDefaultQueueSizeFullWaitTime);
            defaultAsyncExecutor.setTimerLockTimeInMillis(this.asyncExecutorTimerLockTimeInMillis);
            defaultAsyncExecutor.setAsyncJobLockTimeInMillis(this.asyncExecutorAsyncJobLockTimeInMillis);
            if (this.asyncExecutorLockOwner != null) {
                defaultAsyncExecutor.setLockOwner(this.asyncExecutorLockOwner);
            }
    
            defaultAsyncExecutor.setResetExpiredJobsInterval(this.asyncExecutorResetExpiredJobsInterval);
            defaultAsyncExecutor.setResetExpiredJobsPageSize(this.asyncExecutorResetExpiredJobsPageSize);
            defaultAsyncExecutor.setSecondsToWaitOnShutdown(this.asyncExecutorSecondsToWaitOnShutdown);
            this.asyncExecutor = defaultAsyncExecutor;
        }
    
        this.asyncExecutor.setProcessEngineConfiguration(this);
        this.asyncExecutor.setAutoActivate(this.asyncExecutorActivate);
    }
    

    解释下 重要的属性含义:

    补充下BlockingQueue的介绍 :

    补充下线程池的介绍 :

    public class DefaultAsyncJobExecutor implements AsyncExecutor {
         private static Logger log = LoggerFactory.getLogger(DefaultAsyncJobExecutor.class);
         // corePoolSize : the recommended size of the number of threads in the thread pool.
         // If the actual number of threads in the thread pool is less than `corePoolSize`, create a new core thread first
         protected int corePoolSize = 2;
         // If all queues of non-running or working state in the thread pool are full, 
         // but the current number of threads is less than the maximum number of allowed `maxPoolSize` threads, continue creating non-core threads to perform tasks.
         protected int maxPoolSize = 10;
         // Idle timeout for non-core threads: thread will be recycled when timeout.
         protected long keepAliveTime = 5000L;
         protected int queueSize = 100;
         // task queue : by default, use  `ArrayBlockingQueue` for saving tasks waiting to be performed.
         // passed to `ThreadPoolExecutor` as parameters.
         protected BlockingQueue<Runnable> threadPoolQueue;
         // Thread pool
         protected ExecutorService executorService;
         protected long secondsToWaitOnShutdown = 60L;
         protected Thread timerJobAcquisitionThread;
         protected Thread asyncJobAcquisitionThread;
         protected Thread resetExpiredJobThread;
         protected AcquireTimerJobsRunnable timerJobRunnable;
         protected AcquireAsyncJobsDueRunnable asyncJobsDueRunnable;
         protected ResetExpiredJobsRunnable resetExpiredJobsRunnable;
         protected ExecuteAsyncRunnableFactory executeAsyncRunnableFactory;
         protected boolean isAutoActivate;
         //Indicates whether the job executor is activated
         protected boolean isActive;
         protected boolean isMessageQueueMode;
         //The maximum number of timer jobs acquired from database each time
         protected int maxTimerJobsPerAcquisition = 1;
         //The maximum number of asynchronous jobs acquired from database each time
         protected int maxAsyncJobsDuePerAcquisition = 1;
         protected int defaultTimerJobAcquireWaitTimeInMillis = 10000;
         protected int defaultAsyncJobAcquireWaitTimeInMillis = 10000;
         protected int defaultQueueSizeFullWaitTime = 0;
         protected String lockOwner = UUID.randomUUID().toString();
         protected int timerLockTimeInMillis = 300000;
         protected int asyncJobLockTimeInMillis = 300000;
         protected int retryWaitTimeInMillis = 500;
         protected int resetExpiredJobsInterval = 60000;
         protected int resetExpiredJobsPageSize = 3;
         protected LinkedList<Job> temporaryJobQueue = new LinkedList();
         protected ProcessEngineConfigurationImpl processEngineConfiguration;
       
          protected void initAsyncJobExecutionThreadPool() {
             if (this.threadPoolQueue == null) {
                 log.info("Creating thread pool queue of size {}", this.queueSize);
                 // if `threadPoolQueue` is not specified in `ProcessEngineCofiguration` , the `ArrayBlockingQueue` is as its default value.
                 this.threadPoolQueue = new ArrayBlockingQueue(this.queueSize);
             }
     
             if (this.executorService == null) {
                 log.info("Creating executor service with corePoolSize {}, maxPoolSize {} and keepAliveTime {}", new Object[]{this.corePoolSize, this.maxPoolSize, this.keepAliveTime});
                 // Factory for creating theads and add them to thread pool;
                 BasicThreadFactory threadFactory = (new Builder()).namingPattern("activiti-async-job-executor-thread-%d").build();
                 // Thread pool : it implements the `Executor` interface.
                 this.executorService = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime, TimeUnit.MILLISECONDS, this.threadPoolQueue, threadFactory);
             }
     	  }
     	    // skip some methods .....
    }
    

    从默认的异步任务执行器可知,它维护了一个线程池,并且负责一些 Runnable任务执行,

流程定时启动作业的调度时机

Activiti7中多个流程定义以捆绑式自动部署到流程引擎运行,向外提供Runtime Bundle Service,这里帮大家扒扒流程定时启动事件是怎么以作业的形式添加到数据库中,以待执行

BpmnDeployer DeploymentBuilder GET by this.parsedDeploymentBuilderFactory build() : parsedDeployment BpmnDeployer DeploymentBuilder

BpmnDeployer.java
public void deploy(DeploymentEntity deployment, Map<String, Object> deploymentSettings) {
        log.debug("Processing deployment {}", deployment.getName());
        // Parse the process document element and convert it to BpmnModel
        ParsedDeployment parsedDeployment = this.parsedDeploymentBuilderFactory.getBuilderForDeploymentAndSettings(deployment, deploymentSettings).build();
        // Irrelevant codes ...      

        // If the deployed process documentation is updated,...
        if (deployment.isNew()) {
            Map<ProcessDefinitionEntity, ProcessDefinitionEntity> mapOfNewProcessDefinitionToPreviousVersion =TimerJobEntityManager this.getPreviousVersionsOfProcessDefinitions(parsedDeployment);
            this.setProcessDefinitionVersionsAndIds(parsedDeployment, mapOfNewProcessDefinitionToPreviousVersion);
            this.persistProcessDefinitionsAndAuthorizations(parsedDeployment);
            // The scheduled jobs and event subscriptions is updated here, if necessary
            this.updateTimersAndEvents(parsedDeployment, mapOfNewProcessDefinitionToPreviousVersion);
        }
  		// Irrelevant codes ...      
    }
  • 框架Bpmn文档处理的分两阶段:文档解析 + 对象解析。通过文档解析器BpmnParse将文档解析成BpmnModel,并持久化,在ParsedDeploymentBuilder.build()中会调用BpmnParseexecute() , 这里就是文档解析的入口。
  • 注意 updateTimersAndEvents的调用,这里就会检查并更新事件订阅和定时任务。

BpmnDeploymentHelper.java
    public void updateTimersAndEvents(ProcessDefinitionEntity processDefinition, ProcessDefinitionEntity previousProcessDefinition, ParsedDeployment parsedDeployment) {
        Process process = parsedDeployment.getProcessModelForProcessDefinition(processDefinition);
        BpmnModel bpmnModel = parsedDeployment.getBpmnModelForProcessDefinition(processDefinition);
        this.eventSubscriptionManager.removeObsoleteMessageEventSubscriptions(previousProcessDefinition);
        this.eventSubscriptionManager.addMessageEventSubscriptions(processDefinition, process, bpmnModel);
        this.eventSubscriptionManager.removeObsoleteSignalEventSubScription(previousProcessDefinition);
        this.eventSubscriptionManager.addSignalEventSubscriptions(Context.getCommandContext(), processDefinition, process, bpmnModel);
        this.timerManager.removeObsoleteTimers(processDefinition);
        this.timerManager.scheduleTimers(processDefinition, process);
    }
  • 这里BpmnDeployer的helper类来帮忙了,在框架中Helper很常见,可以看做现实世界中的助理吧,在这里应该算是委托类了。这里会依赖事件订阅管理器EventSubscriptionManagerTimerManager定时作业管理器来更新事件订阅或者定时作业。

BpmnDeploymentHelper.java
protected void scheduleTimers(ProcessDefinitionEntity processDefinition, Process process) {
        JobManager jobManager = Context.getCommandContext().getJobManager();
        // Acquire the timer jobs declared on the start element
        List<TimerJobEntity> timers = this.getTimerDeclarations(processDefinition, process);
        Iterator var5 = timers.iterator();

        while(var5.hasNext()) {
            TimerJobEntity timer = (TimerJobEntity)var5.next();
            // The default job manager schedules scheduled jobs to the database
            jobManager.scheduleTimerJob(timer);
        }

    }

protected List<TimerJobEntity> getTimerDeclarations(ProcessDefinitionEntity processDefinition, Process process) {
        JobManager jobManager = Context.getCommandContext().getJobManager();
        List<TimerJobEntity> timers = new ArrayList();
        if (CollectionUtil.isNotEmpty(process.getFlowElements())) {
            Iterator var5 = process.getFlowElements().iterator();
            while(var5.hasNext()) {
                FlowElement element = (FlowElement)var5.next();
                // If the current element is the start node, ...
                if (element instanceof StartEvent) {
                    StartEvent startEvent = (StartEvent)element;
                	// irrelevant codes...
                				// add timer to timer jobs.
                                timers.add(timerJob);
                }
            }
        }
添加作业到数据库
        return timers;
    }
}
  • 在流程部署的过程中,会将声明在开始节点的定时事件,封装为定时作业,然后调度作业到数据库。【注意这里只是对流程启动相关的定时启动事件做调度】

边界定时作业On UserTask的调度时机

前面介绍的元素解析阶段可能需要建立的定时作业,接下来介绍对象解析阶段可能需要建立的的定时作业,这里以加在UserTask 上的边界定时事件为例。

  • 在启动流程的过程中,会进行对象解析,这里我们关注定时事件对应的解析器TimerEventDefinitionParseHandler的核心方法executePasrse 方法。

TimerEventDefinitionParseHandler.java
protected void executeParse(BpmnParse bpmnParse, TimerEventDefinition timerEventDefinition) {
        if (bpmnParse.getCurrentFlowElement() instanceof IntermediateCatchEvent) {
            IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent)bpmnParse.getCurrentFlowElement();
            intermediateCatchEvent.setBehavior(bpmnParse.getActivityBehaviorFactory().createIntermediateCatchTimerEventActivityBehavior(intermediateCatchEvent, timerEventDefinition));
        } else if (bpmnParse.getCurrentFlowElement() instanceof BoundaryEvent) {
            BoundaryEvent boundaryEvent = (BoundaryEvent)bpmnParse.getCurrentFlowElement();
            boundaryEvent.setBehavior(bpmnParse.getActivityBehaviorFactory().createBoundaryTimerEventActivityBehavior(boundaryEvent, timerEventDefinition, boundaryEvent.isCancelActivity()));
        }
}
  • 大致逻辑给边界事件设置行为对象,在流程运转过程中执行到UserTask节点,会执行Boundary Event行为对象execute方法。

BoundaryTimerEventActivityBehavior.java
public void execute(DelegateExecution execution) {
        ExecutionEntity executionEntity = (ExecutionEntity)execution;
        if (!(execution.getCurrentFlowElement() instanceof BoundaryEvent)) {
            throw new ActivitiException("Programmatic error: " + this.getClass() + " should not be used for anything else than a boundary event");
        } else {
            JobManager jobManager = Context.getCommandContext().getJobManager();
            TimerJobEntity timerJob = jobManager.createTimerJob(this.timerEventDefinition, this.interrupting, executionEntity, "trigger-timer", TimerEventHandler.createConfiguration(execution.getCurrentActivityId(), this.timerEventDefinition.getEndDate(), this.timerEventDefinition.getCalendarName()));
            if (timerJob != null) {
            	// The default job manager schedules scheduled jobs to the database
                jobManager.scheduleTimerJob(timerJob);
            }

        }
}
  • jobManager.scheduleTimerJob(timerJob) 似曾相识, 这里和之前一样就是添加作业到数据库并加入缓存。

调度作业的过程

上文介绍了创建的作业和调度作业的时机,下面展开一下DefaultJobManager调度新作业到数据库的过程。


DefaultJobManager.java
public void scheduleTimerJob(TimerJobEntity timerJob) {
        if (timerJob == null) {
            throw new ActivitiException("Empty timer job can not be scheduled");
        } else {
            this.processEngineConfiguration.getTimerJobEntityManager().insert(timerJob);
            CommandContext commandContext = Context.getCommandContext();
            ActivitiEventDispatcher eventDispatcher = commandContext.getEventDispatcher();
            if (eventDispatcher.isEnabled()) {
                eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TIMER_SCHEDULED, timerJob));
            }

        }
}
  • 这里会调用TimerJobEntityManagerImpl来将作业加入缓存并加到数据库,同时在引擎中dispatch相关的事件。
  • 以定时作业为例,定时作业TimerJobEntityCRUDTimerJobEntityManager管理:

TimerJobEntityManagerImpl.java
public void insert(TimerJobEntity jobEntity, boolean fireCreateEvent) {
        this.doInsert(jobEntity, fireCreateEvent);
    }

protected boolean doInsert(TimerJobEntity jobEntity, boolean fireCreateEvent) {
        if (jobEntity.getExecutionId() != null) {
            ExecutionEntity execution = (ExecutionEntity)this.getExecutionEntityManager().findById(jobEntity.getExecutionId());
            if (execution == null) {
                return false;
            }
			// add `jobEntity` to `EntityCache`
            execution.getTimerJobs().add(jobEntity);
            if (execution.getTenantId() != null) {
                jobEntity.setTenantId(execution.getTenantId());
            }

            if (this.isExecutionRelatedEntityCountEnabled(execution)) {
                CountingExecutionEntity countingExecutionEntity = (CountingExecutionEntity)execution;
                countingExecutionEntity.setTimerJobCount(countingExecutionEntity.getTimerJobCount() + 1);
            }
        }

        super.insert(jobEntity, fireCreateEvent);
        return true;
}
AbstractEntityManager.java
public void insert(EntityImpl entity, boolean fireCreateEvent) {
        this.getDataManager().insert(entity);
        ActivitiEventDispatcher eventDispatcher = this.getEventDispatcher();
        //broadcast `ENTITY_CREATED` event in engine.
        if (fireCreateEvent && eventDispatcher.isEnabled()) {
            eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_CREATED, entity));
            eventDispatcher.dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.ENTITY_INITIALIZED, entity));
        }

}

定时作业的触发时机

异步执行器的启动

流程引擎配置中默认是不开启异步作业执行器,需要在配置中激活它,设置asyncExecutorActivatetrue即可。

ProcessEngineConfigurationImpl.java
public ProcessEngine buildProcessEngine() {
        this.init();
        ProcessEngineImpl processEngine = new ProcessEngineImpl(this);
        this.postProcessEngineInitialisation();
        return processEngine;
}
ProcessEngineImpl.java
  public ProcessEngineImpl(ProcessEngineConfigurationImpl processEngineConfiguration) {

  		// ignore some irrelevant codes ...
  		
        if (processEngineConfiguration.isUsingRelationalDatabase() && processEngineConfiguration.getDatabaseSchemaUpdate() != null) {
            this.commandExecutor.execute(processEngineConfiguration.getSchemaCommandConfig(), new SchemaOperationsProcessEngineBuild());
        }

        if (this.name == null) {
            log.info("default activiti ProcessEngine created");
        } else {
            log.info("ProcessEngine {} created", this.name);
        }

        ProcessEngines.registerProcessEngine(this);
        if (this.asyncExecutor != null && this.asyncExecutor.isAutoActivate()) {
        	//start the the asynchronous executor , if the asynchronous executor is not `null` and is automatically activated
            this.asyncExecutor.start();
        }

        if (processEngineConfiguration.getProcessEngineLifecycleListener() != null) {
            processEngineConfiguration.getProcessEngineLifecycleListener().onProcessEngineBuilt(this);
        }

        processEngineConfiguration.getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createGlobalEvent(ActivitiEventType.ENGINE_CREATED));
    }

  • 如果以ProcessEngineFactoryBean或者其他方式new流程引擎实例,需要调用buildProcessEngine来构造, 在构造器中会启动异步执行器。

DefaultAsyncJobExecutor.java
public void start() {
        if (!this.isActive) {
            log.info("Starting up the default async job executor [{}].", this.getClass().getName());
            if (this.timerJobRunnable == null) {
                this.timerJobRunnable = new AcquireTimerJobsRunnable(this, this.processEngineConfiguration.getJobManager());
            }

            if (this.resetExpiredJobsRunnable == null) {
                this.resetExpiredJobsRunnable = new ResetExpiredJobsRunnable(this);
            }
			//If the message queuing mode is adopted, the thread polling mode is discarded.
            if (!this.isMessageQueueMode && this.asyncJobsDueRunnable == null) {
                this.asyncJobsDueRunnable = new AcquireAsyncJobsDueRunnable(this);
            }

            if (!this.isMessageQueueMode) {
                this.initAsyncJobExecutionThreadPool();
                this.startJobAcquisitionThread();
            }

            this.startTimerAcquisitionThread();
            this.startResetExpiredJobsThread();
            this.isActive = true;
            this.executeTemporaryJobs();
        }
    }

在启动方法,会开启是三个线程分别执行三个Runnable的Job, asyncJobAcquisitionThreadtimerJobAcquisitionThread采用轮询数据库查询新的作业。

  • asyncJobAcquisitionThread -> AcquireAsyncJobsDueRunnable : 监听新的需要执行的异步作业,交给JobManager处理;
  • timerJobAcquisitionThread -> AcquireTimerJobsRunnable : 监听新的需要执行的定时作业,交给JobManager处理;
  • resetExpiredJobThread -> ResetExpiredJobsRunnable : 监听过期作业,重置过期作业;

DefaultAsyncJobExecutor.java
protected void executeTemporaryJobs() {
        while(!this.temporaryJobQueue.isEmpty()) {
            Job job = (Job)this.temporaryJobQueue.pop();
            this.executeAsyncJob(job);
        }

    }
    
	public boolean executeAsyncJob(final Job job) {
        if (this.isMessageQueueMode) {
            return true;
        } else {
            Runnable runnable = null;
            if (this.isActive) {
            	//wrap the job as `Runnable` instance.
                runnable = this.createRunnableForJob(job);

                try {
                	//submit the job to thread pool.
                    this.executorService.execute(runnable);
                } catch (RejectedExecutionException var5) {
                    CommandContext commandContext = Context.getCommandContext();
                    if (commandContext != null) {
                        commandContext.getJobManager().unacquire(job);
                    } else {
                        this.processEngineConfiguration.getCommandExecutor().execute(new Command<Void>() {
                            public Void execute(CommandContext commandContext) {
                                commandContext.getJobManager().unacquire(job);
                                return null;
                            }
                        });
                    }

                    return false;
                }
            } else {
            	//if 'AsyncExecutor` is not activated, add job to the queue of temporary jobs.
                this.temporaryJobQueue.add(job);
            }

            return true;
        }
    }

  • 遍历临时作业队列temporaryJobQueue 中的作业,如果此时异步执行器已经激活,直接封装Runnable对象,提交给线程池executorService执行,否则先暂存在临时作业队列中。

定时作业轮询线程

这里我们需要关注的是执行AcquireTimerJobsRunnable对象线程,它负责不断轮询数据库(在不考虑MessageQueue的模式中)中需要当前时间以前执行的作业。


AcquireTimerJobsRunnable.java
public class AcquireTimerJobsRunnable implements Runnable {
    private static Logger log = LoggerFactory.getLogger(AcquireTimerJobsRunnable.class);
    protected final AsyncExecutor asyncExecutor;
    protected final JobManager jobManager;
    protected volatile boolean isInterrupted;
    protected final Object MONITOR = new Object();
    protected final AtomicBoolean isWaiting = new AtomicBoolean(false);
    protected long millisToWait;

    public AcquireTimerJobsRunnable(AsyncExecutor asyncExecutor, JobManager jobManager) {
        this.asyncExecutor = asyncExecutor;
        this.jobManager = jobManager;
    }

    public synchronized void run() {
        log.info("{} starting to acquire async jobs due");
        Thread.currentThread().setName("activiti-acquire-timer-jobs");
        CommandExecutor commandExecutor = this.asyncExecutor.getProcessEngineConfiguration().getCommandExecutor();

        while(true) {
            do {
                if (this.isInterrupted) {
                    log.info("{} stopped async job due acquisition");
                    return;
                }

                try {
                	// Query the current jobs that need to be executed against the database 
                    final AcquiredTimerJobEntities acquiredJobs = (AcquiredTimerJobEntities)commandExecutor.execute(new AcquireTimerJobsCmd(this.asyncExecutor));
                    commandExecutor.execute(new Command<Void>() {
                        public Void execute(CommandContext commandContext) {
                            Iterator var2 = acquiredJobs.getJobs().iterator();

                            while(var2.hasNext()) {
                                TimerJobEntity job = (TimerJobEntity)var2.next();
                               // Adds the the result set to the executable job set.
                               AcquireTimerJobsRunnable.this.jobManager.moveTimerJobToExecutableJob(job);
                            }

                            return null;
                        }
                    });
                    this.millisToWait = (long)this.asyncExecutor.getDefaultTimerJobAcquireWaitTimeInMillis();
                    int jobsAcquired = acquiredJobs.size();
                    if (jobsAcquired >= this.asyncExecutor.getMaxTimerJobsPerAcquisition()) {
                        this.millisToWait = 0L;
                    }
                } catch (ActivitiOptimisticLockingException var11) {
                    if (log.isDebugEnabled()) {
                        log.debug("Optimistic locking exception during timer job acquisition. If you have multiple timer executors running against the same database, this exception means that this thread tried to acquire a timer job, which already was acquired by another timer executor acquisition thread.This is expected behavior in a clustered environment. You can ignore this message if you indeed have multiple timer executor acquisition threads running against the same database. Exception message: {}", var11.getMessage());
                    }
                } catch (Throwable var12) {
                    log.error("exception during timer job acquisition: {}", var12.getMessage(), var12);
                    this.millisToWait = (long)this.asyncExecutor.getDefaultTimerJobAcquireWaitTimeInMillis();
                }
            } while(this.millisToWait <= 0L);

            try {
                if (log.isDebugEnabled()) {
                    log.debug("timer job acquisition thread sleeping for {} millis", this.millisToWait);
                }

                Object var16 = this.MONITOR;
               // When the thread sleeps, release the object `this.MONITOR` and sleep for a while
                synchronized(this.MONITOR) {
                    if (!this.isInterrupted) {
                        this.isWaiting.set(true);
                        this.MONITOR.wait(this.millisToWait);
                    }
                }

                if (log.isDebugEnabled()) {
                    log.debug("timer job acquisition thread woke up");
                }
            } catch (InterruptedException var14) {
                if (log.isDebugEnabled()) {
                    log.debug("timer job acquisition wait interrupted");
                }
            } finally {
            	//Wake up the thread after timeout and re-hold the lock.
                this.isWaiting.set(false);
            }
        }
    }

// omit some irrelevant codes.
}


DefaultJobManager.java
public JobEntity moveTimerJobToExecutableJob(TimerJobEntity timerJob) {
        if (timerJob == null) {
            throw new ActivitiException("Empty timer job can not be scheduled");
        } else {
            JobEntity executableJob = this.createExecutableJobFromOtherJob(timerJob);
            //update the job entity and re-insert into the database and cache.
            boolean insertSuccesful = this.processEngineConfiguration.getJobEntityManager().insertJobEntity(executableJob);
            if (insertSuccesful) {
			  // delete the timer job
              this.processEngineConfiguration.getTimerJobEntityManager().delete(timerJob);
                // trigger the job execution.
                this.triggerExecutorIfNeeded(executableJob);
                return executableJob;
            } else {
                return null;
            }
        }
 }
protected void triggerExecutorIfNeeded(JobEntity jobEntity) {
        if (this.isAsyncExecutorActive()) {
            this.hintAsyncExecutor(jobEntity);
        }

}
 protected void hintAsyncExecutor(JobEntity job) {
        AsyncJobAddedNotification jobAddedNotification = new AsyncJobAddedNotification(job, this.getAsyncExecutor());
        this.getCommandContext().addCloseListener(jobAddedNotification);
}
  • 这里显示JobEntityManagerImpl重新插入数据库和缓存,我这里暂时理解为更新,如果插入成功,TimerJobEntityManager就删除作业实体,这里前面刚更新后面就删除,这里应该有区别,我可能这里说的不对,存疑!
  • triggerExecutorIfNeeded(executableJob)这里就是触发作业执行的时机,跟踪到hintAsyncExecutor , 最后一直到AsyncJobAddedNotification, 他是实现了CommandContextCloseListener , 在执行执行命令拦截器调用链结束时,最后会关闭命令上下文这是会执行命令上下问关闭监听器的closed方法。

AsyncJobAddedNotification.java
public void closed(CommandContext commandContext) {
        CommandExecutor commandExecutor = commandContext.getProcessEngineConfiguration().getCommandExecutor();
        CommandConfig commandConfig = new CommandConfig(false, TransactionPropagation.REQUIRES_NEW);
        commandExecutor.execute(commandConfig, new Command<Void>() {
            public Void execute(CommandContext commandContext) {
                if (AsyncJobAddedNotification.log.isTraceEnabled()) {
                    AsyncJobAddedNotification.log.trace("notifying job executor of new job");
                }
                
				// Submit the timed job to thread pool for execution
                AsyncJobAddedNotification.this.asyncExecutor.executeAsyncJob(AsyncJobAddedNotification.this.job);
                return null;
            }
        });
}
 protected Runnable createRunnableForJob(Job job) {
        return (Runnable)(this.executeAsyncRunnableFactory == null ? new ExecuteAsyncRunnable(job, this.processEngineConfiguration) : this.executeAsyncRunnableFactory.createExecuteAsyncRunnable(job, this.processEngineConfiguration));
    }
  • this.asyncExecutor.executeAsyncJob() : 最后兜了一圈,发现它最后还是把作业交给了线程池执行。。。
  • 而线程池执行作业是调用封装作业的Runnable对象ExecuteAsyncRunnablerun方法

线程池执行定时作业

反正最后作业都还是要走Thread Pool这一遭,接下来从Runanble对象开始扒。


ExecuteAsyncRunnable.java
public void run() {
		// make sure that job is not null. If null, query once again.
        if (this.job == null) {
            this.job = (Job)this.processEngineConfiguration.getCommandExecutor().execute(new Command<JobEntity>() {
                public JobEntity execute(CommandContext commandContext) {
                    return (JobEntity)commandContext.getJobEntityManager().findById(ExecuteAsyncRunnable.this.jobId);
                }
            });
        }
		// if not handed over to the Activiti5 Engine for handling.
        if (!this.isHandledByActiviti5Engine()) {
            boolean lockNotNeededOrSuccess = this.lockJobIfNeeded();
            if (lockNotNeededOrSuccess) {
            	// execute job
                this.executeJob();
                this.unlockJobIfNeeded();
            }

        }
}

protected void executeJob() {
        try {
       		// Execute the commands to process asynchronous jobs.
            this.processEngineConfiguration.getCommandExecutor().execute(new ExecuteAsyncJobCmd(this.jobId));
        } 
        // ignore some clause catching exception ...

 }
  • run方法中,首先确保提交过来的作业不是空作业,如果是空的得继续再通过作业实体管理器查查。正常情况下不为空,就直接提交给executeJob函数处理。
  • executeJob函数中,一行代码是核心,其他是捕获异常,这主要是执行处理异步作业的命令,这就又得去看看这个ExecuteAsyncJobCmd命令的核心函数execute干了什么活。

ExecuteAsyncJobCmd.java
public Object execute(CommandContext commandContext) {
        if (this.jobId == null) {
            throw new ActivitiIllegalArgumentException("jobId is null");
        } else {
            Job job = (Job)commandContext.getJobEntityManager().findById(this.jobId);
            if (job == null) {
                log.debug("Job does not exist anymore and will not be executed. It has most likely been deleted as part of another concurrent part of the process instance.");
                return null;
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Executing async job {}", job.getId());
                }
				// Execute the async job by DefaultJobManager
                commandContext.getJobManager().execute(job);
                if (commandContext.getEventDispatcher().isEnabled()) {
                    commandContext.getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.JOB_EXECUTION_SUCCESS, job));
                }

                return null;
            }
        }
}
  • 这里又是先检查作业是否为空,为空就重新查查,不为空就提交给DefaultJobManagerexecute来处理,和前面ExecuteAsyncRunnablerun方法的如出一辙,还是没有真正干点实际的活,都是一层层嵌套调用,处理一些附带的任务,核心业务还没出来。。。
  • 这里还干了一点活,就是在引擎中广播作业执行成功的时间JOB_EXECUTION_SUCCESS,不过实在作业管理器把作业处理完之后的事。

定时作业的执行

作业管理器

引擎的默认作业管理器是DefaultJobManager累,作业的调度添加由其门下的scheduleTimerJob处理,作业调度待执行由其门下的moveXooJobToExecutableJob处理,作业的执行由其门下的execute执行。虽然分析过程一层层嵌套,但类的职责分的还是十分清楚的,分层职责分明,但是调用栈很深。


DefaultJobManager.java
public void execute(Job job) {
		// judge the type of the job
        if (job instanceof JobEntity) {
            if ("message".equals(job.getJobType())) {
                this.executeMessageJob((JobEntity)job);
            } else if ("timer".equals(job.getJobType())) {
                // hand over to executeTimerJob function
                this.executeTimerJob((JobEntity)job);
            }

        } else {
            throw new ActivitiException("Only jobs with type JobEntity are supported to be executed");
        }
}

protected void executeTimerJob(JobEntity timerEntity) {
        TimerJobEntityManager timerJobEntityManager = this.processEngineConfiguration.getTimerJobEntityManager();
        VariableScope variableScope = null;
        // get the associated variables, if the execution to which the job currently belongs is not null.
        if (timerEntity.getExecutionId() != null) {
            variableScope = (VariableScope)this.getExecutionEntityManager().findById(timerEntity.getExecutionId());
        }

        if (variableScope == null) {
            variableScope = NoExecutionVariableScope.getSharedInstance();
        }
		// resolve and fill the `endDate` filed of the timer entity.
        this.restoreExtraData(timerEntity, (VariableScope)variableScope);
        if (timerEntity.getDuedate() != null && !this.isValidTime(timerEntity, timerEntity.getDuedate(), (VariableScope)variableScope)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Timer {} fired. but the dueDate is after the endDate.  Deleting timer.", timerEntity.getId());
            }

            this.processEngineConfiguration.getJobEntityManager().delete(timerEntity);
        } else {
       		// hand over to the `executeJobHandler` for the further handling
            this.executeJobHandler(timerEntity);
            this.processEngineConfiguration.getJobEntityManager().delete(timerEntity);
            if (logger.isDebugEnabled()) {
                logger.debug("Timer {} fired. Deleting timer.", timerEntity.getId());
            }

            if (timerEntity.getRepeat() != null) {
                TimerJobEntity newTimerJobEntity = timerJobEntityManager.createAndCalculateNextTimer(timerEntity, (VariableScope)variableScope);
                if (newTimerJobEntity != null) {
                    this.scheduleTimerJob(newTimerJobEntity);
                }
            }

        }
    }
 protected void executeJobHandler(JobEntity jobEntity) {
        ExecutionEntity execution = null;
        if (jobEntity.getExecutionId() != null) {
            execution = (ExecutionEntity)this.getExecutionEntityManager().findById(jobEntity.getExecutionId());
        }

        Map<String, JobHandler> jobHandlers = this.processEngineConfiguration.getJobHandlers();
        JobHandler jobHandler = (JobHandler)jobHandlers.get(jobEntity.getJobHandlerType());
        jobHandler.execute(jobEntity, jobEntity.getJobHandlerConfiguration(), execution, this.getCommandContext());
    }
  • execute根据作业的类型进行分流处理,定时作业交由executeTimerJob处理。
  • executeTimerJob中会先检查作业中关联的执行流Id是否 null,如果不为null则获取VariableScope实例,这里应该是从数据库中读取和TimerEntity相关的流程变量,这里是主要是通过restoreExtraData函数设置定时器实体中的EndDate , 如果fire定时作业的时间即dueDate晚于EndDate,就需要是删除定时作业。比如我启动一个循环的定时作业,每隔一段时间执行一个定时作业,但是也指定了一个失效时间EndDate,那么如果在EndDate 之后,就不需要产生新的定时作业,需要删除掉定时器。【注:粗浅理解,存疑,望大神指正
  • 如果这个定时作业是合法的,即合乎上面说的定时器失效时间限制,不过我这里不是循环定时作业,也未指定EndDate,当然没问题,接着就将作业递交给`executeJobHandler`处理。处理完成之后,就删除定时器。
  • executeJobHandler中获取引擎中配置的作业处理器,我这里测试的作业类型是trigger-timer , 对应是TriggerTimerEventJobHandler, 接下来终于到了压轴。

作业处理器

在引擎配置和初始化阶段,陪住了许多JobHandlers, 默认或者自定义,接下来就排上用场了!


TriggerTimerEventJobHandler.java
   public void execute(JobEntity job, String configuration, ExecutionEntity execution, CommandContext commandContext) {
   		//submit `trigger-execution-operation` to the agenda of the activiti engine.
        Context.getAgenda().planTriggerExecutionOperation(execution);
        if (commandContext.getEventDispatcher().isEnabled()) {
            commandContext.getEventDispatcher().dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TIMER_FIRED, job));
        }
        // omit some irrelevant codes.

    }
  • 这里就基本水落石出了,将触发执行的操作TriggerExecutionOperation提交DefaultActivitiEngineAgenda , 这里的默认流程引擎代理替代了以前的流程虚拟机(PVM) , 给发出定时触发事件TIMER_FIRED 。这里引擎代理之流程运转机制在此就不展开了,只需知道Operation提交给引擎代理后,Operation中的run方法就会排队等待执行。下面来看TriggerTimerEventJobHandler 之核心逻辑run方法。

TriggerExecutionOperation.java
   public void run() {
        FlowElement currentFlowElement = this.getCurrentFlowElement(this.execution);
        if (currentFlowElement instanceof FlowNode) {
            ActivityBehavior activityBehavior = (ActivityBehavior)((FlowNode)currentFlowElement).getBehavior();
            if (activityBehavior instanceof TriggerableActivityBehavior) {
                if (currentFlowElement instanceof BoundaryEvent) {
                    this.commandContext.getHistoryManager().recordActivityStart(this.execution);
                }
				// Recourse to the `trigger` function of the `BoundaryEventActivityBehavior` class
                ((TriggerableActivityBehavior)activityBehavior).trigger(this.execution, (String)null, (Object)null);
                if (currentFlowElement instanceof BoundaryEvent) {
                    this.commandContext.getHistoryManager().recordActivityEnd(this.execution, (String)null);
                }

            } else {
                throw new ActivitiException("Invalid behavior: " + activityBehavior + " should implement " + TriggerableActivityBehavior.class.getName());
            }
        } else {
            throw new ActivitiException("Programmatic error: no current flow element found or invalid type: " + currentFlowElement + ". Halting.");
        }
    }
  • BoundaryTimerEventActivityBehavior继承了父类是BoundaryEventActivityBehaviortrigger方法。
  • 最终触发操作会求助与边界事件的行为BoundaryTimerEventActivityBehavior对象的trigger方法,从而推动到流程的运转。

活动行为类

不管是作业处理器,还是其他什么处理器,想对流程的运转做点实质性的干预小动作Operation,最终还是要求助于活动行为类的对象。比如这里的BoundaryTimerEventActivityBehavior .


BoundaryEventActivityBehavior.java
 public void trigger(DelegateExecution execution, String triggerName, Object triggerData) {
        ExecutionEntity executionEntity = (ExecutionEntity)execution;
        CommandContext commandContext = Context.getCommandContext();
        if (this.interrupting) {
            this.executeInterruptingBehavior(executionEntity, commandContext);
        } else {
            this.executeNonInterruptingBehavior(executionEntity, commandContext);
        }

    }
  • 如果边界事件是中断式的(即:如果附加在用户任务上,就是用户任务当前执行流及其子执行流都将cancelled),就交给executeInterruptingBehavior , 反之就交给executeNonInterruptingBehavior进一步处理。

总结

  • 贴的代码有点多,但是我都附了代码的类名(在IDEA中双击shift,键如类名,就能找到类所在),跟着上述流程走一遍应该能顺清定时作业的生生死死?
  • 此处应该差一张架构图?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值