xxl-job源码解读2-调度中心启动任务流程

项目完整解读源码地址:https://gitee.com/duhai123/xxl_job_study

XxlJobDynamicScheduler中的方法:

/**
	 * add trigger + job 增加并开始执行任务
	 * 
	 * @param jobName
	 *            任务id
	 * @param jobGroup
	 *            执行器id
	 * @param cronExpression
	 *            cron表达式
	 * @return
	 * @throws SchedulerException
	 */
	public static boolean addJob(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
		// 1、job key
		TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
		JobKey jobKey = new JobKey(jobName, jobGroup);

		// 2、valid 验证是否存在这个触发器任务
		if (scheduler.checkExists(triggerKey)) {
			return true; // PASS
		}

		// 3、corn trigger
		CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression)
				.withMisfireHandlingInstructionDoNothing(); // withMisfireHandlingInstructionDoNothing 忽略掉调度终止过程中忽略的调度
		CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
				.build();

		// 4、job detail RemoteHttpJobBean的方式执行
		Class jobClass_ = RemoteHttpJobBean.class; // Class.forName(jobInfo.getJobClass());
		JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).build();

		/*
		 * if (jobInfo.getJobData()!=null) { JobDataMap jobDataMap = jobDetail.getJobDataMap();
		 * jobDataMap.putAll(JacksonUtil.readValue(jobInfo.getJobData(), Map.class)); // JobExecutionContext
		 * context.getMergedJobDataMap().get("mailGuid"); }
		 */

		// 5、schedule job 放入到定时任务中
		Date date = scheduler.scheduleJob(jobDetail, cronTrigger);

		logger.info(">>>>>>>>>>> addJob success, jobDetail:{}, cronTrigger:{}, date:{}", jobDetail, cronTrigger, date);
		return true;
	}

xxl-job 所有的任务触发最终都是通过RemotehttpJobBean 来执行 , 该类继承关系如下: 

RemoteHttpJobBean > QuartzJobBean > Job

当quartz监听到有任务需要触发是,会调用 JobRunShell 的run方法, 在该类的run方法中,会调用当前任务的JOB_CLASS 的excute方法

/**
	 * 触发器任务执行的时候,调用这个方法
	 */
	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

		logger.info(">>>>>>>>> RemoteHttpJobBean >>>> put in JobTriggerPool...start......");

		// load jobId
		JobKey jobKey = context.getTrigger().getJobKey();
		Integer jobId = Integer.valueOf(jobKey.getName());

		// trigger 加入到触发器的线程池中
		JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null);

		logger.info(">>>>>>>>> RemoteHttpJobBean >>>> put in JobTriggerPool...end......");

	}

JobTriggerPoolHelper:job触发器线程池

/**
	 * 调用JobTriggerPoolHelper的执行任务方法
	 * 
	 * @param jobId
	 *            调度任务信息XxlJobInfo表主键
	 * @param triggerType
	 *            触发类型
	 * @param failRetryCount
	 *            >=0: use this param <0: use param from job info config
*            >=0: 用这个值 <0: 用XxlJobInfo中配置好的重试次数 * @param executorShardingParam *   执行分片的参数 * @param executorParam *            执行参数:
*            null: use job param ;not null: cover job param *            null: 用XxlJobInfo中配置好的参数 ;not null: 使用这个参数(覆盖XxlJobInfo中配置好的参数) */ public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam) { helper.addTrigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam); }

addTrigger方法:将任务提交给一个线程池(任务调度中心默认开启50个线程),在线程池中调用XxlJobTrigger.trigger

/**
	 * 将任务提交给一个线程池(任务调度中心默认开启50个线程),在线程池中调用XxlJobTrigger.trigger
	 * 
	 * @param jobId
	 *            调度任务信息XxlJobInfo表主键
	 * @param triggerType
	 *            触发类型
	 * @param failRetryCount
	 *            >=0: use this param <0: use param from job info config
*            >=0: 用这个值 <0: 用XxlJobInfo中配置好的重试次数 * @param executorShardingParam *   执行分片的参数 * @param executorParam *            执行参数:
*            null: use job param ;not null: cover job param *            null: 用XxlJobInfo中配置好的参数 ;not null: 使用这个参数(覆盖XxlJobInfo中配置好的参数) */ public void addTrigger(final int jobId, final TriggerTypeEnum triggerType, final int failRetryCount, final String executorShardingParam, final String executorParam) { triggerPool.execute(new Runnable() { @Override public void run() { XxlJobTrigger.trigger(jobId, triggerType, failRetryCount, executorShardingParam, executorParam); } }); }

