由于到新公司解决老旧系统定时任务基于bat脚本curl定时请求无法统一管理和扩展性太差问题,以及在搭建的过程中遇到的坑,故针对这情况写了一个定时任务的管理模块,支持Get,Post请求方式。
源代码地址:源代码地址
无缝嵌入项目,支持Swagger测试
什么是定时任务?
定时发邮件,定时统计信息等内容,那么如何实现才能使得我们的项目整齐划一呢?本文通过一些简单的小例子,简述在.Net 7+Quartz实现定时任务的一些基本操作,及相关知识介绍,仅供学习分享使用,如有不足之处,还请指正。
什么是Quartz?
Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB 作业预构 建,JavaMail 及其它,支持 cron-like 表达式等等。虽然Quartz最初是为Java编写的,但是目前已经有.Net版本的Quartz,所以在.Net中应用Quartz已经不再是奢望,而是轻而易举的事情了。
Github上开源网址为:https://github.com/quartznet
关于Quartz的快速入门和API文档,可以参考:
https://www.quartz-scheduler.net/documentation/quartz-3.x/quick-start.html
Quartz安装
为了方便,本项目采用仓储模式,由Api,BizManagement,DataBase,Entity 4个模块组成,本次基于Rider开发,通过Nuget包管理器进行安装,如下所示:
项目模块组成:
依赖组件:
1. redis (StackExchange.Redis: 2.6.122)查询全量数据的持久化
2. Nlog(5.3.5) 日志记录到文件
3. SkyApm(0.9.0) 接口追踪
4. Mysql(Pomelo.EntityFrameworkCore.MySql: 7.0.0) 持久化Scheduler到数据库
5. AutoMapper(12.0.1) 实体映射
6. EFCore(7.0.13) ORM管理
remark:数据库,Redis地址在api模块 appsetting.json修改
主要功能组件:
QuartzManage: 对Job的管理,支持暂停,启动,更新频率,批量删除等操作
using System.Collections.Specialized;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using org.huage.BizManagement.Job;
using org.huage.BizManagement.Listener;
using org.huage.BizManagement.Proxy;
using org.huage.Entity.common;
using org.huage.Entity.Request;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;
using SchedulerException = Quartz.SchedulerException;
namespace org.huage.BizManagement.Manager;
public class QuartzManage : IQuartzManage
{
private static ISchedulerFactory _schedulerFactory;
private static IScheduler _scheduler;
private readonly IJobFactory _jobFactory;
public QuartzManage(IJobFactory jobFactory)
{
_jobFactory = jobFactory;
var config = new NameValueCollection();
config.Add("quartz.jobStore.misfireThreshold", "1000");
_schedulerFactory = new StdSchedulerFactory(config);
//获得调度器
_scheduler = _schedulerFactory.GetScheduler().Result;
_scheduler.JobFactory = jobFactory;
//调度开始
_scheduler.Start();
}
/// <summary>
/// 判断corn表达式是否有效
/// </summary>
/// <param name="cronExpression"></param>
/// <returns></returns>
private bool IsValidExpression(string cronExpression)
{
CronTriggerImpl trigger = new CronTriggerImpl();
try
{
trigger.CronExpressionString = cronExpression;
DateTimeOffset? date = trigger.ComputeFirstFireTimeUtc(null);
return date != null;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// 暂停Job
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
public async Task PauseJob(string jobName, string groupName)
{
//查询同一个分组的JobKey列表
var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
if (jobKeys != null && jobKeys.Count > 0)
{
//获得指定名称jobName的的jobKey
var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
if (jobKey != null)
{
//暂停job的调度
await _scheduler.PauseJob(jobKey);
}
else
{
throw new SchedulerException("Can' find this Job.");
}
}
else
{
throw new SchedulerException("Can't fin this Job.");
}
}
/// <summary>
/// 重启job
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <exception cref="Exception"></exception>
public async Task StartJob(string jobName, string groupName)
{
//查询同一个分组的JobKey列表
var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
if (jobKeys != null && jobKeys.Any())
{
//获得指定名称jobName的的jobKey
var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
if (jobKey != null)
{
//暂停job的调度
await _scheduler.ResumeJob(jobKey);
}
else
{
throw new SchedulerException("没有此Job");
}
}
else
{
throw new SchedulerException("没有此Job");
}
}
/// <summary>
/// 创建Get请求方式Job
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <param name="desc"></param>
/// <param name="cronExpression"></param>
/// <typeparam name="T"></typeparam>
/// <exception cref="Exception"></exception>
public async Task AddJobForGet<T>(string jobName, string groupName, string remark,AddSchedulerRequest request)
where T : IJob
{
//判断 cron 表达式是否有效
if (!IsValidExpression(request.CronExpression))
{
throw new Exception("请输入cron表达式");
}
//判断url
if (string.IsNullOrEmpty(request.Url) || ! Uri.TryCreate(request.Url,UriKind.Absolute,out var success))
{
throw new Exception($"当前Url 格式不正确:{request.Url}");
}
//创建Job
var job = JobBuilder.Create<T>().UsingJobData(new JobDataMap()
{
new KeyValuePair<string, object>("RequestUrl", request.Url),
new KeyValuePair<string, object>("RequestParam", request.MethodParams)
})
.WithIdentity(jobName, groupName)//键值
.WithDescription(remark)//描述
.Build();
//创建触发器
var timeUnit = SchedulerUtils.GetTimeUnit(request.StartTime);
ITrigger trigger = TriggerBuilder.Create()
.WithCronSchedule(request.CronExpression,x=>x.WithMisfireHandlingInstructionFireAndProceed())
.WithIdentity(jobName, groupName)
.StartAt(DateBuilder.DateOf(timeUnit[3], timeUnit[4],
timeUnit[5],timeUnit[2],timeUnit[1],timeUnit[0]))
.Build();
//添加监听器
//_scheduler.ListenerManager.AddJobListener(new JobListener(),GroupMatcher<JobKey>.AnyGroup());
//_scheduler.ListenerManager.AddTriggerListener(new TriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());
//开始以创建的触发器调度创建的Job
await _scheduler.ScheduleJob(job, trigger);
}
/// <summary>
/// 创建post请求的参数
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <param name="request"></param>
/// <typeparam name="T"></typeparam>
/// <exception cref="Exception"></exception>
public async Task AddJobForPost<T>(string jobName, string groupName,AddSchedulerRequest request)
where T : IJob
{
//判断 cron 表达式是否有效
if (!IsValidExpression(request.CronExpression))
{
throw new Exception("请输入cron表达式");
}
//创建Job
var job = JobBuilder.Create<T>()
.WithIdentity(jobName, groupName).UsingJobData(new JobDataMap()
{
new KeyValuePair<string, object>("RequestUrl", request.Url),
new KeyValuePair<string, object>("RequestParam", request.MethodParams)
}).RequestRecovery(true).WithDescription(request.Remark ?? "")
.Build();
//创建触发器
var timeUnit = SchedulerUtils.GetTimeUnit(request.StartTime);
ITrigger trigger = TriggerBuilder.Create()
.WithCronSchedule(request.CronExpression,x=>x.WithMisfireHandlingInstructionFireAndProceed())
.WithIdentity(SchedulerKeyGenerator.TriggerKey(request.Id), groupName)
.StartAt(DateBuilder.DateOf(timeUnit[3], timeUnit[4],
timeUnit[5],timeUnit[2],timeUnit[1],timeUnit[0]))
.Build();
//添加监听器
//_scheduler.ListenerManager.AddJobListener(new JobListener(),GroupMatcher<JobKey>.AnyGroup());
//_scheduler.ListenerManager.AddTriggerListener(new TriggerListener(), GroupMatcher<TriggerKey>.AnyGroup());
await _scheduler.ScheduleJob(job, trigger);
}
/// <summary>
/// 更新job调度频率
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <param name="remark"></param>
/// <param name="cronExpression"></param>
/// <exception cref="Exception"></exception>
/// <exception cref="SchedulerException"></exception>
public async Task UpdateJobRate(string jobName, string groupName, string remark, string cronExpression,long startTime)
{
if (!IsValidExpression(cronExpression))
{
throw new Exception("cron表达式无效");
}
var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
if (jobKeys != null && jobKeys.Any())
{
var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
if (jobKey != null)
{
var job = await _scheduler.GetJobDetail(jobKey);
if (job == null) throw new Exception("job不存在");
//先删除原有job
await _scheduler.DeleteJob(jobKey);
var timeUnit = SchedulerUtils.GetTimeUnit(startTime);
var trigger = TriggerBuilder.Create()
.WithCronSchedule(cronExpression)
.WithIdentity(jobName, groupName)
.StartAt(DateBuilder.DateOf(timeUnit[3], timeUnit[4],
timeUnit[5],timeUnit[2],timeUnit[1],timeUnit[0]))
.Build();
await _scheduler.ScheduleJob(job, trigger);
}
else
{
throw new SchedulerException("job不存在");
}
}
else
{
throw new SchedulerException("job不存在");
}
}
/// <summary>
/// 删除job
/// </summary>
/// <param name="jobName"></param>
/// <param name="groupName"></param>
/// <exception cref="Exception"></exception>
public async Task DeleteJob(string jobName, string groupName)
{
var jobKeys = await _scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName));
if (jobKeys != null && jobKeys.Any())
{
var jobKey = jobKeys.FirstOrDefault(p => p.Name.Equals(jobName));
if (jobKey != null)
{
await _scheduler.DeleteJob(jobKey);
}
else
{
throw new SchedulerException("要删除的job不存在");
}
}
else
{
throw new SchedulerException("要删除的job不存在");
}
}
}
SchedulerManage: 实际调用的服务,暴露给controller调用。
using System.Linq.Expressions;
using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using org.huage.BizManagement.Job;
using org.huage.BizManagement.Redis;
using org.huage.BizManagement.Wrapper;
using org.huage.Entity.common;
using org.huage.Entity.Enum;
using org.huage.Entity.Model;
using org.huage.Entity.Request;
using org.huage.Entity.Response;
using org.huage.EntityFramewok.Database;
using org.huage.EntityFramewok.Database.Table;
using SchedulerException = org.huage.Entity.common.SchedulerException;
namespace org.huage.BizManagement.Manager;
public class SchedulerManager : ISchedulerManager
{
private readonly IRepositoryWrapper _wrapper;
private readonly ILogger<SchedulerManager> _logger;
private readonly IRedisHelper _redisHelper;
private IMapper _mapper;
private readonly IQuartzManage _quartzManage;
public SchedulerManager(IRepositoryWrapper wrapper, ILogger<SchedulerManager> logger, IMapper mapper,
IRedisHelper redisHelper, IQuartzManage quartzManage)
{
_wrapper = wrapper;
_logger = logger;
_mapper = mapper;
_redisHelper = redisHelper;
_quartzManage = quartzManage;
}
public async Task<AddSchedulerResponse> AddSchedulerAsync(AddSchedulerRequest request)
{
//先保存到数据库,后续丢失可以重新跑
AddSchedulerResponse response = new AddSchedulerResponse();
try
{
if (request is null || (request.RequestType != RequestType.REQUEST_TYPE_GET && request.RequestType != RequestType.REQUEST_TYPE_POST))
throw new SchedulerException("目前只支持Get,Post,请传入正确的RequestType.");
//首先判断传进来的是get/post,然后在IJob定义向指定的接口发送请求。
switch (request.RequestType)
{
case RequestType.REQUEST_TYPE_GET:
await _quartzManage.AddJobForGet<GetJob>(SchedulerKeyGenerator.JobKey(request.Id), SchedulerKeyGenerator.GroupKey(request.Id)
, "",request);
break;
case RequestType.REQUEST_TYPE_POST:
await _quartzManage.AddJobForPost<PostJob>(SchedulerKeyGenerator.JobKey(request.Id), SchedulerKeyGenerator.GroupKey(request.Id), request);
break;
}
//删除缓存
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
//插入数据库
var scheduler1 = _mapper.Map<Scheduler>(request);
_wrapper.Scheduler.Create(scheduler1);
await _wrapper.SaveChangeAsync();
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
}
catch (Exception e)
{
_logger.LogError("AddSchedulerAsync occur error:{Message}", e.Message);
throw;
}
return response;
}
/// <summary>
/// 更新Scheduler Rate(调度速率)
/// </summary>
/// <param name="rateRequest"></param>
/// <returns></returns>
public async Task<UpdateSchedulerRateResponse> UpdateSchedulerRateAsync(UpdateSchedulerRateRequest rateRequest)
{
//先删除缓存
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
var schedulerUpdate = _mapper.Map<Scheduler>(rateRequest);
schedulerUpdate.UpdateBy = "test";
_wrapper.Scheduler.Update(schedulerUpdate);
//1.移除原来的job; 2.修改trigger; 3.重新调度jobDetail
await _quartzManage.UpdateJobRate(SchedulerKeyGenerator.JobKey(rateRequest.Id), SchedulerKeyGenerator.GroupKey(rateRequest.Id), rateRequest.Remark, rateRequest.CronExpression, rateRequest.StartTime);
return new UpdateSchedulerRateResponse();
}
/// <summary>
///更新Scheduler状态,暂停,启用。
/// </summary>
/// <param name="statusRequest"></param>
/// <returns></returns>
/// <exception cref="SchedulerException"></exception>
public async Task<bool> UpdateSchedulerStatusAsync(UpdateSchedulerStatusRequest statusRequest)
{
if (statusRequest is null || (statusRequest.Status != 0 && statusRequest.Status != 1))
throw new SchedulerException("请传入正确的参数: Status.");
switch (statusRequest.Status)
{
case 0:
//执行关闭;
await _quartzManage.PauseJob(SchedulerKeyGenerator.JobKey(statusRequest.Id), SchedulerKeyGenerator.GroupKey(statusRequest.Id));
break;
case 1:
//执行启动
await _quartzManage.StartJob(SchedulerKeyGenerator.JobKey(statusRequest.Id), SchedulerKeyGenerator.GroupKey(statusRequest.Id));
break;
}
//删除缓存
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
//更新数据库
var scheduler = _wrapper.Scheduler.FindByCondition(data => data.Id == statusRequest.Id && data.IsDeleted == false)
.FirstOrDefault();
if (scheduler != null)
{
scheduler.JobStatus = statusRequest.Status;
_wrapper.Scheduler.Update(scheduler);
return true;
}
return false;
}
/// <summary>
/// 更新整个JobDetail内容
/// </summary>
/// <param name="statusRequest"></param>
/// <returns></returns>
/// <exception cref="SchedulerException"></exception>
public async Task<UpdateSchedulerResponse> UpdateSchedulerAsync(UpdateSchedulerRequest request)
{
//Delay double delete.
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
//更新数据库
var updateScheduler = _mapper.Map<Scheduler>(request);
_wrapper.Scheduler.Update(updateScheduler);
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
var response = new UpdateSchedulerResponse();
//思路就是把trigger jobdetail全部替换成新的;1.先移除,2.新建(同时考虑会不会数据库重复,筛选条件加isDel)
if (request is null || !"Get".Equals(request.RequestType,StringComparison.OrdinalIgnoreCase) || !"Post".Equals(request.RequestType,StringComparison.OrdinalIgnoreCase))
throw new SchedulerException("目前只支持Get,Post,请传入正确的RequestType.");
await _quartzManage.DeleteJob(SchedulerKeyGenerator.JobKey(request.Id),SchedulerKeyGenerator.GroupKey(request.Id));
switch (request.RequestType)
{
case RequestType.REQUEST_TYPE_GET:
await _quartzManage.AddJobForGet<GetJob>(SchedulerKeyGenerator.JobKey(request.Id), SchedulerKeyGenerator.GroupKey(request.Id)
, "", new AddSchedulerRequest());
break;
case RequestType.REQUEST_TYPE_POST:
var map = _mapper.Map<AddSchedulerRequest>(request);
await _quartzManage.AddJobForPost<PostJob>(SchedulerKeyGenerator.JobKey(request.Id), SchedulerKeyGenerator.GroupKey(request.Id),map);
break;
}
return response;
}
/// <summary>
/// 批量删除Schedulers
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
/// <exception cref="SchedulerException"></exception>
public async Task<bool> BatchDelSchedulerAsync(List<Guid> ids)
{
if (!ids.Any())
throw new SchedulerException("请传入正确的参数: ids.");
//删除缓存
await _redisHelper.DelKey(RedisKeyGenerator.AllSchedulersRedisKey());
//更新数据库,逻辑删除
var list = await _wrapper.Scheduler.FindByCondition(x => ids.Contains(x.Id)).ToListAsync();
foreach (var scheduler in list)
{
scheduler.IsDeleted = true;
scheduler.JobStatus = 0;
_wrapper.Scheduler.Update(scheduler);
//删除
await _quartzManage.DeleteJob(SchedulerKeyGenerator.JobKey(scheduler.Id), SchedulerKeyGenerator.GroupKey(scheduler.Id));
}
return true;
}
/// <summary>
/// 查询所有的Scheduler列表
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<QuerySchedulerListResponse> QueryAllSchedulersAsync(QuerySchedulerListRequest request)
{
var response = new QuerySchedulerListResponse();
try
{
//先去查redis
var allSchedulers = await _redisHelper.HGetAllValue<Scheduler>(RedisKeyGenerator.AllSchedulersRedisKey());
if (allSchedulers.Any())
{
var map = _mapper.Map<List<SchedulerModel>>(allSchedulers);
response.Schedulers = map;
return response;
}
//查数据库,并设置redis;
var schedulers = _wrapper.Scheduler.FindAll().Where(_=>_.IsDeleted==false).ToList();
var data = _mapper.Map<List<SchedulerModel>>(schedulers);
response.Schedulers = data;
var schedulersDic = schedulers.ToDictionary(_ => _.Id);
await schedulersDic.ParallelForEachAsync(async scheduler =>
{
var redisKey = RedisKeyGenerator.AllSchedulersRedisKey();
try
{
foreach (var s in schedulers)
{
await _redisHelper.HashSet(redisKey, scheduler.Key.ToString(),
JsonConvert.SerializeObject(scheduler.Value));
}
}
catch (Exception e)
{
_logger.LogError(e.Message);
}
});
}
catch (Exception e)
{
_logger.LogError($"QueryAllSchedulersAsync error: {e.Message}");
throw;
}
return response;
}
/// <summary>
/// 根据条件查找schedulers.
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<QuerySchedulerListByConditionsResponse> QuerySchedulerListByConditionsAsync(QuerySchedulerListByConditionsRequest request)
{
var response = new QuerySchedulerListByConditionsResponse();
try
{
//Param Check
Expression<Func<Scheduler, bool>> expression = ExpressionExtension.True<Scheduler>();
if (!string.IsNullOrEmpty(request.MethodName))
{
expression = expression.And(p => p.MethodName.Contains(request.MethodName));
}
if (!string.IsNullOrEmpty(request.Url))
{
expression = expression.And(p => p.Url.Contains(request.Url));
}
if (!string.IsNullOrEmpty(request.RequesType))
{
expression = expression.And(p => p.RequestType.Contains(request.RequesType));
}
//查数据库
var schedulers = await _wrapper.Scheduler.FindByCondition(expression)
.OrderByDescending(_=>_.UpdateTime).ToListAsync();
if (schedulers.Any())
{
var map = _mapper.Map<List<SchedulerModel>>(schedulers);
response.SchedulerModels = map;
}
return response;
}
catch (Exception e)
{
_logger.LogError($"QuerySchedulerListByConditions error:{e.Message}");
throw;
}
}
private void DelayedDoubleDeletion(string key)
{
_ = Task.Factory.StartNew(async () =>
{
await Task.Delay(3000);
//删除key
_redisHelper.Remove(key);
});
}
}
GetJob和PostJob的定义:
注意这里需要解决Quartz.net的Job定义的时候默认不支持依赖注入,我们需要自定义JobFactory去实现IJobFactory接口,本项目中为DefaultSchedulerServiceFactory,注入starup的时候注意要使用AddSingleton方式,然后在QuartzManage构造函数里面主动修改它的JobFactory:
builder.Services.AddTransient<ISchedulerManager, SchedulerManager>();
builder.Services.AddTransient<IRedisHelper, RedisHelper>();
builder.Services.AddSingleton<IJobFactory, DefaultScheduleServiceFactory>();
//这里注意注入方式,以及上下这些注入都不可以缺少
builder.Services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
builder.Services.AddSingleton<GetJob>();
builder.Services.AddSingleton<PostJob>();
builder.Services.AddTransient<IQuartzManage, QuartzManage>();
//主动修改其默认JobFactory
_scheduler.JobFactory = jobFactory;
GetJob定义:
using Microsoft.Extensions.Logging;
using Quartz;
namespace org.huage.BizManagement.Job;
public class GetJob : IJob
{
private readonly ILogger<GetJob> _logger;
public GetJob(ILogger<GetJob> logger)
{
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
var url = context.MergedJobDataMap["RequestUrl"].ToString();
var param = context.MergedJobDataMap["RequestParam"].ToString();
try
{
_logger.LogInformation($"Current execute url: {url}, param is {param}");
var client = new HttpClient();
using var httpResponse = await client.GetAsync(url);
var result = await httpResponse.Content.ReadAsStringAsync();
_logger.LogInformation("Result is: "+ result);
}
catch (Exception e)
{
_logger.LogError("执行请求地址:"+url+" 发生异常");
_logger.LogError("ERROR:"+e.Message);
}
}
}
PostJob定义:
using System.Text;
using Microsoft.Extensions.Logging;
using Quartz;
namespace org.huage.BizManagement.Job;
public class PostJob : IJob
{
private readonly ILogger<PostJob> _logger;
public PostJob(ILogger<PostJob> logger)
{
_logger = logger;
}
public async Task Execute(IJobExecutionContext context)
{
//httpClient.DefaultRequestHeaders.Add("Portfolio", Portfolio);
var url = context.MergedJobDataMap["RequestUrl"].ToString();
var param = context.MergedJobDataMap["RequestParam"].ToString();
var content = new StringContent(param ?? "{}", Encoding.UTF8, "application/json");
try
{
_logger.LogInformation($"Current execute url: {url}, param is {param}");
var client = new HttpClient();
using var httpResponse = await client.PostAsync(url, content);
var result = await httpResponse.Content.ReadAsStringAsync();
_logger.LogInformation("Result is:" + result);
}
catch (Exception e)
{
_logger.LogError("执行请求地址:"+url+"发生异常");
_logger.LogError("ERROR:"+e.Message);
}
}
}
Swagger界面:
remark: 测试AddScheduler RequestType为Post请求的时候需要把methodParams参数压缩转义传递。
2023-12-15更新:
原因:为了满足在项目崩溃重启后能够执行之前的scheduler,我们在项目启动时基于IHostService注册一个后台任务run定时任务。
代码实现:
1. Api项目新增: RestartRegisterScheduler
using AutoMapper;
using org.huage.BizManagement.Job;
using org.huage.BizManagement.Manager;
using org.huage.Entity.common;
using org.huage.Entity.Enum;
using org.huage.Entity.Model;
using org.huage.Entity.Request;
namespace org.huage.Api.Extension;
public class RestartRegisterScheduler : IHostedService
{
private readonly ILogger<RestartRegisterScheduler> _logger;
private readonly IServiceProvider _serviceProvider;
public RestartRegisterScheduler(ILogger<RestartRegisterScheduler> logger, IServiceProvider serviceProvider)
{
_logger = logger;
_serviceProvider = serviceProvider ?? throw new ArgumentNullException("error");
}
/// <summary>
/// 查詢所有的數據庫狀態為enable的,然後重新注冊。
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Register the Scheduler to task container.");
using var scope = _serviceProvider.CreateScope();
var manager = scope.ServiceProvider.GetRequiredService<ISchedulerManager>();
var quartzManage = scope.ServiceProvider.GetRequiredService<IQuartzManage>();
var mapper = scope.ServiceProvider.GetRequiredService<IMapper>();
var request = new QuerySchedulerListByConditionsRequest()
{
Status = 0
};
var schedulerList = await manager.QuerySchedulerListByConditionsAsync(request);
foreach (var scheduler in schedulerList.SchedulerModels)
{
if (request is null || (scheduler.RequestType != RequestType.REQUEST_TYPE_GET && scheduler.RequestType != RequestType.REQUEST_TYPE_POST))
continue;
var addSchedulerRequest = mapper.Map<SchedulerModel,AddSchedulerRequest>(scheduler);
//注冊服務
switch (scheduler.RequestType)
{
case RequestType.REQUEST_TYPE_GET:
await quartzManage.AddJobForGet<GetJob>(SchedulerKeyGenerator.JobKey(scheduler.Id), SchedulerKeyGenerator.GroupKey(scheduler.Id)
, "",addSchedulerRequest);
break;
case RequestType.REQUEST_TYPE_POST:
await quartzManage.AddJobForPost<PostJob>(SchedulerKeyGenerator.JobKey(scheduler.Id), SchedulerKeyGenerator.GroupKey(scheduler.Id), addSchedulerRequest);
break;
}
}
_logger.LogInformation("BackGround Service has all register.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Schedulers Program is shut down,please check out.");
return Task.CompletedTask;
}
}
2. Program.cs注册后台服务
builder.Services.AddHostedService<RestartRegisterScheduler>();
以上就是.Net 7.0+Quartz项目定时任务调度的全部内容,后期会增加前端页面进行可视化界面进行管理。有任何问题欢迎留言,看到会第一时间回复。