Я экспериментирую с IHostedService
в dotnet core 2.2
. Моя задача - создать 2 фоновые длительные задачи.
- Первый из них - управлять
Selenium
сеансом браузера (открывать / закрывать вкладки, анализировать DOM) и помещать сообщения электронной почты в очередь внутри ConcurrentBag
.
- Второй фоновый работник отправляет уведомления по электронной почте один раз в 10 минут для сообщений, которые существуют в
ConcurrentBag
(первая добавленная задача). Он также группирует их вместе, так что отправляется только 1 сообщение.
Однако у меня проблемы с запуском 2-х размещенных процессов одновременно. Кажется, что только первый размещенный процесс выполняется, тогда как второй процесс ожидает полного выполнения первого. Но так как я никогда не ожидаю, что это закончится - второй процесс никогда не начнется ...
Я неправильно использую IHostedService
? Если да, то каков наилучший архитектурный подход к решению моей задачи?
Вот код, который я сейчас использую (пытаюсь завершить):
using System;
// ..
namespace WebPageMonitor
{
class Program
{
public static ConcurrentBag<string> Messages = new ConcurrentBag<string>();
static void Main(string[] args)
{
BuildWebHost(args)
.Run();
Console.ReadKey();
}
private static IHost BuildWebHost(string[] args)
{
var hostBuilder = new HostBuilder()
.ConfigureHostConfiguration(config =>
{
config.AddJsonFile("emailSettings.json", optional: true);
config.AddEnvironmentVariables();
})
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
var bindConfig = new EmailSettings();
hostContext.Configuration.GetSection("EmailSettings").Bind(bindConfig);
services.AddSingleton<EmailSettings>(bindConfig);
services.AddTransient<EmailSender>();
services.AddHostedService<BrowserWorkerHostedService>();
services.AddHostedService<EmailWorkerHostedService>();
});
return hostBuilder.Build();
}
}
}
BrowserWorkerHostedService
public class BrowserWorkerHostedService : BackgroundService
{
private static IWebDriver _driver;
public BrowserWorkerHostedService()
{
InitializeDriver();
}
private void InitializeDriver()
{
try
{
ChromeOptions options = new ChromeOptions();
options.AddArgument("start-maximized");
options.AddArgument("--disable-infobars");
options.AddArgument("no-sandbox");
_driver = new ChromeDriver(options);
}
catch (Exception ex)
{
Program.Messages.Add("Exception: " + ex.ToString());
Console.WriteLine($" Exception:{ex.ToString()}");
throw ex;
}
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
try
{
_driver.Navigate().GoToUrl("https://www.google.com");
Program.Messages.Add("Successfully opened a website!");
// rest of the processing here
Thread.Sleep(60_000);
}
catch (Exception ex)
{
Program.Messages.Add("Exception: " + ex.ToString());
Console.WriteLine(ex.ToString());
Thread.Sleep(120_000);
}
}
_driver?.Quit();
_driver?.Dispose();
}
}
EmailWorkerHostedService
public class EmailWorkerHostedService : BackgroundService
{
private readonly EmailSender _emailSender;
private readonly IHostingEnvironment _env;
public EmailWorkerHostedService(
EmailSender emailSender,
IHostingEnvironment env)
{
_emailSender = emailSender;
_env = env;
}
protected override async Task ExecuteAsync(CancellationToken stopToken)
{
while (!stopToken.IsCancellationRequested)
{
var builder = new StringBuilder();
List<string> exceptionMessages = new List<string>();
string exceptionMessage;
while (Program.Messages.TryTake(out exceptionMessage))
exceptionMessages.Add(exceptionMessage);
if (exceptionMessages.Any())
{
foreach (var message in exceptionMessages)
{
builder.AppendLine(new string(message.Take(200).ToArray()));
builder.AppendLine();
}
string messageToSend = builder.ToString();
await _emailSender.SendEmailAsync(messageToSend);
}
Thread.Sleep(10000);
}
}
}
РЕДАКТИРОВАТЬ: После применения изменений, предложенных в ответе, вот текущая версия кода, который работает. Добавление await
помогло.