Commit eb3dab48 authored by Michael Herndon's avatar Michael Herndon

WIP: Jobs

prototype out jobs service
parent 40c7f00c
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NerdyMishka.Extensions.Logging;
using Quartz;
namespace NerdyMishka.Jobs
{
public abstract class Job : Quartz.IJob
{
protected Stopwatch StopWatch { get; set; }
protected ITelemetryClient TelemetryClient { get; set; }
protected bool Refire { get; set; } = false;
protected void TrackStart(
IJobExecutionContext context,
Dictionary<string, string> properties = null,
Dictionary<string, double> metrics = null)
{
properties = properties ?? new Dictionary<string, string>();
properties.Add("firedAt", context.FireTimeUtc.ToString("o"));
properties.Add("refireCount", context.RefireCount.ToString());
properties.Add("jobName", context.JobDetail.JobType.Name);
properties.Add("jobType", context.JobDetail.JobType.FullName);
this.TelemetryClient?.TrackEvent("Jobs/JobStarted", properties, metrics);
}
protected void TrackException(Exception exception)
{
this.TelemetryClient?.TrackException(exception);
this.TelemetryClient?.Flush();
}
protected void TrackEnd(
IJobExecutionContext context,
Dictionary<string, string> properties = null,
Dictionary<string, double> metrics = null
)
{
properties = properties ?? new Dictionary<string, string>();
properties.Add("firedAt", context.FireTimeUtc.ToString("o"));
properties.Add("refireCount", context.RefireCount.ToString());
properties.Add("jobName", context.JobDetail.JobType.Name);
properties.Add("jobType", context.JobDetail.JobType.FullName);
this.TelemetryClient?.TrackEvent($"Jobs/JobFinished", properties, metrics);
}
public virtual async Task Execute(IJobExecutionContext context)
{
this.StopWatch = new Stopwatch();
var stopwatch = new Stopwatch();
try {
stopwatch.Start();
await this.PerformJobAsync(context);
} catch(JobExecutionException ex) {
throw ex;
} catch(Exception ex) {
this.TrackException(ex);
throw new JobExecutionException(ex, this.Refire);
} finally {
stopwatch.Stop();
this.TelemetryClient?
.GetMetric("Jobs", context.JobDetail.JobType.Name, "ElaspedMilliseconds")?
.TrackValue(stopwatch.ElapsedMilliseconds);
}
}
public abstract Task PerformJobAsync(IJobExecutionContext context);
}
}
\ No newline at end of file
using Quartz;
using Quartz.Spi;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace NerdyMishka.Jobs
{
public class JobFactory : IJobFactory
{
private IServiceProvider serviceProvider;
public JobFactory(IServiceProvider provider)
{
this.serviceProvider = provider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var type = bundle.JobDetail.JobType;
return (IJob)this.serviceProvider.GetRequiredService(type);
}
public void ReturnJob(IJob job)
{
if(job is IDisposable)
((IDisposable)job).Dispose();
}
}
}
\ No newline at end of file
using System;
namespace NerdyMishka.Jobs
{
public class JobRegistration
{
public Type JobType { get; set; }
public Quartz.ITrigger Trigger { get; set; }
public string Json { get; set; }
}
}
\ No newline at end of file
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Quartz;
using Quartz.Spi;
namespace NerdyMishka.Jobs
{
public class JobsHostedService : IHostedService
{
private ISchedulerFactory factory;
public IList<JobRegistration> JobRegistry { get; private set; } = new List<JobRegistration>();
public IScheduler JobScheduler { get; private set; }
public IJobFactory JobFactory { get; private set; }
public JobsHostedService(ISchedulerFactory schedulerFactory, IJobFactory jobFactory, IEnumerable<JobRegistration> jobs)
{
this.factory = schedulerFactory;
this.JobFactory = jobFactory;
this.JobRegistry = new List<JobRegistration>(jobs);
}
public async Task StartAsync(CancellationToken cancellationToken)
{
this.JobScheduler = await this.factory.GetScheduler(cancellationToken);
this.JobScheduler.JobFactory = this.JobFactory;
foreach(var registration in this.JobRegistry)
{
var job = CreateJob(registration);
await this.JobScheduler.ScheduleJob(job, registration.Trigger, cancellationToken);
}
await this.JobScheduler.Start(cancellationToken);
}
public async Task StopAsync(CancellationToken cancellationToken)
{
await this.JobScheduler?.Shutdown(cancellationToken);
}
private static IJobDetail CreateJob(JobRegistration registration)
{
var jobType = registration.JobType;
return JobBuilder
.Create(jobType)
.WithIdentity(jobType.FullName)
.WithDescription(jobType.FullName)
.UsingJobData("configuration", registration.Json)
.Build();
}
}
}
\ No newline at end of file
......@@ -22,7 +22,9 @@
<PackageReference Include="ConsoleTables" Version="2.4.0" />
<PackageReference Include="Quartz" Version="3.0.7" />
<PackageReference Include="System.ComponentModel.Composition" Version="4.7.0" />
......@@ -32,6 +34,8 @@
<ItemGroup>
<ProjectReference Include="..\..\Bcl\Proto\NerdyMishka.Proto.csproj" />
<ProjectReference Include="..\..\Extensions\Serilog\*.csproj" />
</ItemGroup>
</Project>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment