zoukankan      html  css  js  c++  java
  • Quartz.Net 学习随手记之04 构建Windows Service承载服务

    为什么选择Windows Service作为承载主体?

    Quartz.Net只是一个作业调度框架,其承载的主体没有限制,可以是ConsoleApp, WebForm, WebApp, MVC etc. 但是只有Windows Service是最稳定的,而且支持自启动

    如何选择合适的Windows Service?

    直接使用Quartz.Net自动的Server端,即Windows Service + Topself,优点:无需重复开发、跨平台

    安装自带的Windows Service

    1. 新建目录

    目录:D:\Developer\QuartzNET\Server,并拷贝如下文件(之所以新建拷贝,是不想影响原有的代码)

    2. 命令安装

    应管理员身份打开CMD,依次输入如下命令

    cd D:\Developer\QuartzNET\Server

    D:

    Quartz.Server.exe install (对应卸载命令Quartz.Server.exe uninstall)

    Win热键+R,输入services.msc调出service列表,可以看到服务已安装

    输入以下命令启动服务(或者手动启动也可以,对应停止命令sc stop QuartzServer)

    创建调度作业

    1. 获得Remote Schedule

    var properties = new NameValueCollection();
    properties["quartz.scheduler.instanceName"] = "ServerScheduler";
    
    // set remoting expoter
    properties["quartz.scheduler.proxy"] = "true";
    properties["quartz.scheduler.proxy.address"] = string.Format("tcp://{0}:{1}/{2}", "localhost", "555",
                                                                 "QuartzScheduler");
    
    // Get a reference to the scheduler
    var sf = new StdSchedulerFactory(properties);
    
    return sf.GetScheduler();

    为什么是上面三个属性,因为Server服务端公布的属性如下(详见quartz.config文件)

    # You can configure your scheduler in either <quartz>
      configuration section
      # or in quartz properties file
      # Configuration section has precedence
    
      quartz.scheduler.instanceName = ServerScheduler
    
      # configure thread pool info
      quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
      quartz.threadPool.threadCount = 10
      quartz.threadPool.threadPriority = Normal
    
      # job initialization plugin handles our xml reading, without it defaults are used
      quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
      quartz.plugin.xml.fileNames = ~/quartz_jobs.xml
    
      # export this server to remoting context
      quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
      quartz.scheduler.exporter.port = 555
      quartz.scheduler.exporter.bindName = QuartzScheduler
      quartz.scheduler.exporter.channelType = tcp
      quartz.scheduler.exporter.channelName = httpQuartz

    ScheduleJob如下

    public void Run()
    {
        // Get an instance of the Quartz.Net scheduler
        var schd = GetScheduler();
    
        // Start the scheduler if its in standby
        if (!schd.IsStarted)
            schd.Start();
    
        // Define the Job to be scheduled
        var job = JobBuilder.Create<HelloWorldJob>()
            .WithIdentity("WriteHelloToLog", "IT")
            .RequestRecovery()
            .Build();
    
        // Associate a trigger with the Job
        var trigger = (ICronTrigger)TriggerBuilder.Create()
            .WithIdentity("WriteHelloToLog", "IT")
            .WithCronSchedule("0 0/1 * 1/1 * ? *") // visit http://www.cronmaker.com/ Queues the job every minute
            .StartAt(DateTime.UtcNow)
            .WithPriority(1)
            .Build();
    
        //schd.DeleteJob(new JobKey("WriteHelloToLog", "IT"));
        // Validate that the job doesn't already exists
        if (!schd.CheckExists(job.Key))
        {
            var schedule = schd.ScheduleJob(job, trigger);
            Console.WriteLine("Job '{0}' scheduled for '{1}'", "WriteHelloToLog", schedule.ToString("r"));
        }
    }

    具体的HelloWorldJob如下

    View Code
    class HelloWorldJob : IJob
    {
    
        private static readonly ILog Log = LogManager.GetLogger(typeof(HelloWorldJob));
    
        /// <summary> 
            /// Empty constructor for job initilization
            /// <para>
            /// Quartz requires a public empty constructor so that the
            /// scheduler can instantiate the class whenever it needs.
            /// </para>
            /// </summary>
        public HelloWorldJob()
            {
    
            }
    
        public void Execute(IJobExecutionContext context)
        {
            try
            {
                Log.InfoFormat("{0}****{0}Job {1} fired @ {2} next scheduled for {3}{0}***{0}",
                                                                        Environment.NewLine,
                                                                        context.JobDetail.Key,
                                                                        context.FireTimeUtc.Value.ToString("r"),
                                                                        context.NextFireTimeUtc.Value.ToString("r"));
    
    
                Log.InfoFormat("{0}***{0}Hello World!{0}***{0}", Environment.NewLine);
            }
            catch (Exception ex)
            {
                Log.InfoFormat("{0}***{0}Failed: {1}{0}***{0}", Environment.NewLine, ex.Message);
            }
        }
    }

    控制台

    View Code
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // Infinite loop, so that the console doesn't close on you
                while (true)
                {
                    var sj = new ScheduledJob();
                    sj.Run();
    
                    Console.WriteLine(@"{0}Check Quartz.net\Trace\application.log.txt for Job updates{0}",
                                        Environment.NewLine);
    
                    Console.WriteLine("{0}Press Ctrl^C to close the window. The job will continue " +
                                        "to run via Quartz.Net windows service, " +
                                        "see job activity in the Quartz.Net Trace file...{0}",
                                        Environment.NewLine);
    
                    Thread.Sleep(10000 * 100000);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed: {0}", ex.Message);
                Console.ReadKey();
            }
        }
    }

    编译无误后,把编译后的exe文件拷贝到D:\Developer\QuartzNET\Server目录下,然后停止并重启QuartzServer服务。

    最后F5本作业,然后按Crtl+C推出,你会看到服务已成功运行,作业也成功执行

    释疑

    1. 为什么要重启服务?

    编译拷贝文件后一定要重启服务,因为Windows Service如果不重启的话无法识别新的exe文件或者DLL文件

    2. 创建基于此Windows Service的调度作业的关键是什么?

    调度作业和公布的Remote Schedule要保持一致,即tcp://localhost:555/QuartzScheduler,具体看quartz.config配置

    3. 可以保存调度作业到数据库吗?

    可以,在server端的quartz.config中附加如下配置

      # job store
      quartz.jobStore.misfireThreshold =60000
      quartz.jobStore.type = Quartz.Impl.AdoJobStore.JobStoreTX, Quartz
      quartz.jobStore.driverDelegateType = Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz
      quartz.jobStore.useProperties = true
      quartz.jobStore.dataSource = default
      quartz.jobStore.tablePrefix = QRTZ_
      quartz.dataSource.default.connectionString = Server=10.7.11.114;Database=CCLOG_QuartzNET;User Id=CCLOG_LMS_PRO;Password=Testlms20!!
      quartz.dataSource.default.provider = SqlServer-20

    4. 使用log4net记录日志

    更改Server的Quartz.Server.exe.config配置文件如下

    View Code
    <?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="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
        <sectionGroup name="common">
          <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging" />
        </sectionGroup>
      </configSections>  
      <common>
        <logging>
          <factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter, Common.Logging.Log4net1211">
            <arg key="configType" value="INLINE" />
            <!--<arg key="configFile" value="Trace/ApplicationLog.txt" />-->
            <!--<arg key="level" value="INFO" />-->
          </factoryAdapter>
        </logging>
      </common>
      <appSettings>
        <add key="log4net.Internal.Debug" value="false"/>
      </appSettings>
      <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%d [%t] %-5p %l - %m%n" />
          </layout>
        </appender>
        <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender">
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%d [%t] %-5p %l - %m%n" />
          </layout>
        </appender>
        <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
          <param name="File" value="Trace/ApplicationLog.txt" />
          <appendToFile value="true" />
    
          <!--Make the rolling file name with the date and size-->
          <rollingStyle value="Composite" />
          <datePattern value="yyyyMM" />
          <maxSizeRollBackups value="100" />
          <maximumFileSize value="2MB" />
    
          <!--Make the rolling file name like this MyQuartzLog201303.txt, or the deault will be MyQuartzLog.txt201303-->
          <PreserveLogFileNameExtension value="true" />
          <staticLogFileName value="false" />
          <layout type="log4net.Layout.PatternLayout">
            <param name="ConversionPattern" value="%-5p%d{yyyy-MM-dd hh:mm:ss} – %m%n" />
          </layout>
        </appender>
        <root>
          <level value="INFO" />
          <appender-ref ref="ConsoleAppender" />
          <appender-ref ref="EventLogAppender" />
          <appender-ref ref="RollingFileAppender"/>
        </root>
      </log4net>
    </configuration>
    作者:舍长
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Jessica's Reading Problem POJ
    FatMouse and Cheese HDU
    How many ways HDU
    Humble Numbers HDU
    Doing Homework again
    Stacks of Flapjacks UVA
    Party Games UVA
    24. 两两交换链表中的节点
    面试题 03.04. 化栈为队
    999. 可以被一步捕获的棋子数
  • 原文地址:https://www.cnblogs.com/panchunting/p/3017075.html
Copyright © 2011-2022 走看看