代码改变世界

quartz.net 可视化改造

2022-01-03 16:55  mi-战斧  阅读(504)  评论(0)    收藏  举报

Part 1

为什么使用可视化quratz?我之前使用quartz.net 写了一个定时任务系统,是利用quartz.net 结合 IHostedService 写的,任务配置通过plugin  quartz.config 读入 quratz_jobs.xml的配置文件生成。每次需要新增job需要的步骤为:

  • 新增一个Ijob的继承类
  • 配置一个quartz_jobs.xml 对应该job的Trigger 配置
  • 配置一个Log4net Appender,主要用于分离各个Job Log文件。
  • 发布时候,需要重启并且修改对应的配置

  • 这里一直存在的问题是我没有办法随时随地修改配置Job的执行频次,以及决定Job是不是立即执行用于初始化之类的操作。
    所以我一直想要一个可以可视化编辑定时任务的界面。

    Part2

    我查了网上的资料,以及开源的系统,其中Quartzui 是做的最好的,通用性最强,但是他只能使用URL来激活Job,那么根据他的配置方案,是通过定时轮询调用 API 来达到调用Job执行的目的,这里存在一个问题,我的任务可能是耗时很长的操作,比如我需要计算一个月产生数据报表,这个时候可能需要二十多分钟,一个api耗时几十分钟,我内心是不能接受的,虽然这个API肯定不对外,其他程序也不回调用,但是内心很排斥(我也不知道为啥)。
    我理想的方案是可视化直接可编辑我的Job的配置Trigger,这样势必造成不够通用。

    quartz.net 的示例。

               // Grab the Scheduler instance from the Factory
                StdSchedulerFactory factory = new StdSchedulerFactory();
                IScheduler scheduler = await factory.GetScheduler();
    
                // and start it off
                await scheduler.Start();
    
                // define the job and tie it to our HelloJob class
                IJobDetail job = JobBuilder.Create<HelloJob>()
                    .WithIdentity("job1", "group1")
                    .Build();
    
                // Trigger the job to run now, and then repeat every 10 seconds
                ITrigger trigger = TriggerBuilder.Create()
                    .WithIdentity("trigger1", "group1")
                    .StartNow()
                    .WithSimpleSchedule(x => x
                        .WithIntervalInSeconds(10)
                        .RepeatForever())
                    .Build();
    
                // Tell quartz to schedule the job using our trigger
                await scheduler.ScheduleJob(job, trigger);
    
                // some sleep to show what's happening
                await Task.Delay(TimeSpan.FromSeconds(60));
    
                // and last shut down the scheduler when you are ready to close your program
                await scheduler.Shutdown();
    

    这里可以看到Job是需要主导到Scheduler中的,qurartz Pulgin 是使用schduler加载xml中的配置,这个配置可以动态加载的,即可以改动xml配置后,立即生效。
    那么我们的思路可以是这样:通过系统配置修改quartz_job.xml。如果是PRD环境,需要系统生成文件再替换掉原配置文件,这样需要手撸一个生成job xml 生成器,再做一个指定位置的下发。考虑到你的配置系统跟你的Job系统可能不在一个服务器,那么下发xml又是一个问题。
    这样的话,job所在服务器需要有一个解决xml下发问题的逻辑。好麻烦。

    于是想了一个一个办法,可视化系统直接使用 quarzui,通过API 激活 指定job执行。
    API Job集中在另一个系统中,那么问题来到了 api 激活job🈶不想耗时怎么解决?

    part3

    答案是我把调用的API系统中同样集成quartz.net,示例如下:

    [HttpGet]
        [Route("api/job/hello")]
        public async Task<string> GetHelloJob()
        {
            var scheduler = await SchedulerSingleton.CreateInstance();
    
            // define the job and tie it to our HelloJob class
            IJobDetail job = JobBuilder.Create<HelloJob>()
                .WithIdentity("helloJob", "group1")
                .Build();
    
            await scheduler.DeleteJob(job.Key);
    
            // Trigger the job to run now, and then repeat every 10 seconds
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("helloTrigger", "group1")
                .StartNow()
                .WithSimpleSchedule(x => x
                    .WithRepeatCount(0))
                .Build();
    
            // Tell quartz to schedule the job using our trigger
            await scheduler.ScheduleJob(job, trigger);
    
            await scheduler.Start();
    
            return "hello guy";
        }
    

    API 中的 Trigger 是立即执行永不重复,那么每次API调用都会激活我们的helloJob 任务。
    helloJob 只需要写自己的处理逻辑。同时Job 加上特性DisallowConcurrentExecution,即使API 不停的调用,job也不会有多线程执行,这样就解决了不用造轮子的问题,就是懒。

    这里代码同样可以看到每次执行前都会从Scheduler中删除该Job,然后再添加,这样势必造成内存消耗,我的考虑是单次调用这部分损耗很少,但是如果并发量很大,大量API调用操作scheduler 对象,势必会有一定的影响。目前还没有测试,测试后更新。

    quartzui的部分可以参考: github https://github.com/zhaopeiym/quartzui
    可以讨论一下

    爱自己爱生活