.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. IntelliJ IDEA设置主题和背景图片(背景色)

    设置主题以及背景图片 设置代码背景颜色

  2. E04 【买衣服】Do you have this T-shirt in red?

    核心句型 Do you have this T-shirt in red? 这样的T恤衫,你们有红色的吗? 场景对话: A:Excuse me,do you have this T-shirt in ...

  3. python中调用httpclient接口的实例代码

    #coding=utf-8 import httplib,urllib #get调用 httpClient=None try: params=urllib.urlencode({'account':' ...

  4. day6_7.4总结数据类型的可变不可变

    续昨天: 列表的常用方法: 1.chear() 用途:清空列表,其返回值无,返回none. list1=[1,2,3,4,5] a=list1.clear() print(list1) print(a ...

  5. SpringCloud介绍(一)

    Spring Cloud 是一套完整的微服务解决方案,基于 Spring Boot 框架,准确的说,它不是一个框架,而是一个大的容器,它将市面上较好的微服务框架集成进来,从而简化了开发者的代码量. 一 ...

  6. zz深度学习目标检测2014至201901综述

    论文学习-深度学习目标检测2014至201901综述-Deep Learning for Generic Object Detection A Survey  发表于 2019-02-14 |  更新 ...

  7. SpringBoot 指定资源文件的位置

    SpringBoot默认的存放静态资源文件的位置是在: 里面的. 注:SpringBoot中的src/main/resources/资源文件夹对应classpath:. 默认存放静态资源文件的位置,在 ...

  8. Excel 文本函数

    1.FIND函数--要查找的字符在 字符串中 的  位置 FIND(find_text,within_text,start_num) Find_text 是要查找的字符串. Within_text 是 ...

  9. Layui 文件上传 附带data数据

    配置项中增加参数: , data: { CaseId: function () { return $("#CaseId option:selected").val(); }, Ca ...

  10. requests--超时设置,代理设置,身份认证

    超时设置 你可以告诉 requests 在经过以 timeout 参数设定的秒数时间之后停止等待响应.基本上所有的接口都应该使用这一参数.如果不使用,你的程序可能会永远失去响应 import requ ...