[英]use hangfire as windows service by topshelf (.net core 2.2)
[英]Getting Hangfire to work in a windows service using .NET 4.6.1
先生们,女士们,
对于一个项目,我正在将现有控制台转换为 Windows 服务。 当前控制台应用程序连续运行一系列作业。
该服务的想法是按计划运行这些作业中的每一个。 为此,我们创建了一个 windows 服务,该服务使用 Hangfire 执行此操作。
构造器
public BatchService(ILogger<BatchService> logger)
{
InitializeComponent();
GlobalConfiguration.Configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170)
.UseColouredConsoleLogProvider()
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(Constants.ConnectionString.Peach,
new SqlServerStorageOptions
{
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
QueuePollInterval = TimeSpan.Zero,
UseRecommendedIsolationLevel = true
})
.UseNLogLogProvider();
_logger = logger;
}
用于将所有内容加载到Hangfire的方法:
private void LoadConfigurationIntoHangfire()
{
// Loading the job configurationNo
var jobListing = Configuration.GetJobConfiguration().Where(job => job.JobType.Equals("CRON")).ToList();
// Debug
_logger.LogDebug($"LoadConfigurationIntoHangfire() - jobListing = {JsonConvert.SerializeObject(jobListing)}");
foreach (var job in jobListing)
{
// Information
_logger.LogInformation($"Added/updated job '{job.JobName}' with cron expression '{job.CronExpression}'.");
RecurringJob.AddOrUpdate<IDataProtectionJob>(x => x.ExecuteJob(job), job.CronExpression);
}
}
private void StopHangfire()
{
// Information
_logger.LogInformation($"Stopping Hangfire.");
_hangfireServer.SendStop();
_hangfireServer.WaitForShutdown(new TimeSpan(0, 0, 5));
_hangfireServer.Dispose();
// Information
_logger.LogInformation($"Hangfire stopped.");
}
private void StartHangfire()
{
// Information
_logger.LogInformation($"Starting Hangfire.");
_hangfireServer = new BackgroundJobServer();
LoadConfigurationIntoHangfire();
// Information
_logger.LogInformation($"Hangfire started.");
}
服务的OnStart事件:
protected override void OnStart(string[] args)
{
try
{
// Update the service state to Start Pending.
ServiceStatus serviceStatus = new ServiceStatus
{
dwCurrentState = ServiceState.SERVICE_START_PENDING,
dwWaitHint = 100000
};
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
// Information
_logger.LogInformation("Starting Windows Service 'BatchService'");
SetPolling();
StartHangfire();
_logger.LogInformation("Windows Service 'BatchService' started");
// Update the service state to Running.
serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
SetServiceStatus(this.ServiceHandle, ref serviceStatus);
}
catch (Exception e)
{
_logger.LogError($"Windows Service 'BatchService' failed to start ({e})");
throw;
}
}
这些作业基于一个接口 IDataProtectionJob,它有一个实现,实现本身超出了 scope(我认为)。
现在,实现构建和启动没有问题,但是......没有执行任何任务。 在日志文件中,我发现了以下错误消息:
2022-11-23 19:05:53.0881|DEBUG|Hangfire.Server.DelayedJobScheduler|2 个由调度程序处理的预定作业。 2022-11-23 19:05:53.1353|WARN|Hangfire.AutomaticRetryAttribute|无法处理作业“17”:发生异常。 将在 00:05:36 中执行 10 次重试中的 5 次。|System.MissingMethodException:没有为此 object 定义无参数构造函数。在 System.RuntimeTypeHandle.CreateInstance(RuntimeType 类型,Boolean publicOnly,Boolean noCheck,Boolean&canBethMethodHand,Runtime Boolean& bNeedSecurityCheck) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.Activator.CreateInstance(类型类型,Boolean 非公共) 在 System.Activator.CreateInstance(类型类型) 在 Hangfire.JobActivator.ActivateJob(类型 jobType) 在 Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(类型类型) 在 Hangfire.Server。 CoreBackgroundJobPerformer.Perform(PerformContext 上下文)在 Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_0.b__0() 在 Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter 过滤器,PerformingContext preContext,Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_1.<PerformJobWithFilters>b__2() at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable
1 个过滤器)字符串 jobId) 2022-11-23 19:05:53.1383|WARN|Hangfire.AutomaticRetryAttribute|无法处理 j ob '20': 发生异常。 将在 00:01:04 执行 10 次重试中的第 2 次尝试。|System.MissingMethodException:无法创建接口的实例。 at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at System.RuntimeType.CreateInstanceDefaultCtor (Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
在 System.Activator.CreateInstance(类型类型,Boolean 非公共) 在 System.Activator.CreateInstance(类型类型) 在 Hangfire.JobActivator.ActivateJob(类型 jobType) 在 Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(类型类型) 在 Hangfire.Server。 CoreBackgroundJobPerformer.Perform(PerformContext 上下文)在 Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_0.b__0() 在 Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter 过滤器,PerformingContext preContext,Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass9_1.<PerformJobWithFilters>b__2() at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable
1 个过滤器)字符串 jobId)
我搜索了错误消息,找到了一些基于 Hangfire 解析您分配给它执行的方法的依赖关系的方式的解释。
我玩了一下,但无济于事。
这里有人有想法吗?
操作系统:Windows 服务器 2016 .NET 版本:4.6.1
默认情况下,Hangfire 不知道在使用接口排队时使用什么类型来执行作业。 有一个名为JobActivator的特定组件,它仅使用Activator.CreateInstance创建类型以执行后台作业的方法。 而且您不能只使用 Activator.CreateInstance 来创建类型的实例。 你会得到一个错误:
'无法动态创建类型 '' 的实例。 原因:无法创建接口实例。
它也不能创建没有无参数构造函数的类型的实例。 考虑这段代码:
var result = Activator.CreateInstance(typeof(TestType));
public class TestType
{
public TestType(string someParameter)
{
}
}
你会得到这个错误:
System.MissingMethodException:“无法动态创建类型”的实例。 原因:没有定义无参数构造函数。
这恰好是你得到的同样的错误。
然而,仅仅因为默认的作业激活器不知道如何处理接口或无参数类型并不意味着你被卡住了。 Hangfire 的创建者提供了交换不同工作激活器的灵活性, 如他们的文档中所述。
在.NET中,我们经常使用IoC容器来解决依赖。 这正是 Hangfire 所需要的。 为了连接它,您选择一个 IoC 容器(如果您还没有的话),然后找到一个合适的库,该库提供一个作业激活器作为您的 IoC 容器和 Hangfire 之间的粘合剂。 如果不存在,您可以自己创建一个,因为合同并不那么复杂。 您只需继承 JobActivator 并覆盖其方法。
在我的公司,我们有一个使用Autofac作为 IoC 容器的应用程序。 所以我们安装了Hangfire.Autofac库,它为我们提供了 Hangfire 的 Autofac 作业激活器。
然后我们告诉 Hangfire 在启动时使用它:
IContainer container = GetContainer(); //implement this however you like
// Tell Hangfire to use our container
GlobalConfiguration.Configuration.UseAutofacActivator(container);
// Now later on, when we have jobs to enqueue, we can do so via an interface.
RecurringJob.AddOrUpdate<IDataProtectionJob>(x => x.ExecuteJob(job), job.CronExpression);
在 Hangfire 的作业存储中,它会将表达式与接口名称序列化。 因此,任何后台作业服务器(可能与安排作业的应用程序相同或不同)都可以使用其 IoC 容器来解析实现您的接口的具体类型(在本例中为 IDataProjectionJob),然后执行后台作业方法(在这种情况下为 ExecuteJob)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.