Create windows services step by step using the topshelf component (2) schedule using quartz.net

Time:2020-2-26

The previous article described how to use the topshelf component to quickly create windows services. Next, it introduced how to use quartz.net

As for the benefits of quartz.net, online search is a lot. I will not introduce it any more.

First, introduce the plug-ins to be used:

I use version 2.6.2 of quartz, but I don’t use version 3.0 or above, because you will know that you will print out a lot of log files,

I haven’t found a way to block it. If any of you have it, please share it. I’ll learn it, haha.

The overall project structure is as follows:

The appconfighhelper file needs to be changed. Add the following properties
/// 
         ///Program identification
         /// 
         [ConfigurationProperty("AppKey", IsRequired = true)]
         public string AppKey
         {
             get { return base["AppKey"].ToString(); }
             internal set { base["AppKey"] = value; }
         }
 
         /// 
         ///Assembly information
         /// 
         [ConfigurationProperty("TypeInfo", IsRequired = true)]
         public string TypeInfo
         {
             get { return base["TypeInfo"].ToString(); }
             internal set { base["TypeInfo"] = value; }
         }

The AppConfig file is also slightly changed

Processprintlogservice is the logical program file to be executed by windows service. It can perform any function you want
Processservice.processprintlogservice. Processservice is the format of namespace. Class name and class name. It is used for the back reflection assembly. If you want to execute other business logic programs, just change the configuration here,
The business logic content of processprintlogservice is as follows: This is the business logic we want to execute. Print a section of log content regularly. You can create a class library to store the business logic you want to execute
namespace ProcessService
 {
     /// 
     ///Log printing service
     /// 
     public class ProcessPrintLogService
     {
         private Logger log = LogManager.GetCurrentClassLogger();
         /// 
         ///Service entrance
         /// 
         public void DoWork()
         {
             //Log.Info ("the service of the leaderboard starts to execute");
             try
             {
                 PrintLogMethod();
             }
             catch (Exception ex)
             {
                 Log. Error (string. Format ("leaderboard service exception, reason: {0}", ex));
             }
             finally
             {
                 //Log.info ("*************** leaderboard service end execution **********);
             }
         }
 
 
         private void PrintLogMethod()
         {
             Log. Trace (string. Format ("I am log: {0} sign", thread. Currentthread. Managedthreadid));
         }
     }
 }

Then we need to add two new files: quartz.config and quartz_jobs.xml

The contents of the quartz.config file are as follows:

# You can configure your scheduler in either  configuration section
# or in quartz properties file
# Configuration section has precedence

quartz.scheduler.instanceName = ServiceQuartzScheduler

# 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

#3.0 the following configuration is used for the above
# quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins
# 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
quartz.scheduler.instanceName =Servicequartzscheduler is the instance name of the schedule, which can be customized at will
The others are fixed and do not need to be modified
The contents of quartz ﹣ jobs.xml file are as follows:
true
  
  
    
    
      ProcessPrintLogService
      ProcessPrintLogServiceGroup
      Log printing service
      Quartz.WinService.QuartzWork,Quartz.WinService
      true
      false
    
    
      
        ProcessPrintLogServiceTrigger
        ProcessPrintLogServiceTriggerGroup
        ProcessPrintLogService
        ProcessPrintLogServiceGroup
        SmartPolicy
        0/3 * * * * ?

This XML configuration file is very important. Let’s focus on it

First, both the job node and the trigger node can define multiple, that is, a service can run multiple different business logic programs

Let’s talk about the job node first

  • Name (required) task name. The names of multiple jobs cannot be the same. Generally, the name of business logic program is used here
  • Group (optional) task group, which is used to identify the task group. Generally, the name of business logic program + group suffix are as follows:sampleGroup
  • Description (optional) task description, used to describe the specific content of the task, such as:Print log service
  • Job type (required) task type, task specific type and its assembly, format: the class name and assembly name of the ijob interface, including the full namespace, such as:Quartz.Server.SampleJob, Quartz.Server
  • The specific function of dual (optional) is unknown. In the official example, it defaults to true, such as:true
  • The specific function of recover (optional) is unknown. In the official example, it defaults to false, such as:false

The task type called by the job type node here needs to be described. What is set here is the quartzwork class in the upper project structure. The specific content is as follows:

namespace Quartz.WinService
{
    public class QuartzWork : IJob
    {
        private Logger log = LogManager.GetCurrentClassLogger();
        //Concurrentdictionary is a thread safe dictionary set
        private readonly ConcurrentDictionary> _dynamicCache = new ConcurrentDictionary>();

        //Record whether the current working interface has worked
        private static readonly Dictionary WorkingNow = new Dictionary();

        /// 
        ///Task scheduling execution entry
        ///Implement the execute method of ijob, write the business logic to be processed in the execute method, and the system will process it regularly according to the quartz configuration
        ///When the trigger of a job is triggered, the execute (..) method is executed in the scheduler's worker thread
        /// 
        /// 
        public void Execute(IJobExecutionContext context)
        {
            try
            {
                Task.Factory.StartNew(() =>
                {
                    var service = AppConfigHelper.Initity();
                    WorkNow(service);
                });
            }
            catch (Exception ex)
            {
                Log. Fatal ($"execute quartz scheduling exception, information: {ex.message}");
            }
            //Return task. Fromresult (true); // returns a bool type task, which needs to be used above version 3.0 of quartz
        }

