17

.Net Core实现基于Quart.Net的任务管理

 3 years ago
source link: http://www.cnblogs.com/zhaorong0912/p/13853061.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前段时间给公司项目升级.net框架,把原先的任务管理平台用.net core实现,现做如下整理:

一、实现思路

之前的实现也是参考了博客园中其他文章实现的思路:

  1. 一个任务定义一个实现IJob接口的类,通过单独的dll管理;
  2. 通过数据库持久化、维护任务,便于服务重启时任务的恢复;
  3. 定义一个管理任务的基础服务,轮询数据库中的任务,根据任务的状态维护任务的执行;
  4. 新增任务时,需要在数据库中添加一条记录,并且在任务管理的dll中添加一个实现IJob的类,基础服务通过反射dll来构建任务的实例添加到调度器中

由于业务代码会频繁调整,我们业务代码从任务执行中拆分出来,独立部署成http服务,任务的执行就是调用一个http请求,这样不同的任务就是请求的url不一样,查看官方文档( https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/more-about-jobs.html#job-instances )发现,我们可以通过只创建一个基础任务类,创建多个该任务类的实例来实现构建多个任务,IJobDetail中可以用JobDataMap对象来存储Job实例的参数,所以我们通过JobDataMap将请求url传递到任务的Execute()方法中,我们只需要在数据库中补充任务请求的url信息就可以了,不需要单独的dll去定义任务。

二、项目结构

根据上面思路,我们只需要一个管理任务的基础服务、一个Web管理平台就可以实现,为了保持项目简单,把任务管理无关的功能合并在一个项目里,并且尽量排除无关的框架和功能点,最终程序包含3个项目:

  1. JobManage.Service:控制台程序,管理任务的基础服务,通过Topshelf部署成windows服务,如何部署参考: https://www.cnblogs.com/podolski/p/10054286.html
  2. JobManage.Web:Web应用程序,管理平台,新增、暂停、恢复、删除任务,查看任务运行日志;
  3. JobManage.Core:类库,提供业务基础服务,如数据库操作等

动态添加任务:

IJobDetail jobDetail = JobBuilder.Create<BaseJob>()
     .WithIdentity(jobKey)
     .UsingJobData("RequestUrl", job.RequestUrl)
     .Build();

ITrigger trigger = TriggerBuilder.Create()
    .WithIdentity(group, name)
    .StartNow()
    .WithCronSchedule(job.CronExpression)
    .Build();

await context.Scheduler.ScheduleJob(jobDetail, trigger);

基础任务类BaseJob.cs的Execute()方法:

public async Task Execute(IJobExecutionContext context)
{
    var url = context.JobDetail.JobDataMap.GetString("RequestUrl");
    var client = _clientFactory.CreateClient();
    var request = new HttpRequestMessage(HttpMethod.Post, url);
    var response = await client.SendAsync(request);
    if (response.IsSuccessStatusCode)
    {
    	await response.Content.ReadAsStringAsync();
    }
}

三、任务状态管理

这里定义7个任务状态:待执行、执行中、待暂停、已暂停、待恢复、待删除、已删除

web管理平台维护任务(新增、暂停、恢复、删除)时将任务状态更新为待处理状态(待执行、待暂停、待恢复、待删除),任务管理基础服务定时遍历业务任务,根据数据库中任务当前的状态修改任务的执行,并且将数据库中待处理任务状态更新为已处理状态(执行中、已暂停、已删除)

四、任务依赖注入服务

在任务类中我们用到了http服务,我们需要在任务类中获取http服务,我们通过.Net Core注入和获取服务的方式来实现,这里主要是要自定义任务类实例的创建和获取,官方文档( https://www.quartz-scheduler.net/documentation/quartz-3.x/tutorial/miscellaneous-features.html#jobfactory )中说明可以通过实现 IJobFactory 接口,并且修改 IScheduler.JobFactory的属性来实现:

//自定义任务实例获取
public class JobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;
    public JobFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        IJobDetail jobDetail = bundle.JobDetail;
        Type jobType = jobDetail.JobType;
        return _serviceProvider.GetService(jobType) as IJob;
    }

    public virtual void ReturnJob(IJob job)
    {
        var disposable = job as IDisposable;
        disposable?.Dispose();
    }
}
    
//修改IScheduler.JobFactory属性
_scheduler.JobFactory = serviceProvider.GetService<JobFactory>();

官方文档中也提供了依赖注入的示例: https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/microsoft-di-integration.html#di-aware-job-factories

五、任务监听

我们需要记录任务执行的情况,Quartz.Net提供了任务监听功能,我们可以自己实现IJobListener接口,也可以继承Quartz.Net框架中IJobListener的实现类JobListenerSupport来完成任务的监听,继承JobListenerSupport 类时重写对应的方法来实现我们需要的操作,如下实现记录任务上次执行时间、下次执行时间、执行时长、执行异常错误信息

//监听实现
public class JobListener : JobListenerSupport
{
    private readonly JobRepository _jobRepository;
    private readonly JobRunLogRepository _jobRunLogRepository;

    public JobListener(JobRepository jobRepository, JobRunLogRepository jobRunLogRepository)
    {
    	_jobRepository = jobRepository;
    	_jobRunLogRepository = jobRunLogRepository;
    }

    public override string Name
    {
    	get { return "jobListener"; }
    }

    public override async Task JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException, CancellationToken cancellationToken = default)
    {
        string group = context.JobDetail.Key.Group;
        string name = context.JobDetail.Key.Name;
        DateTime fireTimeUtc = TimeZoneInfo.ConvertTimeFromUtc(context.FireTimeUtc.DateTime, TimeZoneInfo.Local);

        DateTime? nextFireTimeUtc = null;
        if (context.NextFireTimeUtc != null)
        {
            nextFireTimeUtc = TimeZoneInfo.ConvertTimeFromUtc(context.NextFireTimeUtc.Value.DateTime, TimeZoneInfo.Local);
        }

        if (!JobHelper.IsBaseJob(group, name))
        {
            //更新任务执行情况
            await _jobRepository.UpdateExecuteAsync(group, name, fireTimeUtc, nextFireTimeUtc);
            //记录运行日志
            double totalSeconds = context.JobRunTime.TotalSeconds;
            bool succ = true;
            string exception = string.Empty;
            if (jobException != null)
            {
                succ = false;
                exception = jobException.ToString();
            }
            JobRunLog log = new JobRunLog(group, name, totalSeconds, fireTimeUtc, succ, exception);
            await _jobRunLogRepository.InsertAsync(log);
        }
    }
}

//注册监听器
JobListener listener = serviceProvider.GetService<JobListener>();
_scheduler.ListenerManager.AddJobListener(listener, GroupMatcher<JobKey>.AnyGroup());

六、总结

上述内容只是记录了搭建任务管理平台时的思路和几个关键的点,没有对Quartz.Net基础功能、MongoDB操作做说明,官方文档中包含了完整的说明,官方提供的源码中也有完整的示例,建议阅读官方文档源码来实现更高级的功能。

项目完整代码地址: https://github.com/zhrong92/JobManage

项目截图:

jeIVzmm.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK