.net core+topshelf+quartz创建windows定时任务服务
.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定时任务服务的更多相关文章
- 使用.Net Core 2.2创建windows服务
使用.Net Core 2.2创建windows服务 我的环境 win 10 home Visual Studio 2019 v16.1.3 安装有.net core 2.2 创建项目 编辑项目文件 ...
- .NET创建Windows定时任务
创建Windows定时任务教程 1.创建一个控制台应用程序,保证程序正常运行. 2.右键点击我的电脑->点击管理. 3.在计算机管理弹出框->展开计算机管理(本地)->展开系统工具- ...
- 【C#】C#创建Windows Service服务
目录结构: contents structure [+] 创建Windows服务 配置 安装Windows服务 在Visual Studio中调试 常见问题 最近写了一个TCP连接的程序,由于这种通信 ...
- Topshelf便捷创建Windows服务
结合Quartz.net学习,前提已经创建了一个定时任务,可见 <定时调度框架:Quartz.net> (基于配置文件形式) 首先引用Topshelf.dll 自定义服务TestServi ...
- TopShelf框架创建Windows服务作为Remoting的宿主案例:
1.创建服务 using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...
- 通过TopShelf简单创建windows service
目前很多项目都是B/S架构的,我们经常会用到webapi.MVC等框架,实际项目中可能不仅仅是一些数据的增删改查,需要对数据进行计算,但是将计算逻辑放到api层又会拖累整个项目的运行速度,从而会写一些 ...
- TopShelf+Quartz.net 实现window服务
Quartz.NET官网 TopShelf 网址 代码地址:https://github.com/SeaLee02/ProjectDemo/tree/master/WindowServerDemo ...
- .Net core使用Quartz.Net 实现定时任务
很多情况下,我们需要完成一些定时执行的功能,用很多定时工具,像:hangfire,TimeJob,以及Quartz.net,不过quartz.net 比较精确一些,功能也比较强大,所以我选择了Quar ...
- 如何创建Windows定时任务
我们经常使用电脑,有没有那么一个瞬间想着要是电脑可以每隔一段时间,自动处理一件事情就好了呢? 其实Windows还真有这样的功能,很多软件检测更新就是通过这个方法实现的. 这次我们来做一个简易的喝水提 ...
随机推荐
- 当MySQL数据库遇到Syn Flooding
Syn攻击是最常见又最容易被利用的一种攻击手法,利用TCP协议的缺陷,发送大量伪造TCP连接请求,常用假冒的IP发来海量的SYN包,被攻击的服务器回应SYN+ACK,因为对方是假冒的IP,永远收不到包 ...
- 简单使用:SpringBoot整合Redis
1.导入依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- 3.1 Spark概述
一.Spark简介 1.Spark的特点 特点1:运行速度快(内存计算,循环数据流.有向无环图设计机制) 把所有针对数据集的操作转换成一张有向无环图,整个执行引擎调度都是基于这个有向无环图,对这个有向 ...
- Rust语言中的常量,变量,运算符,数据类型
简单练练, 夏天太热. const MAX_POINTS: u32 = 100_100; fn main() { let mut x = 5; let y = 5; let y = y + 1; le ...
- 08配置基础路径 同时导出一个函数和一个变量 封装微信请求Api
地址===>https://www.bilibili.com/video/av58993009/?p=46 1==>配置基础路径同时导出一个函数和一个变量 var mynetwork= f ...
- No archetypes currently available. The archetype list will refresh when the indexes finish updating
配置方法: 1. 在卡住的而画面点击"config" 2. 点击"Add remote catalog", 然后设置华为云的maven仓库地址, 然后点击&qu ...
- Docker底层原理(三)
1. 我们运行:docker run hello-world 由于本地没有hello-world,所以会远程下载一个hello-world的镜像,并在容器内运行. 2. docker run干了什么?
- BIO/NIO/AIO的区分(十四)
BIO:同步阻塞IO(平常说的IO指的是BIO)NIO:同步非阻塞IOAIO:异步非阻塞IO io操作分为两部分,发起io请求,和io数据读写. 阻塞.非阻塞主要是针对线程发起io请求后,是否立即返回 ...
- http的get请求与post请求区别
原文 http://www.w3school.com.cn/tags/html_ref_httpmethods.asp GET 方法 请注意,查询字符串(名称/值对)是在 GET 请求的 URL 中发 ...
- (translation.E004) You have provided a value for the LANGUAGE_CODE setting that is not in the LANGUAGES setting.
django3.0开始LANGUAGE_CODE前面必须配相应的LANGUAGES配置如下: from django.utils.translation import gettext_lazy as ...