当前位置: 移动技术网 > IT编程>开发语言>.net > .net core+topshelf+quartz创建windows定时任务服务

.net core+topshelf+quartz创建windows定时任务服务

2019年10月30日  | 移动技术网IT编程  | 我要评论

haswell架构,欧比斯疾走,金煌棋牌

.net core+topshelf+quartz创建windows定时任务服务


准备工作

  • 创建.net core 控制台应用程序,这里不做过多介绍
  • 添加topshelf包:topshelf;
  • 添加quartz包:quartz、quartz.plugins;
  • 添加依赖注入包:microsoft.extensions.dependencyinjection;
  • 添加读取配置文件包:microsoft.extensions.configuration.json;
  • 添加访问数据库包:microsoft.entityframeworkcore;
  • 添加日志包:serilog、serilog.sinks.console、serilog.sinks.file

配置quartz

  • 创建appsettings.json文件,右键文件属性,并更改属性为始终复制 内容
{
"quartz": {
    "scheduler": {
      "instancename": "job"
    },
    "threadpool": {
      "type": "quartz.simpl.simplethreadpool, quartz",
      "threadpriority": "normal",
      "threadcount": 10
    },
    "plugin": {
      "jobinitializer": {
        "type": "quartz.plugin.xml.xmlschedulingdataprocessorplugin, quartz.plugins",
        "filenames": "quartz_jobs.xml"
      }
    }
  }
}
  • 创建quartzoption 类
namespace job
{
    public class quartzoption
    {
        public quartzoption(iconfiguration config)
        {
            if (config == null)
            {
                throw new argumentnullexception(nameof(config));
            }

            var section = config.getsection("quartz");
            section.bind(this);
        }

        public scheduler scheduler { get; set; }

        public threadpool threadpool { get; set; }

        public plugin plugin { get; set; }

        public namevaluecollection toproperties()
        {
            var properties = new namevaluecollection
            {
                ["quartz.scheduler.instancename"] = scheduler?.instancename,
                ["quartz.threadpool.type"] = threadpool?.type,
                ["quartz.threadpool.threadpriority"] = threadpool?.threadpriority,
                ["quartz.threadpool.threadcount"] = threadpool?.threadcount.tostring(),
                ["quartz.plugin.jobinitializer.type"] = plugin?.jobinitializer?.type,
                ["quartz.plugin.jobinitializer.filenames"] = plugin?.jobinitializer?.filenames
            };

            return properties;
        }
    }

    public class scheduler
    {
        public string instancename { get; set; }
    }

    public class threadpool
    {
        public string type { get; set; }

        public string threadpriority { get; set; }

        public int threadcount { get; set; }
    }

    public class plugin
    {
        public jobinitializer jobinitializer { get; set; }
    }

    public class jobinitializer
    {
        public string type { get; set; }
        public string filenames { get; set; }
    }
}

添加一个job

namespace job
{
    public class syncjob : ijob
    {
        private readonly iservice _service;

        public syncjob(iservice service)
        {
            _service = service;
        }

        public async task execute(ijobexecutioncontext context)
        {
            log.information("同步开始...");
            _service.dosomething();
        }
    }
}

实现ijobfactory

namespace job
{
    public class jobfactory : ijobfactory
    {
        protected readonly iserviceprovider container;

        public jobfactory(iserviceprovider container)
        {
            container = container;
        }

        public ijob newjob(triggerfiredbundle bundle, ischeduler scheduler)
        {
            return container.getservice(bundle.jobdetail.jobtype) as ijob;
        }

        public void returnjob(ijob job)
        {
            (job as idisposable)?.dispose();
        }
    }
}

创建quartz调度的配置文件 quartz_jobs.xml

文件名与appsetting中的quartz.plugin.jobinitializer.filenames 保持一致

<?xml version="1.0" encoding="utf-8"?>