        private void WorkNow(AppConfigHelper service)
        {
            String key = service.appkey; // key value
            lock (this)
            {
                if (!WorkingNow.ContainsKey(key))
                {
                    WorkingNow.Add(key, false);
                }
                //Jump out if executed
                if (WorkingNow[key])
                {
                    Log.trace ($"service key: {key} is running, service ignored this time");
                    return;
                }
                //And set to execution state
                WorkingNow[key] = true;
            }
            try
            {
                Var type = type.gettype (service. TypeInfo); // set here through the app.config file
                if (type != null)
                {
                    //Creating an instance of the specified type is equivalent to reflecting a new object instance
                    var provider = Activator.CreateInstance(type);
                    Dynamic(provider, "DoWork", key);
                }
                else
                {
                    Log. Error ($"task: {key} instantiation failed");
                }
            }
            catch (Exception ex)
            {
                Log.Fatal ($"task: {key} instantiation exception: {ex.Message}");
            }
            finally
            {
                WorkingNow[key] = false;
            }
        }

        //Official definition of delegate.createdelegate: used to dynamically create a delegate of a specified type, which can call a specified method on a specified class instance.
        //In short: the method specified in the specified class can be called, provided that the class needs to be instantiated when using
        //Getoradd function will judge whether there is corresponding content according to the specified key, and return if there is
        //Dynamicinvoke dynamically calls the delegate method
        //Obj parameter is the instantiation object of the specified class, and methodname specifies the method name in the class
        private void Dynamic(object obj, string methodName, string key)
        {
            var dmc = _dynamicCache.GetOrAdd(key, t => new Lazy(() => Delegate.CreateDelegate(typeof(Action), obj, methodName)));
            DMC. Value. Dynamicinvoke(); // dynamically call the delegate method
        }

    }
}

Let’s move onTrigger node

Trigger task trigger, which is used to define the way to start a job. A job can define multiple triggers, and multiple triggers can execute independently,

Only one trigger type (calendar interval, simple, cron) must be defined in each trigger

To put it bluntly, if you want a service to perform tasks in the time periods of 8:00-18:00 a.m. and 00:00-6:00 a.m., you can set two trigger triggers,

Set these two time periods to achieve the results you want. How about that? It’s amazing

  • Name (required) trigger name, generally ending with business logic class + trigger. If multiple trigger nodes need to be set, the name cannot be the same
  • Group (optional) trigger group usually ends with business logic class + trigger group. There are multiple trigger nodes with the same name
  • Job name (required) the name of the task to be scheduled. The job name must be the same as the name in the corresponding job node
  • Job group the group to which a job belongs. The value must be exactly the same as the group name in the job node
  • Misfire instruction doesn’t know what to do, just write itSmartPolicy
  • Cron expression (required) cron expression, such as:0/10 * * * * ?Every 10 seconds

It should be noted that after modifying the quartz ﹣ jobs.xml file, the quartz service will not reload the file by default. To make the modified file effective, you need to restart the service.

In addition, the quartz.config file andThe quartz ﹣ jobs.xml file needs to be set in the project. Right click the property – > copy to the output directory – > always copy

 

The service registration file registservice adds the function of automatic restart. The complete contents are as follows:

namespace Quartz.WinService
{
    public class RegistService
    {
        /// 
        ///Registration entry
        /// 
        ///Profile
        ///Register or not
        public static void Regist(AppConfigHelper config, bool isreg = false)
        {
            //You can also use hostfactory. Run() instead of hostfactory. New() here
            var host = HostFactory.New(x =>
            {
                x.Service(s =>
                {
                    //Building a service instance through new quartzhost() 
                    s.ConstructUsing(name => new QuartzHost());
                    //What to do when the service starts
                    s.WhenStarted(tc => tc.Start());
                    //What to do when the service stops
                    s.WhenStopped(tc => tc.Stop());
                    //What to do when the service is suspended
                    s.WhenPaused(w => w.Stop());
                    //What to do when the service continues
                    s.WhenContinued(w => w.Start());
                });

                If (! Isreg) return; // false means not to register

                //Service runs with local system account
                x.RunAsLocalSystem();

                //Enable automatic restart service
                x.EnableServiceRecovery(v =>
                {
                    v. Restartservice (2); // restart in 2 minutes
                });

                //Description of the service
                x.SetDescription(config.Description);
                //Display name of the service
                x.SetDisplayName(config.ServiceName);
                //The name of the service (it is better not to include spaces or characters with space attribute) windows service name cannot be duplicate.
                x.SetServiceName(config.ServiceName);
            }). Run(); // this method is not required to start the service if hostfactory. Run() is used
        }
    }
}

The QuartzHost class that is invoked in service registration is as follows:

namespace Quartz.WinService
{
    public class QuartzHost
    {
        private Logger log = LogManager.GetCurrentClassLogger();
        private readonly IScheduler scheduler;
        public QuartzHost()
        {
            //Initialize scheduling service
            //Scheduler = stdschedulerfactory. Getdefaultscheduler(). Result; // above 3.0
            scheduler = StdSchedulerFactory.GetDefaultScheduler();
        }

        /// 
        ///Scheduling start
        /// 
        public void Start()
        {
            try
            {
                scheduler.Start();
                Log.info ("quartz scheduling service starts to work");
            }
            catch (Exception ex)
            {
                Log.fatal (string. Format ("the quartz scheduling service started to be abnormal! Error message: {0} ", ex));
                throw;
            }
        }

        /// 
        ///Scheduling stop
        /// 
        public void Stop()
        {
            try
            {
                if (scheduler != null)
                {
                    scheduler.Shutdown(true);
                }
                Log.info ("quartz scheduling service end work");
            }
            catch (Exception ex)
            {
                Log.fatal (string. Format ("quartz scheduling service stop exception! Error message: {0} ", ex));
                throw;
            }
        }
    }
}
Project file address: https://gitee.com/gitee ABCD quartz.winservice.git reference document:

https://blog.csdn.net/clb929/article/details/90341485

https://blog.csdn.net/weixin_33948416/article/details/92989386

https://www.cnblogs.com/lzrabbit/archive/2012/04/14/2446942.html