【问题标题】:Quartz.NET - Jobs don't run?Quartz.NET - 作业不运行?
【发布时间】:2010-11-24 17:08:41
【问题描述】:

我正在尝试将 Quartz.NET 实现为 C# 中的 Windows 服务。当我期望它们触发时,我的工作并没有触发......实际上,据我所知?

我的“我的工作”计划在“每分钟”之后的下一个偶数分钟开始运行。但是,当下一分钟到来时,我似乎无法判断是否有任何实际运行。

我会假设当我的作业运行时,会在作业执行时弹出一个 CLI 窗口,并且控制台操作将可见,(我什至在其中放了一个 Console.ReadKey() 以确保窗口不会打开和关闭,所以快我看不到它),但据我所知,时间表根本没有执行作业。

我注意到所有时间都是 UTC 时间,StartTimeUtc 将设置为 UTC 时间,距离我的本地计算机时间 +6 小时,但我还假设 Quartz 调度程序通过计算来处理它我的 TimeZone 设置中的执行时间,尽管我无法确认,或确认我的时间表设置的实际时间。

我想有一些方法可以设置 Common Logging 程序集并利用它来帮助我了解我的状态,但我还没有弄清楚该怎么做才能启用任何类型的日志以获取来自我的 Windows 的反馈服务,除了写入我为它创建的事件日志。

我的 Windows 服务的 OnStart 函数

protected override void OnStart(string[] args)
    {
        eventLog.WriteEntry("--- STARTING eLoyalty Scheduler Service ---");

        // construct a scheduler factory
        ISchedulerFactory schedFact = new StdSchedulerFactory();

        // get a scheduler
        IScheduler sched = schedFact.GetScheduler();

        // construct job info
        JobDetail jobDetail = new JobDetail("eLoyaltySchedulerService", null, typeof(PortalSchedulerJob));
        jobDetail.JobDataMap["jobSays"] = "eLoyalty Scheduler Service Executing!";
        jobDetail.JobDataMap["myStateData"] = new ArrayList(); 

        // fire every minute
        Trigger trigger = TriggerUtils.MakeMinutelyTrigger();

        // start on the next even minute
        trigger.StartTimeUtc = TriggerUtils.GetEvenMinuteDate(DateTime.UtcNow);

        // name it
        trigger.Name = "NextEvenMinute";

        // schedule it
        sched.ScheduleJob(jobDetail, trigger);

        // start the schedule
        sched.Start();

        eventLog.WriteEntry("--- STARTED eLoyalty Scheduler Service ---");
    }

我的 Job 的 Execute() 函数如下:

public void Execute(JobExecutionContext context)
    {
        try
        {
            string instName = context.JobDetail.Name;
            string instGroup = context.JobDetail.Group;
            JobDataMap dataMap = context.MergedJobDataMap;
            string jobSays = dataMap.GetString("jobSays");
            ArrayList state = (ArrayList)dataMap["myStateData"];
            state.Add(DateTime.UtcNow);

            Console.WriteLine("Instance {0} of PortalSchedulerJob says: {1} @ {2}", instName, jobSays, DateTime.UtcNow);
            Console.ReadKey();
        }
        catch (JobExecutionException Ex)
        {
            throw Ex;
        }
    }

如果您能帮我弄清楚如何解决我的实际日程安排活动问题,我也许可以自己解决这个问题...?

