Topshelf+Quartz在.Net Core框架下的实现
在我们日常开发工作中,经常会运用到Quartz+Topshelf组件的组合来开发一些定时任务。那么在.Net Core下如何去使用呢?我自己尝试搭建了一个测试项目,过程中遇到了以下一些问题:
- Quartz 配置文件及版本问题。我们知道Quartz有2个配置文件,quartz.config和quartz.job.xml。前者负责组件初始化配置,后者负责job和triggle的配置。刚开始我是直接把framework下的配置文件直接拿过来用的,启动直接报错。主要问题在quartz.config
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
这一段上。原因Quartz最新版本已经将Plugin模块单独剥离出一个独立的DLL,这里的引用也要变化。需要Nuget上下载Quartz.Plugins组件,并将上一段改成
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins
-
DI问题。为了贴合.Net Core DI精神,我们也要来实现Console程序的DI功能。第一个问题是Job如何DI?首先我们需要自己去实现JobFactory
public class NewJobFactory : IJobFactory { private readonly IServiceProvider _serviceProvider; public NewJobFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { return _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob; } public void ReturnJob(IJob job) { var disposable = job as IDisposable; disposable?.Dispose(); } }
注入方式
IServiceCollection services = new ServiceCollection(); services.AddScoped<IJobFactory, NewJobFactory>(); services.AddSingleton(service => { var scheduler = StdSchedulerFactory.GetDefaultScheduler().Result; scheduler.JobFactory = service.GetService<IJobFactory>(); return scheduler; });
- Console程序的配置文件获取以及注入问题。众所周知,.Net Core下建立的Console程序就是一块白板,什么都没有。配置文件我们还得自己去建一个.json文件。并且需要自己从Nuget上下载的组件包(见后面项目结构截图)。加载方式如下
-
private IConfiguration ConfigureConfiguration() { //配置文件 var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); return builder.Build(); }
注入方式如下
if (configuration != null) { //iconfiguration注入 services.AddSingleton<IConfiguration>(configuration); } //自定义 option方式注入 services.Configure<AppSetting>(configuration);
这样就可以在后续的类代码中 直接通过DI方式获取IConfiguration对象或者你自己的Option对象了
贴上完整代码。
Program.cs
static void Main(string[] args) { HostFactory.Run(x => { x.Service<ServiceRunner>(); x.SetDescription("gt.dotnetcore.consolesample"); x.SetDisplayName("gt.dotnetcore.consolesample"); x.SetServiceName("gt.dotnetcore.consolesample"); x.StartAutomatically(); }); }
ServiceRunner.cs 启动的核心
public class ServiceRunner : ServiceControl { //private readonly IScheduler _scheduler; private IServiceProvider _serviceProvider; public ServiceRunner() { var configurationRoot = ConfigureConfiguration(); _serviceProvider = ConfigureServices(configurationRoot); } private IConfiguration ConfigureConfiguration() { //配置文件 var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); return builder.Build(); } private IServiceProvider ConfigureServices(IConfiguration configuration) { //依赖注入 IServiceCollection services = new ServiceCollection(); //后续需要使用log的话,这里需要注入 services.AddTransient<ILoggerFactory, LoggerFactory>(); services.AddTransient<ITest, TestBiz>(); services.AddScoped<IJobFactory, NewJobFactory>(); if (configuration != null) { //iconfiguration注入 services.AddSingleton<IConfiguration>(configuration); } //自定义 option方式注入 services.Configure<AppSetting>(configuration); //这里注意Job的注入方式,不要强制指定IJob实现方式!! services.AddScoped<TestJob>(); services.AddScoped<Test2Job>(); services.AddSingleton(service => { var scheduler = StdSchedulerFactory.GetDefaultScheduler().Result; scheduler.JobFactory = service.GetService<IJobFactory>(); return scheduler; }); //构建容器 return services.BuildServiceProvider(); } public bool Start(HostControl hostControl) { var scheduler = _serviceProvider.GetService(typeof(IScheduler)) as IScheduler; scheduler.Start(); return true; } public bool Stop(HostControl hostControl) { var scheduler = _serviceProvider.GetService(typeof(IScheduler)) as IScheduler; scheduler.Shutdown(true); return true; } }
Test2Job.cs
public class Test2Job : IJob { private ITest _testService; private AppSetting _appsetting; private IConfiguration _configuration; public Test2Job(ITest testService, IOptionsMonitor<AppSetting> appSettingAccessor, IConfiguration configuration) { _testService = testService; _appsetting = appSettingAccessor.CurrentValue; _configuration = configuration; } public Task Execute(IJobExecutionContext context) { Console.WriteLine($"job2222222222222 started:{_appsetting.TestCN}"); var t = _testService.Dowork(1); t.Wait(); Console.WriteLine($"job2222222222222 ended:{_configuration["TestCN"]}"); return Task.CompletedTask; } }
项目结构截图