.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

详情参见 https://topshelf.readthedocs.io/en/latest/configuration/quickstart.html

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之前执行了依赖注入等操作,导致服务启动失败,故写下这篇文章

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

.net core+topshelf+quartz创建windows定时任务服务的更多相关文章

  1. 使用.Net Core 2.2创建windows服务

    使用.Net Core 2.2创建windows服务 我的环境 win 10 home Visual Studio 2019 v16.1.3 安装有.net core 2.2 创建项目 编辑项目文件 ...

  2. .NET创建Windows定时任务

    创建Windows定时任务教程 1.创建一个控制台应用程序,保证程序正常运行. 2.右键点击我的电脑->点击管理. 3.在计算机管理弹出框->展开计算机管理(本地)->展开系统工具- ...

  3. 【C#】C#创建Windows Service服务

    目录结构: contents structure [+] 创建Windows服务 配置 安装Windows服务 在Visual Studio中调试 常见问题 最近写了一个TCP连接的程序,由于这种通信 ...

  4. Topshelf便捷创建Windows服务

    结合Quartz.net学习,前提已经创建了一个定时任务,可见 <定时调度框架:Quartz.net> (基于配置文件形式) 首先引用Topshelf.dll 自定义服务TestServi ...

  5. TopShelf框架创建Windows服务作为Remoting的宿主案例:

    1.创建服务 using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...

  6. 通过TopShelf简单创建windows service

    目前很多项目都是B/S架构的,我们经常会用到webapi.MVC等框架,实际项目中可能不仅仅是一些数据的增删改查,需要对数据进行计算,但是将计算逻辑放到api层又会拖累整个项目的运行速度,从而会写一些 ...

  7. TopShelf+Quartz.net 实现window服务

    Quartz.NET官网   TopShelf 网址 代码地址:https://github.com/SeaLee02/ProjectDemo/tree/master/WindowServerDemo ...

  8. .Net core使用Quartz.Net 实现定时任务

    很多情况下,我们需要完成一些定时执行的功能,用很多定时工具,像:hangfire,TimeJob,以及Quartz.net,不过quartz.net 比较精确一些,功能也比较强大,所以我选择了Quar ...

  9. 如何创建Windows定时任务

    我们经常使用电脑,有没有那么一个瞬间想着要是电脑可以每隔一段时间,自动处理一件事情就好了呢? 其实Windows还真有这样的功能,很多软件检测更新就是通过这个方法实现的. 这次我们来做一个简易的喝水提 ...

随机推荐

  1. Nginx02(环境配置以及基本使用)

    一:Nginx环境配置 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet ...

  2. 安装教程-Xshell 5 远程连接工具的安装

    Xshell 5 远程连接工具的安装 1.实验描述 物理机中安装 Xshell 5 ,为实现 Linux 等操作系统提供远程连接的环境. 2.实验环境 物理机系统:Windows 10 企业版 3.实 ...

  3. 其他综合-CentOS 7 rsync+nginx实现公网yum源

    CentOS 7 rsync+nginx实现公网yum源 1.实验描述 镜像同步公网 yum 源上游 yum 源必须要支持 rsync 协议,否则不能使用 rsync 进行同步.CentOS 7.6 ...

  4. jenkins如何构建C#代码写的网站

    纯粹是因为同事习惯了写C#代码,开发的网站用C#编译, 对于习惯了用Maven编译的测试人员,真是一头雾水.不用jenkins吧,效率特别低,每次收到开发发过来的版本,还要进行数据库相关配置,是非常累 ...

  5. K8s集群中设置harbor仓库认证

    一,获取harbor的登陆用户名和密码(demo_user/demo_pwd) 二,使用kubectl命令生成secret(不同的namespace要分别生成secret,不共用) kubectl c ...

  6. JS高阶---原型链

    [大纲] [主体] 1.创建函数 注意:Object内置原生对象原来就有 2.添加实例方法 3.根据构造函数创建实例对象 原型链寻找 1.本身有在本身找 2.本身没有往摸着隐式原型链往里找 或者再上层 ...

  7. 第十二周博客作业 <西北师范大学| 周安伟>

    第十二周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10831971 ...

  8. VC 静态库与动态库(二)静态库创建与使用

    1.新建项目,创建项目和解决方案 StaticLibrary  这是静态库项目 G:\C++Learn\Library  Library文件夹用于存放库相关文件,包含静态库与后面的动态库工程和解决方案 ...

  9. 20180706模拟赛T3——神经衰弱

    文件名: card 题目类型: 传统题 时间限制: 1秒 内存限制: 128MB 编译优化: 无 题目描述 天然少女小雪非常喜欢玩一个叫做神经衰弱的游戏. 游戏规则是,有若干种牌,每种牌有若干对,开始 ...

  10. web框架--tornado之验证码实例

    tornado随机生成图片验证码 用python生成随机验证码需要借鉴一个插件,和一个io模块,实现起来也非常容易,当然也需要借鉴session来判断验证码是否错误,下面写一段用户登录验证带验证码的. ...