Browse Source

Merge pull request #293 from liuluo129/feature/timeout

“添加任务超时属性,超时后失败”

赞,这个特性十分实用!
许雪里 6 years ago
parent
commit
9de5da098b

+ 1 - 0
doc/db/tables_xxl_job.sql

@@ -158,6 +158,7 @@ CREATE TABLE `XXL_JOB_QRTZ_TRIGGER_INFO` (
   `update_time` datetime DEFAULT NULL,
   `author` varchar(64) DEFAULT NULL COMMENT '作者',
   `alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
+  `execute_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间',
   `executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
   `executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
   `executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',

+ 11 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/model/XxlJobInfo.java

@@ -36,6 +36,17 @@ public class XxlJobInfo {
 	// copy from quartz
 	private String jobStatus;		// 任务状态 【base on quartz】
 
+	private int executeTimeout;     // 任务最多执行时间,超时后报警
+
+	public int getExecuteTimeout() {
+		return executeTimeout;
+	}
+
+	public void setExecuteTimeout(int executeTimeout) {
+		this.executeTimeout = executeTimeout;
+	}
+
+
 	public int getId() {
 		return id;
 	}

+ 3 - 0
xxl-job-admin/src/main/java/com/xxl/job/admin/core/trigger/XxlJobTrigger.java

@@ -96,6 +96,8 @@ public class XxlJobTrigger {
                     triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
                     triggerParam.setBroadcastIndex(i);
                     triggerParam.setBroadcastTotal(addressList.size()); // update02
+                    // 执行超时时间
+                    triggerParam.setExecuteTimeout(jobInfo.getExecuteTimeout());
 
                     // 4.2、trigger-run (route run / trigger remote executor)
                     triggerResult = runExecutor(triggerParam, address);     // update03
@@ -164,6 +166,7 @@ public class XxlJobTrigger {
                 triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
                 triggerParam.setBroadcastIndex(0);
                 triggerParam.setBroadcastTotal(1);
+                triggerParam.setExecuteTimeout(jobInfo.getExecuteTimeout());
 
                 // 4.2、trigger-run (route run / trigger remote executor)
                 triggerResult = executorRouteStrategyEnum.getRouter().routeRun(triggerParam, addressList);

+ 2 - 0
xxl-job-admin/src/main/resources/i18n/message.properties

@@ -107,6 +107,7 @@ jobinfo_field_gluetype=运行模式
 jobinfo_field_executorparam=任务参数
 jobinfo_field_cron_unvalid=Cron格式非法
 jobinfo_field_author=负责人
+jobinfo_field_timeout=最大执行时间
 jobinfo_field_alarmemail=报警邮件
 jobinfo_field_alarmemail_placeholder=请输入报警邮件,多个邮件地址则逗号分隔
 jobinfo_field_executorRouteStrategy=路由策略
@@ -157,6 +158,7 @@ joblog_clean_type_9=清理所有日志数据
 joblog_clean_type_unvalid=清理类型参数异常
 joblog_handleCode_200=成功
 joblog_handleCode_500=失败
+joblog_handleCode_400=超时
 joblog_handleCode_501=失败重试
 joblog_kill_log=终止任务
 joblog_kill_log_limit=调度失败,无法终止日志

+ 2 - 0
xxl-job-admin/src/main/resources/i18n/message_en.properties

@@ -103,6 +103,7 @@ jobinfo_field_update=Edit Job
 jobinfo_field_id=Job ID
 jobinfo_field_jobgroup=Executor
 jobinfo_field_jobdesc=Job description
+jobinfo_field_timeout=Max execute time
 jobinfo_field_gluetype=GLUE Type
 jobinfo_field_executorparam=Param
 jobinfo_field_cron_unvalid=The Cron is illegal
@@ -157,6 +158,7 @@ joblog_clean_type_9=Clean up all log data
 joblog_clean_type_unvalid=Clean type is illegal
 joblog_handleCode_200=Success
 joblog_handleCode_500=Fail
+joblog_handleCode_400=Timeout
 joblog_handleCode_501=Fail retry
 joblog_kill_log=Kill Job
 joblog_kill_log_limit=Trigger Fail, can not kill job

+ 20 - 15
xxl-job-admin/src/main/resources/mybatis-mapper/XxlJobInfoMapper.xml

@@ -1,18 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.xxl.job.admin.dao.XxlJobInfoDao">
-	
+
 	<resultMap id="XxlJobInfo" type="com.xxl.job.admin.core.model.XxlJobInfo" >
 		<result column="id" property="id" />
-	
+
 		<result column="job_group" property="jobGroup" />
 	    <result column="job_cron" property="jobCron" />
 	    <result column="job_desc" property="jobDesc" />
-	    
+
 	    <result column="add_time" property="addTime" />
 	    <result column="update_time" property="updateTime" />
-	    
+
 	    <result column="author" property="author" />
 	    <result column="alarm_email" property="alarmEmail" />
 
@@ -21,11 +21,12 @@
 	    <result column="executor_param" property="executorParam" />
 		<result column="executor_block_strategy" property="executorBlockStrategy" />
 		<result column="executor_fail_strategy" property="executorFailStrategy" />
-	    
+
 	    <result column="glue_type" property="glueType" />
 	    <result column="glue_source" property="glueSource" />
 	    <result column="glue_remark" property="glueRemark" />
 		<result column="glue_updatetime" property="glueUpdatetime" />
+		<result column="execute_timeout" property="executeTimeout" />
 
 		<result column="child_jobid" property="childJobId" />
 	</resultMap>
@@ -48,9 +49,10 @@
 		t.glue_source,
 		t.glue_remark,
 		t.glue_updatetime,
+		t.execute_timeout,
 		t.child_jobid
 	</sql>
-	
+
 	<select id="pageList" parameterType="java.util.HashMap" resultMap="XxlJobInfo">
 		SELECT <include refid="Base_Column_List" />
 		FROM XXL_JOB_QRTZ_TRIGGER_INFO AS t
@@ -68,7 +70,7 @@
 		ORDER BY id DESC
 		LIMIT #{offset}, #{pagesize}
 	</select>
-	
+
 	<select id="pageListCount" parameterType="java.util.HashMap" resultType="int">
 		SELECT count(1)
 		FROM XXL_JOB_QRTZ_TRIGGER_INFO AS t
@@ -103,10 +105,11 @@
 			glue_source,
 			glue_remark,
 			glue_updatetime,
-			child_jobid
+			child_jobid,
+		    execute_timeout
 		) VALUES (
 			#{jobGroup},
-			#{jobCron}, 
+			#{jobCron},
 			#{jobDesc},
 			NOW(),
 			NOW(),
@@ -121,7 +124,8 @@
 			#{glueSource},
 			#{glueRemark},
 			NOW(),
-			#{childJobId}
+			#{childJobId},
+		    #{executeTimeout}
 		);
 		<!--<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
 			SELECT LAST_INSERT_ID()
@@ -134,10 +138,10 @@
 		FROM XXL_JOB_QRTZ_TRIGGER_INFO AS t
 		WHERE t.id = #{id}
 	</select>
-	
+
 	<update id="update" parameterType="com.xxl.job.admin.core.model.XxlJobInfo" >
 		UPDATE XXL_JOB_QRTZ_TRIGGER_INFO
-		SET 
+		SET
 			job_cron = #{jobCron},
 			job_desc = #{jobDesc},
 			update_time = NOW(),
@@ -152,10 +156,11 @@
 			glue_source = #{glueSource},
 			glue_remark = #{glueRemark},
 			glue_updatetime = #{glueUpdatetime},
-			child_jobid = #{childJobId}
+			child_jobid = #{childJobId},
+			execute_timeout = ${executeTimeout}
 		WHERE id = #{id}
 	</update>
-	
+
 	<delete id="delete" parameterType="java.util.HashMap">
 		DELETE
 		FROM XXL_JOB_QRTZ_TRIGGER_INFO

+ 5 - 0
xxl-job-admin/src/main/webapp/WEB-INF/template/jobinfo/jobinfo.index.ftl

@@ -169,6 +169,11 @@
 						<div class="col-sm-4"><input type="text" class="form-control" name="alarmEmail" placeholder="${I18n.jobinfo_field_alarmemail_placeholder}" maxlength="100" ></div>
 					</div>
 
+					<div class="form-group">
+						<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_timeout}<font color="red">*</font></label>
+						<div class="col-sm-4"><input type="text" class="form-control" name="executeTimeout" placeholder="0" maxlength="100" ></div>
+					</div>
+
                     <hr>
 					<div class="form-group">
 						<div class="col-sm-offset-3 col-sm-6">

+ 1 - 0
xxl-job-admin/src/main/webapp/static/js/jobinfo.index.1.js

@@ -362,6 +362,7 @@ $(function() {
 		$("#updateModal .form input[name='jobCron']").val( row.jobCron );
 		$("#updateModal .form input[name='author']").val( row.author );
 		$("#updateModal .form input[name='alarmEmail']").val( row.alarmEmail );
+		$("#updateModal .form input[name='executeTimeout']").val( row.executeTimeout );
 		$('#updateModal .form select[name=executorRouteStrategy] option[value='+ row.executorRouteStrategy +']').prop('selected', true);
 		$("#updateModal .form input[name='executorHandler']").val( row.executorHandler );
 		$("#updateModal .form input[name='executorParam']").val( row.executorParam );

+ 2 - 0
xxl-job-admin/src/main/webapp/static/js/joblog.index.1.js

@@ -159,6 +159,8 @@ $(function() {
                                 html = '<span style="color: red">'+ I18n.joblog_handleCode_500 +'</span>';
                             } else if (data == 501) {
                                 html = '<span style="color: red">'+ I18n.joblog_handleCode_501 +'</span>';
+                            } else if (data == 400) {
+                                html = '<span style="color: red">'+ I18n.joblog_handleCode_400 +'</span>';
                             } else if (data == 0) {
                                 html = '';
                             }

+ 3 - 0
xxl-job-core/src/main/java/com/xxl/job/core/biz/model/ReturnT.java

@@ -12,8 +12,11 @@ public class ReturnT<T> implements Serializable {
 
 	public static final int SUCCESS_CODE = 200;
 	public static final int FAIL_CODE = 500;
+	public static final int EXECUTE_TIMEOUT = 400;
+
 	public static final ReturnT<String> SUCCESS = new ReturnT<String>(null);
 	public static final ReturnT<String> FAIL = new ReturnT<String>(FAIL_CODE, null);
+	public static final ReturnT<String> TIMEOUT = new ReturnT<String>(EXECUTE_TIMEOUT, "执行超时");
 	
 	private int code;
 	private String msg;

+ 10 - 0
xxl-job-core/src/main/java/com/xxl/job/core/biz/model/TriggerParam.java

@@ -24,6 +24,16 @@ public class TriggerParam implements Serializable{
     private int broadcastIndex;
     private int broadcastTotal;
 
+    private int executeTimeout;
+
+    public int getExecuteTimeout() {
+        return executeTimeout;
+    }
+
+    public void setExecuteTimeout(int executeTimeout) {
+        this.executeTimeout = executeTimeout;
+    }
+
     public int getJobId() {
         return jobId;
     }

+ 2 - 0
xxl-job-core/src/main/java/com/xxl/job/core/handler/IJobHandler.java

@@ -14,6 +14,8 @@ public abstract class IJobHandler {
 	public static final ReturnT<String> SUCCESS = new ReturnT<String>(200, null);
 	/** fail */
 	public static final ReturnT<String> FAIL = new ReturnT<String>(500, null);
+	/** timeout */
+	public static final ReturnT<String> TIMEOUT = new ReturnT<String>(400, null);
 	/** fail retry */
 	public static final ReturnT<String> FAIL_RETRY = new ReturnT<String>(501, null);
 

+ 29 - 1
xxl-job-core/src/main/java/com/xxl/job/core/thread/JobThread.java

@@ -15,11 +15,15 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Collections;
 import java.util.Date;
+
+import java.util.concurrent.*;
+
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
+
 /**
  * handler thread
  * @author xuxueli 2016-1-16 19:52:47
@@ -107,6 +111,7 @@ public class JobThread extends Thread{
 
             TriggerParam triggerParam = null;
             ReturnT<String> executeResult = null;
+			ExecutorService singleThread = Executors.newSingleThreadExecutor();
             try {
 				// to check toStop signal, we need cycle, so wo cannot use queue.take(), instand of poll(timeout)
 				triggerParam = triggerQueue.poll(3L, TimeUnit.SECONDS);
@@ -122,7 +127,27 @@ public class JobThread extends Thread{
 
 					// execute
 					XxlJobLogger.log("<br>----------- xxl-job job execute start -----------<br>----------- Param:" + triggerParam.getExecutorParams());
-					executeResult = handler.execute(triggerParam.getExecutorParams());
+					int executeTimeout = triggerParam.getExecuteTimeout();
+
+
+					final TriggerParam finalTriggerParam = triggerParam;
+
+					Future<ReturnT<String>> future = singleThread.submit(new Callable<ReturnT<String>>() {
+						@Override
+						public ReturnT<String> call() throws Exception {
+							return handler.execute(finalTriggerParam.getExecutorParams());
+						}
+					});
+
+					try {
+						if (executeTimeout > 0) {
+							executeResult = future.get(executeTimeout, TimeUnit.SECONDS);
+						} else {
+							executeResult = future.get();
+						}
+					} catch (TimeoutException timeoutException) {
+						executeResult = ReturnT.TIMEOUT;
+					}
 					if (executeResult == null) {
 						executeResult = IJobHandler.FAIL;
 					}
@@ -145,6 +170,9 @@ public class JobThread extends Thread{
 
 				XxlJobLogger.log("<br>----------- JobThread Exception:" + errorMsg + "<br>----------- xxl-job job execute end(error) -----------");
 			} finally {
+            	if (singleThread != null) {
+            		singleThread.shutdown();
+				}
                 if(triggerParam != null) {
                     // callback handler info
                     if (!toStop) {