【问题讨论】:

    标签: c# quartz-scheduler quartz.net


    【解决方案1】:

    在 Quartz.NET 任务中,您只能引发 JobExecutionException 异常: Quartz.net- Lesson 3:

    Job.Execute(..) 方法 最后,我们 需要通知你一些细节 IJob.Execute(..) 方法。唯一的 您被允许的例外类型 从执行方法抛出是 作业执行异常。因为 这个,你通常应该包装 执行方法的全部内容 带有“try-catch”块。你应该 也花一些时间看 的文档 JobExecutionException,因为您的工作可以 用它来提供调度器 关于你想要的各种指令 要处理的异常。

    代替:

    catch (JobExecutionException Ex)
    {
       throw Ex;
    }
    

    做这样的事情:

    catch (Exception err)
    {
        // Only allow JobExecutionException exceptions to be thrown...
        throw new Quartz.JobExecutionException(err);
    }
    

    然后您可以集中处理异常:

    _globalJobListener = new GlobalJobListener();
    sched.AddGlobalJobListener(_globalJobListener);
    
    
    public class GlobalJobListener : Quartz.IJobListener
    {
        public GlobalJobListener()
        {
        }
    
        public virtual string Name
        {
            get { return "MainJobListener"; }
        }
    
        public virtual void JobToBeExecuted(JobExecutionContext context)
        {       
        }
    
        public virtual void JobWasExecuted(JobExecutionContext inContext, JobExecutionException inException)
        {
            if (inException != null)
            {
                // Log/handle error here
            }
        }
    
    
        public virtual void JobExecutionVetoed(JobExecutionContext inContext)
        {
    
        }
    }
    

    【讨论】:

    • 这很有帮助,但不是这个特定问题的答案! :)
    • 非常正确。被异常处理的东西缠住了,我什至没有注意到你对控制台的使用——这在 Windows 服务中肯定行不通!
    • @keith - Windows 服务可以输出到控制台。您需要为此启用交互式访问。
    【解决方案2】:

    调试时,您始终可以在控制台中运行您的 Windows 服务:

    static class Program
    {
        static void Main()
        {
            var servicesToRun = new ServiceBase[] 
                { 
                    new CassetteDeckService() 
                };
    
            if (Environment.UserInteractive)
            {
                var type = typeof(ServiceBase);
                const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
                var method = type.GetMethod("OnStart", flags);
    
                foreach (var service in servicesToRun)
                {
                    method.Invoke(service, new object[] { null });
                }
    
                Console.WriteLine("Service Started!");
                Console.ReadLine();
    
                method = type.GetMethod("OnStop", flags);
    
                foreach (var service in servicesToRun)
                {
                    method.Invoke(service, null);
                }
                Environment.Exit(0);
            }
            else
            {
                ServiceBase.Run(servicesToRun);
            }
        }
    }
    

    只需确保在您的 Windows 服务项目的属性中将应用程序类型更改为控制台应用程序(这不会影响它在未以交互模式运行时作为 Windows 服务运行)。

    【讨论】:

      【解决方案3】:

      感谢大家的输入...不幸的是,我无法让任何这些工作,但是当我将事件记录器添加到我的作业执行功能时,我发现它每分钟都会写入事件日志正如预期的那样。

      我猜Console.WriteLine()Console.ReadKey() func 在 Windows 服务环境中什么都不做?

      注意:它们会做一些事情,但在后台作为服务而不是在控制台窗口中运行...

      【讨论】:

      • 如果我没记错的话,Console.Anything 会炸毁 Windows 服务。
      【解决方案4】:

      如果您想调试您的工作,请保持打开 Visual Studio,在您的工作代码中输入:

      System.Diagnostics.Debugger.Launch();

      这将允许您附加到 Visual Studio 中的调试器,您可以看到发生了什么。

      或者,如果您想让常用日志记录正常工作,请给我发送消息,因为我的 Windows 服务中一切正常。

      【讨论】:

        【解决方案5】:

        我们遇到了一些触发器失灵的问题。我added a global trigger listener 捕获与触发作业相关的事件,并提供调试问题所需的信息。

        我们还通过 Common.Logging 连接了 NLog 来捕获 Quartz 的内部日志(你可能注意到我们自己使用了Simple Logging Facade,所以这里也配置了这个组件)。这是我们的应用配置,希望它可以让您快速入门:

        <?xml version="1.0" encoding="utf-8" ?>
        <configuration>
            <configSections>
                <section name="quartz" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                <section name="slf" type="Slf.Config.SlfConfigurationSection, slf"/>
                <sectionGroup name="common">
                    <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
                </sectionGroup>
            </configSections>
        
        
            <quartz>
                <add key="quartz.threadPool.threadCount" value="20" />
                <add key="quartz.jobStore.misfireThreshold" value="420000" /><!-- 7 minutes -->
            </quartz>
        
        
            <slf>
                <factories>
                    <factory name="nlog" type="SLF.NLogFacade.NLogLoggerFactory, SLF.NLogFacade"/>
                </factories>
        
                <loggers>
                    <logger factory="nlog"/>
                </loggers>
            </slf>
        
            <common>
                <logging>
                    <factoryAdapter type="Common.Logging.NLog.NLogLoggerFactoryAdapter, Common.Logging.NLog">
                        <arg key="configType" value="FILE" />
                        <arg key="configFile" value="~/NLog.config" />
                    </factoryAdapter>
                </logging>
            </common>
        
        </configuration>
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多