![](/img/trans.png)
[英]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.