我有一系列与Quartz + TopShelf实现中的作业相关的触发器。该作业需要长时间运行,某些触发器需要运行约2个小时。
作业的热点是使用Excel Automation处理.xls文件的作业。这是有时由于各种错误(例如此The remote procedure call failed. (Exception from HRESULT: 0x800706BE)
)服务挂起的地方。
我的问题是:
当遇到与Excel Automation相关的任何错误(请参阅“失败部件代码”)时,如何指示该服务执行受到威胁,我需要它重新启动自身?
是否可以确保首先由GC正确收集了所有Excel对象?
如果我设法重新启动该服务,如何通知Quartz正在运行的作业已停止并且需要恢复/重新执行其步骤?
服务部分:
using System;
internal static partial class Main
{
public static void Main()
{
var rc = HostFactory.Run(x =>
{
x.Service<ScheduleService>(ServiceConfiguratorCallback);
x.RunAsLocalSystem();
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console().WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}logs\\log.txt", rollingInterval: RollingInterval.Day).CreateLogger();
x.UseSerilog(Log.Logger);
x.SetDescription("Test service");
x.SetDisplayName("Test service");
x.SetServiceName("Test service");
});
var exitCode = Int(Convert.ChangeType(rc, rc.GetTypeCode()));
Environment.ExitCode = exitCode;
}
private static void ServiceConfiguratorCallback(ServiceConfigurator s)
{
s.ConstructUsing(name => new ScheduleService());
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
}
}
执行部分:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public partial class SendEmailQlik : IJob
{
public Task Execute(IJobExecutionContext context)
{
try
{
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
string appPath = AppDomain.CurrentDomain.BaseDirectory;
string configpath = appPath + @"\Config.ini";
IniData configdata = excelDownload.ReadIniFile(configpath);
var mod_rulare = configdata["GENERAL"]("MODE");
string numejob = context.JobDetail.Key.Name;
string lastRun = context.PreviousFireTimeUtc?.DateTime.ToString() ?? string.Empty;
Log.Information("Psst! Trigger-ul {triggername} s-a activat! Ultima oara am rulat la: {lastRun}", context.Trigger.Key.Name, lastRun);
Log.Information("Se executa job-urile asociate trigger-ului {0}!", context.Trigger.Key.Name);
if (mod_rulare.ToLower == "all")
{
Log.Information("Processing image files...");
List<string> images = Utils.QlikExport(context);
Log.Information("Processing xls files...");
var r = Utils.GetContextXlsv2(context);
r.Wait();
List<string> xls = r.Result;
Utils.SendEmails(images, xls, context);
}
else if (mod_rulare.ToLower == "rpt")
{
Log.Warning("Mod rulare is {0}", mod_rulare);
Log.Information("Processing xls files...");
var r = Utils.GetContextXlsv2(context);
r.Wait();
List<string> xls = r.Result;
}
else if (mod_rulare.ToLower == "img")
{
Log.Warning("Mod rulare is {0}", mod_rulare);
Log.Information("Processing image files...");
List<string> images = Utils.QlikExport(context);
}
else
{
Log.Error("Mode {0} is not recognized. Please set a valid run mode...", mod_rulare);
}
Log.Information("Waiting next schedule...");
return Task.CompletedTask;
}
catch (Exception ex)
{
var jee = new JobExecutionException(ex);
Log.Error("Error raised is: {0}", ex.Message);
jee.RefireImmediately = true;
}
return default;
}
}
失败的部分:
try
{
Log.Information("Instantiating xlApp for {agent}...", agent);
object oMissing = Missing.Value;
var exWbk = exApp.Workbooks.Open(filepath, oMissing, false, oMissing, oMissing, oMissing, true, oMissing, oMissing, true, oMissing, oMissing, oMissing, oMissing, oMissing);
try
{
System.Threading.Thread.Sleep(200);
Worksheet summary_wksheet = exWbk.Sheets("Summary");
summary_wksheet.Activate();
summary_wksheet.Move(Before: exWbk.Worksheets("Sheet1"));
Log.Information("Refreshing data...");
exWbk.Sheets("Summary").PivotTables("PivotTable1").PivotCache.Refresh();
exWbk.RefreshAll();
exApp.Calculate();
exWbk.Save();
exWbk.Close(0);
exApp.Quit();
}
catch (COMException e)
{
if ((e.ErrorCode & 0xFFFF) == 0x10A)
{
System.Threading.Thread.Sleep(10000);
exWbk.Close(0);
exApp.Quit();
}
else
{
System.Threading.Thread.Sleep(10000);
exWbk.Close(0);
exApp.Quit();
throw e;
}
}
}
catch (Exception e)
{
Log.Error("Error while opening Excel app for {0}: {1}", numeraport, e.Message);
Log.Error("{0}|{1}|{2}|{4}|{3}", "EROARE", agent, "", e.Message, numeraport);
System.Threading.Thread.Sleep(5000);
Log.Error("Killing faulty Excel app process...");
KillXLS();
continue;
}