/** * do callback, will retry if error * @param callbackParamList */ private void doCallback(List <HandleCallbackParam> callbackParamList) { // callback, will retry if error foreach (AdminBiz adminBiz in XxlJobExecutor.getAdminBizList()) { try { ReturnT <String> callbackResult = adminBiz.callback(callbackParamList); if (callbackResult != null && ReturnT <string> .SUCCESS_CODE == callbackResult.code) { callbackResult = ReturnT <string> .SUCCESS; logger.Info(string.Format(">>>>>>>>>>> xxl-job callback success, callbackParamList:{0}, callbackResult:{1}", callbackParamList, callbackResult)); break; } else { logger.Info(string.Format(">>>>>>>>>>> xxl-job callback fail, callbackParamList:{0}, callbackResult:{1}", callbackParamList, callbackResult)); } } catch (Exception e) { logger.Error(string.Format(">>>>>>>>>>> xxl-job callback error, callbackParamList:{0}", callbackParamList), e); //getInstance().callBackQueue.addAll(callbackParamList); } } }
private async Task DoCallBack() { List <HandleCallbackParam> list = new List <HandleCallbackParam>(); while (list.Count < Constants.MaxCallbackRecordsPerRequest && this.taskQueue.TryDequeue(out var item)) { list.Add(item); } if (!list.Any()) { return; } ReturnT result; try { result = await this._adminClient.Callback(list); } catch (Exception ex) { this._logger.LogError(ex, "trigger callback error:{error}", ex.Message); result = ReturnT.Failed(ex.Message); this._retryQueue.Push(list); } LogCallBackResult(result, list); }
private async Task DoCallBack() { List <HandleCallbackParam> list = new List <HandleCallbackParam>(); if (!taskQueue.TryDequeue(out var item)) { return; } list.Add(item); ReturnT result; try { result = await _adminClient.Callback(list).ConfigureAwait(false); } catch (Exception ex) { _logger.LogError(ex, "trigger callback error:{error}", ex.Message); result = ReturnT.Failed(ex.Message); _retryQueue.Push(list); } LogCallBackResult(result, list); }
private void LogCallBackResult(ReturnT result, List <HandleCallbackParam> list) { foreach (var param in list) { this._jobLogger.LogSpecialFile(param.LogDateTime, param.LogId, result.Msg ?? "Success"); } }
/// <summary> /// 查看执行日志 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <object> HandleLog(HttpContext context) { var res = ReturnT.Success(); try { var info = await context.Request.FromBody <JobGetLogRequest>(); _logger.LogInformation($"--------------查看执行日志{info?.logId}--------------"); res.Content = new { fromLineNum = 0, // 本次请求,日志开始行数 toLineNum = 100, // 本次请求,日志结束行号 logContent = "暂无日志", // 本次请求日志内容 isEnd = true // 日志是否全部加载完 }; await Task.CompletedTask; } catch (Exception ex) { res = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob查看执行日志错误"); } return(res); }
/// <summary> /// 任务结果回调 回调结果状态不是200 就会再次重试该任务(配置了重试次数). ///如果没有回调结果,在当前执行器服务关闭后且10分钟后,自动标识失败 也会重试的(配置了重试次数) /// </summary> /// <param name="logId"></param> /// <returns></returns> public async Task CallBackOld(int logId, ReturnT executeResult) { //todo: 回调任务改为多次重试的,保证回调成功,定时任务量一般不会很大,这里可以内存处理重试机制 // var action = ""; //在action中判断重试次数,不到继续加入队列 // _taskExecutor.GetSingleThreadTaskExecutor(logId).Execute(action); try { var calbackBody = new { logId = logId, logDateTime = DateUtils.GetTimeStamp(), executeResult = new { code = executeResult.Code, msg = executeResult.Msg } }; var header = new Dictionary <string, string>(); header.Add(XxlJobConstant.Token, _xxlJobOption.Token); var res = await _httpClientFactory.CreateClient().PostJsonAsync <ReturnT>(_xxlJobOption.XxlJobAdminUrl.AppendUrlPath("api/callback"), new object[] { calbackBody }, header); if (res.Code != ReturnT.SUCCESS_CODE) { _logger.LogError($"xxljob任务结果回调失败,logId:{logId},{res.Code},{res.Msg}"); } } catch (Exception ex) { _logger.LogError(ex, "xxljob任务结果回调错误"); } }
/// <summary> /// 忙碌检测 路由策略选择忙碌转移时触发 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <object> HandleIdleBeat(HttpContext context) { var res = ReturnT.Success(); try { var info = await context.Request.FromBody <JobIdleBeatRequest>(); //_logger.LogInformation("--------------忙碌检测--------------"); if (info != null) { var isRunning = _xxlJobExecutor.IsRunningOrHasQueue(info.jobId); if (isRunning) { return(ReturnT.Failed("任务执行中")); } } await Task.CompletedTask; } catch (Exception ex) { res = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob忙碌检测错误"); } return(res); }
/// <summary> /// 终止任务 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <object> HandleKill(HttpContext context) { var res = ReturnT.Failed("job has been executed or is executing");//This feature is not supported try { var info = await context.Request.FromBody <JobKillRequest>(); _logger.LogInformation($"--------------终止任务{info?.jobId}--------------"); if (info != null) { var success = _xxlJobExecutor.KillJob(info.jobId, "终止任务[调度中心主动停止任务]"); if (success) { return(ReturnT.Success()); } } await Task.CompletedTask; } catch (Exception ex) { res = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob终止任务错误"); } return(res); }
/// <summary> /// 异步执行任务 把任务插入线程任务队列中排队执行 /// </summary> /// <param name="jobInfo"></param> /// <param name="jobHandler"></param> /// <returns></returns> private async Task AsyncExecuteJob(JobRunRequest jobInfo, IJobHandler jobHandler) { Func <object, Task> action = async(state) => { if (jobInfo.JobStatus == JobStatus.Killed) //已终止的任务 就不要再运行了 { _logger.LogInformation($"**************该任务已被终止 {jobInfo.jobId},{jobInfo.logId}********************"); return; } jobInfo.SetRunning(); ReturnT executeResult = null; try { executeResult = await jobHandler.Execute(new JobExecuteContext(jobInfo.logId, jobInfo.executorParams)); } catch (Exception ex) { executeResult = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob执行任务错误"); } _xxlJobExecutor.RemoveJobInfo(jobInfo); await CallBack(jobInfo.logId, executeResult); //这里保证一定要回调结果 不然就要重试了(配置了重试次数),这里回调为失败结果也会重试(配置了重试次数) }; _xxlJobExecutor.RegistJobInfo(jobInfo); //插入任务执行队列中 根据jobid路由到固定线程中 保证同一个jobid串行执行 _taskExecutor.GetSingleThreadTaskExecutor(jobInfo.jobId).Execute(action, null); await Task.CompletedTask; }
/// <summary> /// 说明:触发任务执行 ///------ ///地址格式:{执行器内嵌服务跟地址}/run ///Header: /// XXL-JOB-ACCESS-TOKEN : {请求令牌} ///请求数据格式如下,放置在 RequestBody 中,JSON格式: /// { /// "jobId":1, // 任务ID /// "executorHandler":"demoJobHandler", // 任务标识 /// "executorParams":"demoJobHandler", // 任务参数 /// "executorBlockStrategy":"COVER_EARLY", // 任务阻塞策略,可选值参考 com.xxl.job.core.enums.ExecutorBlockStrategyEnum /// "executorTimeout":0, // 任务超时时间,单位秒,大于零时生效 /// "logId":1, // 本次调度日志ID /// "logDateTime":1586629003729, // 本次调度日志时间 /// "glueType":"BEAN", // 任务模式,可选值参考 com.xxl.job.core.glue.GlueTypeEnum /// "glueSource":"xxx", // GLUE脚本代码 /// "glueUpdatetime":1586629003727, // GLUE脚本更新时间,用于判定脚本是否变更以及是否需要刷新 /// "broadcastIndex":0, // 分片参数:当前分片 /// "broadcastTotal":0 // 分片参数:总分片 /// } ///响应数据格式: /// { /// "code": 200, // 200 表示正常、其他失败 /// "msg": null // 错误提示消息 /// } /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <IActionResponse> ActionAsync(HttpContext context) { var jobInfo = await context.Request.FromBody <JobRunRequest>(); // 如果有积压任务,丢弃当前任务 if (jobInfo.ExecutorBlockStrategy == XxlJobConstant.EXECUTORBLOCKSTRATEGY_DISCARD_LATER) { if (await _dispatcher.Exist(jobInfo.JobId)) { return(await context.JsonAsync(ReturnT.Failed("终止任务[执行策略: DISCARD_LATER]"))); } } // 覆盖之前调度负载之前积压的任务 else if (jobInfo.ExecutorBlockStrategy == XxlJobConstant.EXECUTORBLOCKSTRATEGY_COVER_EARLY) { if (await _dispatcher.Exist(jobInfo.JobId)) { await _dispatcher.Kill(jobInfo.JobId, "终止任务[执行策略: COVER_EARLY]"); } } await _dispatcher.Enqueue(new JobMessage(jobInfo)); // action return(await context.JsonAsync(ReturnT.Success())); }
public async Task <ReturnT> Execute(JobMessage job) { var handler = await _handlerProvider.GetInstanceAsync(job.ExecutorHandler); if (handler == null) { return(ReturnT.Failed($"没有找到名为{job.ExecutorHandler}的JobHandler")); } // if killed if (job.Status == JobStatus.Killed) { return(ReturnT.Failed($"任务已被终止,id:{job.Id}, reason: {job.Reason}, logId: {job.LogId}")); } job.Run(); try { return(await handler.Execute(new JobExecuteContext(job.Id, job.ExecutorParams))); } catch (Exception e) { _logger.LogError(e, "xxljob执行任务错误"); return(ReturnT.Failed(e.Message)); } }
private ReturnT IdleBeat(int jobId) { JobThread jobThread = _jobThreadFactory.FindJobThread(jobId); if (jobThread != null && jobThread.IsRunningOrHasQueue()) { return(ReturnT.CreateFailedResult("job thread is running or has trigger queue.")); } return(ReturnT.SUCCESS); }
private ReturnT Kill(int jobId) { if (_jobThreadFactory.TryRemoveJobThread(jobId, "scheduling center kill job.")) { return(ReturnT.SUCCESS); } else { return(ReturnT.CreateSucceededResult("job thread aleady killed.")); } }
public async Task <ReturnT> Execute(JobExecuteContext context) { await Task.Delay(500); _logger.LogInformation("defaultJobHandler执行了-------------------------"); await Task.CompletedTask; return(ReturnT.Success()); // return ReturnT.Failed("失败原因"); }
public Task <ReturnT> Execute(TriggerParam triggerParam, CancellationToken cancellationToken) { var handler = _handlerFactory.GetJobHandler(triggerParam.ExecutorHandler); if (handler == null) { return(Task.FromResult(ReturnT.Failed($"job handler [{triggerParam.ExecutorHandler} not found."))); } var context = new JobExecuteContext(this._jobLogger, triggerParam.ExecutorParams, cancellationToken); return(handler.Execute(context)); }
private async Task <bool> ValidToken(HttpContext context) { context.Request.Headers.TryGetValue(XxlJobConstant.Token, out StringValues token); if (!string.IsNullOrEmpty(_options.Token) && token != _options.Token) { await context.Response.WriteAsync(ReturnT.Failed("token验证失败").SerializeObject()); _logger.LogError($"xxljob token验证失败:{context.Request.Path.Value}"); return(false); } return(true); }
/// <summary> /// 心跳检测 路由策略选择故障转移时触发 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <object> HandleBeat(HttpContext context) { var res = ReturnT.Success(); try { //_logger.LogInformation("--------------心跳检测--------------"); await Task.CompletedTask; } catch (Exception ex) { res = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob心跳检测错误"); } return(res); }
public ReturnT PushTriggerQueue(TriggerParam triggerParam) { // avoid repeat if (_triggerLogIdSet.ContainsKey(triggerParam.logId)) { _logger.LogInformation("repeate trigger job, logId:{logId}", triggerParam.logId); return(ReturnT.CreateFailedResult("repeate trigger job, logId:" + triggerParam.logId)); } _logger.LogInformation("repeate trigger job, logId:{logId}", triggerParam.logId); _triggerLogIdSet[triggerParam.jobId] = 0; _triggerQueue.Enqueue(triggerParam); _queueHasDataEvent.Set(); return(ReturnT.SUCCESS); }
public async Task <IActionResponse> ActionAsync(HttpContext context) { var info = await context.Request.FromBody <JobBaseRequest>(); if (info == null || info.JobId <= 0) { return(await context.JsonAsync(ReturnT.Failed("参数错误"))); } if (await _dispatcher.Kill(info.JobId, "终止任务[调度中心主动停止任务]")) { return(await context.JsonAsync(ReturnT.Success())); } return(await context.JsonAsync(ReturnT.Failed("终止任务失败"))); }
public async Task CallBack(int logId, ReturnT executeResult) { Func <object, Task> action = null; action = async(state) => { var retryInfo = state as RetryCallbackDTO; try { var calbackBody = new { code = executeResult.Code, msg = executeResult.Msg, logId = logId, logDateTime = DateUtils.GetTimeStamp(), executeResult = new { code = executeResult.Code, msg = executeResult.Msg } }; var header = new Dictionary <string, string>(); header.Add(XxlJobConstant.Token, _xxlJobOption.Token); var res = await _httpClientFactory.CreateClient().PostJsonAsync <ReturnT>(_xxlJobOption.XxlJobAdminUrl.AppendUrlPath("api/callback"), new object[] { calbackBody }, header); if (res?.Code == ReturnT.SUCCESS_CODE) { return; } } catch (Exception ex) { _logger.LogError(ex, $"xxljob任务结果回调失败,logId:{logId},等待重试,第{retryInfo?.ErrorCount}次"); } //处理重试 if (retryInfo == null) { return; } if (retryInfo.ErrorCount > RetryCallbackDTO.MaxRetryCount) { return; } retryInfo.ErrorCount++; var delay = TimeSpan.FromSeconds(retryInfo.GetDelaySecond()); _taskExecutor.GetSingleThreadTaskExecutor(logId).Schedule(action, retryInfo, delay); }; _taskExecutor.GetSingleThreadTaskExecutor(logId).Execute(action, new RetryCallbackDTO()); await Task.CompletedTask; }
public async Task InvokeAsync(HttpContext context, RequestDelegate next) { if (IsValidToken(context.Request)) { //进行token验证 context.Request.Headers.TryGetValue(XxlJobConstant.Token, out StringValues token); if (!string.IsNullOrEmpty(_xxlJobOption.Token) && token != _xxlJobOption.Token) { await context.Response.WriteAsync(JsonUtils.ToJson(ReturnT.Failed("token验证失败"))); _logger.LogError($"xxljob token验证失败:{context.Request.Path.Value}"); return; } } await next(context); }
private async Task HandleRequest(HttpContext context, PathString subPath) { context.Response.ContentType = "application/json; charset=utf-8"; // token 检查 if (!await ValidToken(context)) { context.Response.StatusCode = StatusCodes.Status401Unauthorized; await context.Response.WriteAsync(ReturnT.Failed("401 Unauthorized").SerializeObject()); return; } context.Response.StatusCode = StatusCodes.Status200OK; var subUrl = subPath.Value.EnsureStartSlash(); var route = _routeTable.FindByPattern(subUrl); if (route != null) { var controller = context.RequestServices.GetRequiredService(route.TypeController) as IJobController; if (controller != null) { try { var response = await controller.ActionAsync(context); if (response != null) { await context.Response.WriteAsync(response.ExecuteResult(context), Encoding.UTF8); return; } } catch (Exception e) { _logger.LogError(e, "请求异常"); await context.Response.WriteAsync(ReturnT.Failed("请求异常").SerializeObject()); return; } } } context.Response.StatusCode = StatusCodes.Status404NotFound; await context.Response.WriteAsync(ReturnT.Failed("404 NotFound").SerializeObject()); }
public async Task <IActionResponse> ActionAsync(HttpContext context) { var info = await context.Request.FromBody <JobBaseRequest>(); if (info == null || info.JobId <= 0) { return(await context.JsonAsync(ReturnT.Failed("参数错误"))); } var isRunning = await _dispatcher.Exist(info.JobId); if (isRunning) { return(await context.JsonAsync(ReturnT.Failed("任务执行中"))); } return(await context.JsonAsync(ReturnT.Success())); }
public override async Task <ReturnT> Execute(JobExecuteContext context) { if (string.IsNullOrEmpty(context.JobParameter)) { return(ReturnT.Failed("url is empty")); } string url = context.JobParameter; if (!UrlPattern.IsMatch(url)) { return(ReturnT.Failed("url format is not valid")); } context.JobLogger.Log("Get Request Data:{0}", context.JobParameter); using (var client = this._httpClientFactory.CreateClient(Constants.DefaultHttpClientName)) { try { var response = await client.GetAsync(url); if (response == null) { context.JobLogger.Log("call remote error,response is null"); return(ReturnT.Failed("call remote error,response is null")); } if (response.StatusCode != HttpStatusCode.OK) { context.JobLogger.Log("call remote error,response statusCode ={0}", response.StatusCode); return(ReturnT.Failed("call remote error,response statusCode =" + response.StatusCode)); } string body = await response.Content.ReadAsStringAsync(); context.JobLogger.Log("<br/> call remote success ,response is : <br/> {0}", body); return(ReturnT.SUCCESS); } catch (Exception ex) { context.JobLogger.LogError(ex); return(ReturnT.Failed(ex.Message)); } } }
/// <summary> /// 触发任务 /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <object> HandleRun(HttpContext context) { var res = ReturnT.Success(); try { var jobInfo = await context.Request.FromBody <JobRunRequest>(); //_logger.LogInformation($"--------------触发任务{JsonUtils.ToJson(jobInfo)}--------------"); //获取jobhandler并执行 var jobHandler = _jobHandlerManage.GetJobHandler(jobInfo.executorHandler); if (jobHandler == null) { throw new Exception($"没有对应的JobHandler,{jobInfo.executorHandler}"); } //处理执行器策略 默认是串行执行 Enum.TryParse(jobInfo.executorBlockStrategy, out ExecutorBlockStrategy blockStrategy); if (blockStrategy == ExecutorBlockStrategy.DISCARD_LATER) //如果有积压任务,丢弃当前任务 { if (_xxlJobExecutor.IsRunningOrHasQueue(jobInfo.jobId)) { return(ReturnT.Failed("终止任务[执行策略: DISCARD_LATER]")); } } else if (blockStrategy == ExecutorBlockStrategy.COVER_EARLY) //覆盖之前调度 负载之前积压的任务 { if (_xxlJobExecutor.IsRunningOrHasQueue(jobInfo.jobId)) { _xxlJobExecutor.KillJob(jobInfo.jobId, "终止任务[执行策略:COVER_EARLY]"); //停止该jogid对应的所有积压的任务(已经在执行中的就停止不了) } } await AsyncExecuteJob(jobInfo, jobHandler); } catch (Exception ex) { res = ReturnT.Failed(ex.StackTrace + "————" + ex.Message); _logger.LogError(ex, "xxljob触发任务错误"); } return(res); }
public async Task <ReturnT> Execute(JobExecuteContext context) { var result = ReturnT.Success(); try { //await Task.Delay(30000); //这里执行业务逻辑 _logger.LogInformation("SecondJobHandler执行了-------------------------"); await Task.CompletedTask; } catch (BizException) //业务异常 { } catch (Exception ex) { //只有系统异常返回错误,便于重试 result = ReturnT.Failed(ex.Message); } return(result); }
public async Task <ReturnT> Execute(JobExecuteContext context) { var result = ReturnT.Success(); try { var url = _xxlJobOption.HttpJobhandlerUrl; if (string.IsNullOrEmpty(url)) { return(ReturnT.Failed("httpJobHandler任务请配置url")); } JObject jobParameter = JObject.Parse(context.JobParameter); string path = GetPathAndRemovePath(jobParameter); if (string.IsNullOrEmpty(path)) { return(ReturnT.Failed("httpJobHandler任务请配置path")); } // jobParameter.Add("logId", context.LogId); var header = CreateHeader(); var param = new { logId = context.LogId, jobParameter = jobParameter }; var res = await _httpClientFactory.CreateClient().PostJsonAsync <HttpJobHandlerResponse>(StringUtils.AppendUrlPath(url, path), param, header); if (res == null || res.code != HttpJobHandler.SuccessCode) { result = ReturnT.Failed(res?.msg ?? "执行http任务异常"); } } catch (Exception ex) { _logger.LogError(ex, "执行httpJobHandler异常"); result = ReturnT.Failed($"{ex.StackTrace},{ex.Message}"); } return(result); }
private ReturnT Run(TriggerParam triggerParam) { if (Constants.GlueType.BEAN != triggerParam.glueType) { return(ReturnT.CreateFailedResult("glueType[" + triggerParam.glueType + "] is not valid.")); } JobThread jobThread; var isNewThread = _jobThreadFactory.GetJobThread(triggerParam, out jobThread); if (!isNewThread && Constants.ExecutorBlockStrategy.DISCARD_LATER == triggerParam.executorBlockStrategy && jobThread.IsRunningOrHasQueue()) { return(ReturnT.CreateFailedResult("block strategy effect:" + triggerParam.executorBlockStrategy)); } var result = jobThread.PushTriggerQueue(triggerParam); if (isNewThread) { jobThread.Start(); } return(result); }
public async Task <IActionResponse> ActionAsync(HttpContext context) { var info = await context.Request.FromBody <JobLogRequest>(); if (info == null || info.JobLogId <= 0) { return(await context.JsonAsync(ReturnT.Failed("参数错误"))); } (var logs, var totalCount) = await _store.FilterJobList(info.FromLineNum - 1, Default_Limit, info.JobLogId); StringBuilder sb = new StringBuilder(); foreach (var log in logs) { sb.AppendLine($"[{log.InData.ToString("yyyy-MM-dd HH:mm:ss")}],{log.Message}"); } // 从1开始 var endLineNum = info.FromLineNum + logs.Count; var res = ReturnT.Success(); res.Content = new { // 本次请求,日志开始行数 fromLineNum = info.FromLineNum, // 本次请求,日志结束行号 toLineNum = endLineNum, // 本次请求日志内容 logContent = sb.ToString(), // 日志是否全部加载完 isEnd = totalCount <= endLineNum }; return(await context.JsonAsync(res)); }
public async Task <ReturnT> Execute(JobExecuteContext context) { var result = ReturnT.Success(); try { // await Task.Delay(30000); //这里执行业务逻辑 //_logger.LogInformation("firstJobHandler执行了{0}, {1}", "1", 2); _logger.LogInformation("firstJobHandler执行了{a}, {b}", "1", 2); //只占位符 //throw new Exception(" 系统异常了,要重试了吧"); await Task.CompletedTask; } catch (BizException) //业务异常 { } catch (Exception ex) { //只有系统异常返回错误,便于重试 result = ReturnT.Failed($"{ex.StackTrace},{ex.Message}"); } return(result); }