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>
    作者:舍长
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    单击按钮左键弹起菜单
    高亮选中MEMO某一行
    DelphiTXT文档编辑器
    桌面名人名言
    判断richtextbox选中的是否为图片
    数组
    解决Linux下IDEA无法使用ibus输入法的问题和tip乱码
    Spring实现AOP的多种方式
    java术语(PO/POJO/VO/BO/DAO/DTO)
    idea intellij对Spring进行单元测试
  • 原文地址:https://www.cnblogs.com/panchunting/p/3017075.html
Copyright © 2011-2022 走看看