XxlJobTrigger: 任务触发执行类

/**
	 * 触发任务
	 * 
	 * @param jobId
	 *            调度任务信息XxlJobInfo表主键
	 * @param triggerType
	 *            触发类型
	 * @param failRetryCount
	 *            >=0: use this param <0: use param from job info config
*            >=0: 用这个值 <0: 用XxlJobInfo中配置好的重试次数 * @param executorShardingParam *            执行分片的参数 * @param executorParam *            执行参数:
*            null: use job param ;not null: cover job param
*            null: 用XxlJobInfo中配置好的参数 ;not null: 使用这个参数(覆盖XxlJobInfo中配置好的参数) */ public static void trigger(int jobId, TriggerTypeEnum triggerType, int failRetryCount, String executorShardingParam, String executorParam) { // 查询job信息 XxlJobInfo jobInfo = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().loadById(jobId); if (jobInfo == null) { logger.warn(">>>>>>>>>>>> trigger fail, jobId invalid,jobId={}", jobId); return; } if (executorParam != null) {// 执行参数不为空,则覆盖设置执行参数 jobInfo.setExecutorParam(executorParam); } // 设置失败重试次数失败重试次数 int finalFailRetryCount = failRetryCount >= 0 ? failRetryCount : jobInfo.getExecutorFailRetryCount(); // 获取执行器信息 XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(jobInfo.getJobGroup()); // sharding param int[] shardingParam = null; if (executorShardingParam != null) { String[] shardingArr = executorShardingParam.split("/"); if (shardingArr.length == 2 && StringUtils.isNumeric(shardingArr[0]) && StringUtils.isNumeric(shardingArr[1])) { shardingParam = new int[2]; shardingParam[0] = Integer.valueOf(shardingArr[0]); shardingParam[1] = Integer.valueOf(shardingArr[1]); } } // 路由:分片广播 if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == ExecutorRouteStrategyEnum .match(jobInfo.getExecutorRouteStrategy(), null) && CollectionUtils.isNotEmpty(group.getRegistryList()) && shardingParam == null) { for (int i = 0; i < group.getRegistryList().size(); i++) { processTrigger(group, jobInfo, finalFailRetryCount, triggerType, i, group.getRegistryList().size()); } } else {// 路由:非分片广播 if (shardingParam == null) { shardingParam = new int[] { 0, 1 }; } // 执行任务 processTrigger(group, jobInfo, finalFailRetryCount, triggerType, shardingParam[0], shardingParam[1]); } }

processTrigger:

/**
	 * 执行任务
	 * 
	 * @param group
	 *            执行器信息
	 * @param jobInfo
	 *            任务信息
	 * @param finalFailRetryCount
	 *            失败重试次数失败重试次数
	 * @param triggerType
	 *            触发类型
	 * @param index
	 *            sharding index
	 * @param total
	 *            sharding index
	 */
	private static void processTrigger(XxlJobGroup group, XxlJobInfo jobInfo, int finalFailRetryCount,
			TriggerTypeEnum triggerType, int index, int total) {

		// 阻塞处理策略
		ExecutorBlockStrategyEnum blockStrategy = ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(),
				ExecutorBlockStrategyEnum.SERIAL_EXECUTION); // block strategy
		// 路由策略
		ExecutorRouteStrategyEnum executorRouteStrategyEnum = ExecutorRouteStrategyEnum
				.match(jobInfo.getExecutorRouteStrategy(), null); // route strategy

		// 路由策略是否是分片广播
		String shardingParam = (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum)
				? String.valueOf(index).concat("/").concat(String.valueOf(total)) : null;

		// ----------------执行前进行的操作-----------------------TODO--------

		// 1、save log-id
		XxlJobLog jobLog = new XxlJobLog();
		jobLog.setJobGroup(jobInfo.getJobGroup());
		jobLog.setJobId(jobInfo.getId());
		jobLog.setTriggerTime(new Date());
		// 保存日志
		XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().save(jobLog);
		logger.debug(">>>>>>>>>>> xxl-job trigger start, jobId:{}", jobLog.getId());

		// 2、init trigger-param===初始化设置触发器参数
		TriggerParam triggerParam = new TriggerParam();
		triggerParam.setJobId(jobInfo.getId());
		triggerParam.setExecutorHandler(jobInfo.getExecutorHandler());
		triggerParam.setExecutorParams(jobInfo.getExecutorParam());
		triggerParam.setExecutorBlockStrategy(jobInfo.getExecutorBlockStrategy());
		triggerParam.setExecutorTimeout(jobInfo.getExecutorTimeout());
		triggerParam.setLogId(jobLog.getId());
		triggerParam.setLogDateTim(jobLog.getTriggerTime().getTime());
		triggerParam.setGlueType(jobInfo.getGlueType());
		triggerParam.setGlueSource(jobInfo.getGlueSource());
		triggerParam.setGlueUpdatetime(jobInfo.getGlueUpdatetime().getTime());
		triggerParam.setBroadcastIndex(index);
		triggerParam.setBroadcastTotal(total);

		// 3、init address(根据路由策略执行器地址)
		String address = null;
		ReturnT routeAddressResult = null;

		// 执行器地址列表不为空
		if (CollectionUtils.isNotEmpty(group.getRegistryList())) {

			// 分片广播
			if (ExecutorRouteStrategyEnum.SHARDING_BROADCAST == executorRouteStrategyEnum) {
				if (index < group.getRegistryList().size()) {
					address = group.getRegistryList().get(index);
				} else {
					address = group.getRegistryList().get(0);
				}
			} else {// 其他方式
				// 此处使用了策略模式, 根据不同的策略 使用不同的实现类 TODO
				routeAddressResult = executorRouteStrategyEnum.getRouter().route(triggerParam, group.getRegistryList());

				if (routeAddressResult.getCode() == ReturnT.SUCCESS_CODE) {
					address = routeAddressResult.getContent();
					logger.debug(">>>>>>>>>>> xxl-job trigger excute address, address:{}", address);

				}
			}
		} else {

			routeAddressResult = new ReturnT(ReturnT.FAIL_CODE,
					I18nUtil.getString("jobconf_trigger_address_empty"));
		}

		// 4、trigger remote executor 调用执行器远程执行
		ReturnT triggerResult = null;
		if (address != null) {
			// ---- 调用执行器远程执行 ----- TODO --
			triggerResult = runExecutor(triggerParam, address);
		} else {
			triggerResult = new ReturnT(ReturnT.FAIL_CODE, null);
		}

		// 5、collection trigger info 触发执行后的消息
		StringBuffer triggerMsgSb = new StringBuffer();
		triggerMsgSb.append(I18nUtil.getString("jobconf_trigger_type")).append(":").append(triggerType.getTitle());
		triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_admin_adress")).append(":") .append(IpUtil.getIp()); triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regtype")).append(":") .append((group.getAddressType() == 0) ? I18nUtil.getString("jobgroup_field_addressType_0") : I18nUtil.getString("jobgroup_field_addressType_1")); triggerMsgSb.append("
").append(I18nUtil.getString("jobconf_trigger_exe_regaddress")).append(":") .append(group.getRegistryList()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorRouteStrategy")).append(":") .append(executorRouteStrategyEnum.getTitle()); if (shardingParam != null) { triggerMsgSb.append("(" + shardingParam + ")"); } triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorBlockStrategy")).append(":") .append(blockStrategy.getTitle()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_timeout")).append(":") .append(jobInfo.getExecutorTimeout()); triggerMsgSb.append("
").append(I18nUtil.getString("jobinfo_field_executorFailRetryCount")).append(":") .append(finalFailRetryCount); triggerMsgSb .append("

>>>>>>>>>>>" + I18nUtil.getString("jobconf_trigger_run") + "<<<<<<<<<<<
") .append((routeAddressResult != null && routeAddressResult.getMsg() != null) ? routeAddressResult.getMsg() + "

" : "") .append(triggerResult.getMsg() != null ? triggerResult.getMsg() : ""); // 6、save log trigger-info 修改触发器日志信息 jobLog.setExecutorAddress(address); jobLog.setExecutorHandler(jobInfo.getExecutorHandler()); jobLog.setExecutorParam(jobInfo.getExecutorParam()); jobLog.setExecutorShardingParam(shardingParam); jobLog.setExecutorFailRetryCount(finalFailRetryCount); // jobLog.setTriggerTime(); jobLog.setTriggerCode(triggerResult.getCode()); jobLog.setTriggerMsg(triggerMsgSb.toString());// 修改日志触发信息 // 修改日志触发信息 XxlJobAdminConfig.getAdminConfig().getXxlJobLogDao().updateTriggerInfo(jobLog); // 7、monitor trigger // 最后:这个触发器任务正在执行,放到队列里面 // 要么执行器回调后被操作,要么超时被失败日志监控线程操作 JobFailMonitorHelper.monitor(jobLog.getId()); logger.debug(">>>>>>>>>>> xxl-job trigger end, jobId:{}", jobLog.getId()); }

runExecutor:调用执行器执行该触发器任务

/**
	 * 调用执行器执行该触发器任务 run executor
	 * 
	 * @param triggerParam
	 *            触发器参数(调用执行器进行执行)
	 * @param address
	 *            执行器地址
	 * @return
	 */
	public static ReturnT runExecutor(TriggerParam triggerParam, String address) {
		ReturnT runResult = null;
		try {

			// 创建一个ExcutorBiz 的对象,重点在这个方法里面
			ExecutorBiz executorBiz = XxlJobDynamicScheduler.getExecutorBiz(address);
			// 这个run 方法不会最终执行,仅仅只是为了触发 proxy object 的 invoke方法,
			// 同时将目标的类型传送给执行器端, 因为在代理对象的invoke的方法里面没有执行目标对象的方法
			runResult = executorBiz.run(triggerParam);

		} catch (Exception e) {
			logger.error(">>>>>>>>>>> xxl-job trigger error, please check if the executor[{}] is running.", address, e);
			runResult = new ReturnT(ReturnT.FAIL_CODE, ThrowableUtil.toString(e));
		}

		StringBuffer runResultSB = new StringBuffer(I18nUtil.getString("jobconf_trigger_run") + ":");
		runResultSB.append("
address:").append(address); runResultSB.append("
code:").append(runResult.getCode()); runResultSB.append("
msg:").append(runResult.getMsg()); runResult.setMsg(runResultSB.toString()); return runResult; }

 XxlJobDynamicScheduler.getExecutorBiz (address) , 通过机器地址,获取一个executor

// -------------- executor-client -----执行器client(用这个去调用执行器的接口)---TODO-------------
	// 执行器client的map
	private static ConcurrentHashMap executorBizRepository = new ConcurrentHashMap();

	/**
	 * 获取执行器client
	 * 
	 * @param address
	 * @return
	 * @throws Exception
	 */
	public static ExecutorBiz getExecutorBiz(String address) throws Exception {
		// valid
		if (address == null || address.trim().length() == 0) {
			return null;
		}

		// 执行器client的map 中获取
		address = address.trim();
		// 查看缓存里面是否存在,如果存在则不需要再去创建executorBiz了
		ExecutorBiz executorBiz = executorBizRepository.get(address);
		if (executorBiz != null) {
			return executorBiz;
		}

		// 创建ExecutorBiz的代理对象
		executorBiz = (ExecutorBiz) new XxlRpcReferenceBean(NetEnum.JETTY,
				Serializer.SerializeEnum.HESSIAN.getSerializer(), CallType.SYNC, ExecutorBiz.class, null, 10000,
				address, XxlJobAdminConfig.getAdminConfig().getAccessToken(), null).getObject();

		// 放入执行器client的map
		executorBizRepository.put(address, executorBiz);
		return executorBiz;
	}

XxlRpcReferenceBean:xxl-rpc项目可以仔细研究源代码,这里我们不深究,但还是大约看看是怎么RPC通过远程调用的

public Object getObject() {
		return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { iface },
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						String className = method.getDeclaringClass().getName();

						// filter method like "Object.toString()"
						if (Object.class.getName().equals(className)) {
							logger.info(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}.{}]", className,
									method.getName());
							throw new XxlRpcException("xxl-rpc proxy class-method not support");
						}

						// address
						String address = routeAddress();
						if (address == null || address.trim().length() == 0) {
							throw new XxlRpcException("xxl-rpc reference bean[" + className + "] address empty");
						}

						// request
						// 创建request信息, 发送HTTP请求到执行器服务器上。
						XxlRpcRequest xxlRpcRequest = new XxlRpcRequest();
						xxlRpcRequest.setRequestId(UUID.randomUUID().toString());
						// 创建时间, 用于判断请求是否超时
						xxlRpcRequest.setCreateMillisTime(System.currentTimeMillis());
						// 数据校验
						xxlRpcRequest.setAccessToken(accessToken);
						// 将目标类的class名称传给执行器,让那边来创建对象,并执行逻辑代码
						xxlRpcRequest.setClassName(className);
						// 方法名称为run
						xxlRpcRequest.setMethodName(method.getName());
						// 参数类型
						xxlRpcRequest.setParameterTypes(method.getParameterTypes());
						// 参数
						xxlRpcRequest.setParameters(args);

						// send
						if (CallType.SYNC == callType) {
							try {
								// future set
								XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(xxlRpcRequest, null);

								// do invoke 发送执行方法请求
								client.asyncSend(address, xxlRpcRequest);

								// future get
								XxlRpcResponse xxlRpcResponse = futureResponse.get(timeout, TimeUnit.MILLISECONDS);
								if (xxlRpcResponse.getErrorMsg() != null) {
									throw new XxlRpcException(xxlRpcResponse.getErrorMsg());
								}
								return xxlRpcResponse.getResult();
							} catch (Exception e) {
								logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address,
										xxlRpcRequest);

								throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
							} finally {
								// remove-InvokerFuture
								XxlRpcFutureResponseFactory.removeInvokerFuture(xxlRpcRequest.getRequestId());
							}
						} else if (CallType.FUTURE == callType) {

							// thread future set
							XxlRpcInvokeFuture invokeFuture = null;
							try {
								// future set
								invokeFuture = new XxlRpcInvokeFuture(new XxlRpcFutureResponse(xxlRpcRequest, null));
								XxlRpcInvokeFuture.setFuture(invokeFuture);

								// do invoke
								client.asyncSend(address, xxlRpcRequest);

								return null;
							} catch (Exception e) {
								logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address,
										xxlRpcRequest);

								// remove-InvokerFuture
								invokeFuture.stop();

								throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
							}

						} else if (CallType.CALLBACK == callType) {

							// get callback
							XxlRpcInvokeCallback finalInvokeCallback = invokeCallback;
							XxlRpcInvokeCallback threadInvokeCallback = XxlRpcInvokeCallback.getCallback();
							if (threadInvokeCallback != null) {
								finalInvokeCallback = threadInvokeCallback;
							}
							if (finalInvokeCallback == null) {
								throw new XxlRpcException("xxl-rpc XxlRpcInvokeCallback(CallType="
										+ CallType.CALLBACK.name() + ") cannot be null.");
							}

							try {
								// future set
								XxlRpcFutureResponse futureResponse = new XxlRpcFutureResponse(xxlRpcRequest,
										finalInvokeCallback);

								client.asyncSend(address, xxlRpcRequest);
							} catch (Exception e) {
								logger.info(">>>>>>>>>>> xxl-job, invoke error, address:{}, XxlRpcRequest{}", address,
										xxlRpcRequest);

								// future remove
								XxlRpcFutureResponseFactory.removeInvokerFuture(xxlRpcRequest.getRequestId());

								throw (e instanceof XxlRpcException) ? e : new XxlRpcException(e);
							}

							return null;
						} else if (CallType.ONEWAY == callType) {
							client.asyncSend(address, xxlRpcRequest);
							return null;
						} else {
							throw new XxlRpcException("xxl-rpc callType[" + callType + "] invalid");
						}

					}
				});
	}

你可能感兴趣的:(xxl-job)