OFBiz中JOB的运行机制

OFBiz执行后台任务的类在org.ofbiz.service.job中。

JobPoller和JobInvoker是主要的两个类,一个负责查询可以执行的Job,另一个执行Job任务。

Job类图如下所示。


1.Job轮询

创建JobManager时,会创建JobPoller的一个实例。JobPoller实现了Runnable接口,以此创建线程后
通过JobManager一直轮询是否有Job需要执行,如果有奖将其放入队列中。
public synchronized void run() {
	try {
		// wait 30 seconds before the first poll
		java.lang.Thread.sleep(30000);
	} catch (InterruptedException e) {
	}

	while (isRunning) {
	  try {
		// grab a list of jobs to run.
		List<Job> pollList = jm.poll();
		//Debug.logInfo("Received poll list from JobManager [" + pollList.size() + "]", module);

		for (Job job : pollList) {
			if (job.isValid()) {
				queueNow(job);
				//Debug.logInfo("Job [" + job.getJobId() + "] is queued", module);
			}
		}
		// NOTE: using sleep instead of wait for stricter locking
                java.lang.Thread.sleep(pollWaitTime());
	  } catch (InterruptedException e) {
		Debug.logError(e, module);
		stop();
	  }
	}
}


queueNow方法将要执行job放入到队列中,如果队列中的等待执行的job数量很多,那么就创建一定数量的线程执行这些job。
public void queueNow(Job job) {
  synchronized (run) {
    run.add(job);
  }
  if (Debug.verboseOn()) Debug.logVerbose("New run queue size: " + run.size(), module);
    if (run.size() > pool.size() && pool.size() < maxThreads()) {
      synchronized (pool) {
        if (run.size() > pool.size() && pool.size() < maxThreads()) {
          int calcSize = (run.size() / jobsPerThread()) - (pool.size());
          int addSize = calcSize > maxThreads() ? maxThreads() : calcSize;
          for (int i = 0; i < addSize; i++) {
            JobInvoker iv = new JobInvoker(this, invokerWaitTime());
            pool.add(iv);
          }
        }
      }
  }
}

JobInvoker就是执行的线程,它从queue中取job并执行。JobInvoker线程不是一直运行下去,运行的时间长度超过一定的值(见serviceengine.xml中ttl的值)线程就会停止并从pool中删除。JobInvoker的run方法中job.exec()执行具体的任务。


2.Job执行

Job类都有一个exec方法,用户执行Job的service。如GenericServiceJob中的exec方法如下:
public void exec() throws InvalidJobException {
	init();
 
	// no transaction is necessary since runSync handles this
	try {
	  // get the dispatcher and invoke the service via runSync -- will run all ECAs
	  LocalDispatcher dispatcher = dctx.getDispatcher();
	  Map<String, Object> result = dispatcher.runSync(getServiceName(), getContext());
 
	  // check for a failure
	  boolean isError = ModelService.RESPOND_ERROR.equals(result.get(ModelService.RESPONSE_MESSAGE));
	  if (isError) {
		String errorMessage = (String) result.get(ModelService.ERROR_MESSAGE);
		this.failed(new Exception(errorMessage));
	  }

	  if (requester != null) {
		requester.receiveResult(result);
	  }
	} catch (Throwable t) {
	  // pass the exception back to the requester.
	  if (requester != null) {
		requester.receiveThrowable(t);
	  }
 
            // call the failed method
            this.failed(t);
        }
 
	// call the finish method
	this.finish();
}

在执行service执行,有一个init方法,在PersistedServiceJob类中init方法主要是生成下一个执行的任务,如果有的话。也即是说每一个job是由当时执行的这个job生成的,根据是什么呢?主要是两个变量:tempExprId和maxRecurrenceCount,init方法中:
TemporalExpression expr = null;
……

if (expr == null && UtilValidate.isNotEmpty(job.getString("tempExprId"))) {
	try {
              expr = TemporalExpressionWorker.getTemporalExpression(this.delegator, job.getString("tempExprId"));
        } catch (GenericEntityException e) {
                 throw new RuntimeException(e.getMessage());
	}
}

TemporalExpressionWorker里面有一个makeTemporalExpression方法很重要,从这个方法可以知道怎么配置TemporalExpression实体数据了,当然要结合TemporalExpressions类,里面定义了各种配置的细节。

tempExprTypeId有如下几种:

DateRange
DayInMonth
DayOfMonthRange
DayOfWeekRange
Difference
Frequency
Intersection
MonthRange
TimeOfDayRange
Union

比如如果希望服务只执行一次,可以如下配置:
<TemporalExpression tempExprId="RUNONCE" 
	tempExprTypeId="FREQUENCY" 
	integer1="1" integer2="1"/>
<JobSandbox jobId="CurrencyRateSynAll" jobName="Currency Rate SynAll" 
	runTime="2010-02-26 09:38:00.000" 
	serviceName="currencyRateSynAll" 
	poolId="pool" 
	runAsUser="system" 
	tempExprId="RUNONCE" 
	maxRecurrenceCount="0"/>
maxRecurrenceCount="0" 表示,不重复。tempExprTypeId="FREQUENCY" integer1="1" integer2="1"表示一年执行一次。所以总共执行一次就结束了。


每天都执行可以这样配置
<TemporalExpression tempExprId="MIDNIGHT_DAILY" 
	tempExprTypeId="TIME_OF_DAY_RANGE" 
	string1="20:00:00" string2="20:00:00"/>
<JobSandbox jobId="MailNotification" jobName="Mail Notification Job" 
	runTime="2010-02-25 18:00:00.000" 
	serviceName="mailNotificantion" 
	poolId="pool" 
	runAsUser="system" 
	tempExprId="MIDNIGHT_DAILY" 
	maxRecurrenceCount="-1"/>
maxRecurrenceCount="-1"表示无限循环下去。tempExprId="MIDNIGHT_DAILY" tempExprTypeId="TIME_OF_DAY_RANGE" string1="20:00:00" string2="20:00:00"/>表示每天晚上八点执行。


每个月一次任务可以如下配置:
<TemporalExpression tempExprId="ONCEINMONTH" 
	tempExprTypeId="FREQUENCY" 
	date1="2010-02-26 11:05:00.000" 
	integer1="2" integer2="1"/>
<JobSandbox jobId="CurrencyRateSyn" jobName="Currency Rate Syn" 
	runTime="2010-02-26 11:05:00.000" 
	serviceName="currencyRateSyn" 
	poolId="pool" 
	runAsUser="system" 
	tempExprId="ONCEINMONTH" 
	maxRecurrenceCount="-1"/>
tempExprTypeId="FREQUENCY" date1="2010-02-26 11:05:00.000" integer1="2" integer2="1"表示每月一次,时间就是date1定义的时间,如果没用定义date1,那么就是当前时间。

这里的配置相当灵活,好好掌握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值