<job-scheduling-data xmlns="http://quartznet.sourceforge.net/jobschedulingdata"
                     xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
                     version="2.0">

  <processing-directives>
    <overwrite-existing-data>true</overwrite-existing-data>
  </processing-directives>

  <schedule>
    <job>
      <name>syncjob</name>
      <group>syncgroup</group>
      <description>数据同步任务</description>
      <job-type>mille.job.syncjob, mille.job</job-type>
      <durable>true</durable>
      <recover>false</recover>
    </job>
    <trigger>
      <cron>
        <name>synctrigger</name>
        <group>syncgroup</group>
        <description>同步触发器</description>
        <job-name>syncdjob</job-name>
        <job-group>syncgroup</job-group>
         <!--每晚23:50跑一次,具体参见cron表达式-->
        <cron-expression>0 50 23 ? * *</cron-expression>
      </cron>
    </trigger>

    <!--<trigger>
      <simple>
        <name>synctrigger</name>
        <group>syncgroup</group>
        <description>数据同步触发器</description>
        <job-name>syncjob</job-name>
        <job-group>syncgroup</job-group>
        <repeat-count>-1</repeat-count>
        2s跑一次
        <repeat-interval>2000</repeat-interval>
      </simple>
    </trigger>-->
  </schedule>
</job-scheduling-data>

添加一个类,此类用户服务启动调用

namespace job
{
    public class syncservice
    {
        public async task startasync()
        {
            var provider = registerservices();
            scheduler = provider.getservice(typeof(ischeduler)) as ischeduler;
            await scheduler.start();
            log.information("quartz调度已启动...");
        }

        public async task stopasync()
        {
            await scheduler.shutdown();
            log.information("quartz调度结束...");
            log.closeandflush();
        }

        #region utils
        private ischeduler scheduler { get; set; }
        private static serviceprovider registerservices()
        {
            log.information("配置依赖注入...");
            var configuration = readfromappsettings();
            var services = new servicecollection();

            #region 

            services.addscoped<syncservice>();
            services.adddbcontext<datacontext>(opt => opt.usemysql(configuration.getconnectionstring("connstr")));
            services.addscoped<iservice,service>();

            #endregion

            #region quartz

            log.information("配置quartz...");
            services.addscoped<ijobfactory, jobfactory>();
            services.addsingleton(service =>
            {
                var option = new quartzoption(configuration);
                var sf = new stdschedulerfactory(option.toproperties());
                var scheduler = sf.getscheduler().result;
                scheduler.jobfactory = service.getservice<ijobfactory>();
                return scheduler;
            });
            services.addscoped<syncjob>(); 
            //此处不能写成services.addscoped<ijob,syncjob>(); 会造成在找不到syncjob

            #endregion

            var provider = services.buildserviceprovider();
            return provider;
        }

        private static iconfigurationroot readfromappsettings()
        {
            //读取appsettings.json
            return new configurationbuilder()
                .setbasepath(directory.getcurrentdirectory())
                .addjsonfile("appsettings.json", false)
                .build();
        }
    
        #endregion
    }
}

配置topshelf

详情参见

namespace job
{
    public class program
    {
        public static void main(string[] args)
        {
            instancelog();
            var rc = hostfactory.run(x =>
            {
                x.service<syncservice>(s =>
                {
                    s.constructusing(name => new syncservice());
                    s.whenstarted(async tc => await tc.startasync()); //调用此方法前勿有太多操作,会造成服务启动失败
                    s.whenstopped(async tc => await tc.stopasync());
                });
                x.runaslocalsystem();

                x.setdescription("syncjob description");
                x.setdisplayname("syncjob displayname");
                x.setservicename("syncjob servicename");
            });
            var exitcode = (int)convert.changetype(rc, rc.gettypecode());
            environment.exitcode = exitcode;
        }

        private static void instancelog()
        {
            //配置serilog
            var template = "{timestamp:hh:mm:ss} [{level:u3}] {message}{newline}{exception}";
            log.logger = new loggerconfiguration()
                .writeto.file(path: "logs/log.txt", outputtemplate: template, rollinginterval: rollinginterval.day)
                .writeto.console(logeventlevel.information)
                .createlogger();
        }
    }
}

然后在项目文件中加上项目的运行环境相关配置

<propertygroup>
    <outputtype>exe</outputtype>
    <targetframework>netcoreapp3.0</targetframework>
    <runtimeidentifier>win7-x64</runtimeidentifier>//不同的环境rid不同
</propertygroup>

运行

编译项目之后,进入到/bin/debug/netcoreapp3.0/win7-x64目录,在此处以管理员运行cmd,然后执行 依次job.exe install 安装服务, job.exe start 启动服务

如果遇到启动服务时报 1053错误:服务没有及时响应启动或控制请求。检查start函数调用之前是否还有其他操作,如有,请将这些操作移动到start调用后执行;本人就是由于在start之前执行了依赖注入等操作,导致服务启动失败,故写下这篇文章

此文章有部分借鉴其他博主的博客,如有侵权,请联系删除

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网