forked from ddrilling/AsbCloudServer
WorkBase добавлено больше диагностической информации.
This commit is contained in:
parent
01f04c7ea5
commit
673cb8960c
@ -14,10 +14,10 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
{
|
{
|
||||||
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
private static readonly TimeSpan executePeriod = TimeSpan.FromSeconds(10);
|
||||||
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2);
|
private static readonly TimeSpan minDelay = TimeSpan.FromSeconds(2);
|
||||||
private static readonly TimeSpan exceptionHandleTimeout = TimeSpan.FromSeconds(2);
|
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly WorkQueue workQueue = new WorkQueue();
|
private readonly WorkQueue workQueue = new WorkQueue();
|
||||||
public string? CurrentWorkId;
|
public string? CurrentWorkId;
|
||||||
|
|
||||||
public BackgroundWorker(IServiceProvider serviceProvider)
|
public BackgroundWorker(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
@ -58,36 +58,18 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
{
|
{
|
||||||
while (!token.IsCancellationRequested)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
var dateStart = DateTime.Now;
|
|
||||||
var work = workQueue.Pop();
|
var work = workQueue.Pop();
|
||||||
if (work is null)
|
if (work is null)
|
||||||
{
|
{
|
||||||
await Task.Delay(executePeriod, token);
|
await Task.Delay(executePeriod, token);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentWorkId = work.Id;
|
CurrentWorkId = work.Id;
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
|
|
||||||
try
|
await work.Start(scope.ServiceProvider, token);
|
||||||
{
|
|
||||||
Trace.TraceInformation($"Backgroud work:\"{work.Id}\" start.");
|
|
||||||
var task = work.ActionAsync(work.Id, scope.ServiceProvider, token);
|
|
||||||
await task.WaitAsync(work.Timeout, token);
|
|
||||||
|
|
||||||
work.ExecutionTime = DateTime.Now - dateStart;
|
|
||||||
Trace.TraceInformation($"Backgroud work:\"{work.Id}\" done. ExecutionTime: {work.ExecutionTime:hh\\:mm\\:ss\\.fff}");
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Trace.TraceError($"Backgroud work:\"{work.Id}\" throw exception: {exception.Message}");
|
|
||||||
if (work.OnErrorAsync is not null)
|
|
||||||
{
|
|
||||||
using var task = Task.Run(
|
|
||||||
async () => await work.OnErrorAsync(work.Id, exception, token),
|
|
||||||
token);
|
|
||||||
await task.WaitAsync(exceptionHandleTimeout, token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CurrentWorkId = null;
|
CurrentWorkId = null;
|
||||||
await Task.Delay(minDelay, token);
|
await Task.Delay(minDelay, token);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudInfrastructure.Background
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс разовой работы.
|
/// Класс разовой работы.
|
||||||
/// Разовая работа приоритетнее периодической.
|
/// Разовая работа приоритетнее периодической.
|
||||||
@ -14,7 +15,7 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки.
|
/// Идентификатор работы. Должен быть уникальным. Используется в логах и передается в колбэки.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; private set; }
|
public string Id { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Делегат работы.
|
/// Делегат работы.
|
||||||
@ -36,7 +37,7 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// </list>
|
/// </list>
|
||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal Func<string, IServiceProvider, CancellationToken, Task> ActionAsync { get; set; }
|
internal Func<string, IServiceProvider, CancellationToken, Task> ActionAsync { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Делегат обработки ошибки.
|
/// Делегат обработки ошибки.
|
||||||
@ -50,20 +51,118 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
|
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Фактическое время успешного выполнения работы
|
/// Продолжительность последнего выполнения
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan? ExecutionTime { get; internal set; }
|
public TimeSpan? LastExecutionTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Текущее время выполнения
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? CurrentExecutionTime => CurrentStart.HasValue
|
||||||
|
? DateTime.Now - CurrentStart.Value
|
||||||
|
: null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Время последнего запуска
|
/// Время последнего запуска
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime LastStart { get; set; }
|
public DateTime? CurrentStart { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Текстовое описание того, что происходит в задаче.
|
||||||
|
/// </summary>
|
||||||
|
public string? CurrentStatus { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Время последнего запуска
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? CurrentStatusUpdate { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Последняя ошибка
|
||||||
|
/// </summary>
|
||||||
|
public string? LastErrorMessage { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата последнего запуска
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastStart { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата последней ошибки
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastError { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Дата завершения последнего выполнения
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? LastComplete { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Кол-во завершений
|
||||||
|
/// </summary>
|
||||||
|
public int CountComplete { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Кол-во ошибок
|
||||||
|
/// </summary>
|
||||||
|
public int CountErrors { get; private set; }
|
||||||
|
|
||||||
|
private string WorkNameForTrace => $"Backgroud work:\"{Id}\"";
|
||||||
|
|
||||||
public WorkBase(string id, Func<string, IServiceProvider, CancellationToken, Task> actionAsync)
|
public WorkBase(string id, Func<string, IServiceProvider, CancellationToken, Task> actionAsync)
|
||||||
{
|
{
|
||||||
Id = id;
|
Id = id;
|
||||||
ActionAsync = actionAsync;
|
ActionAsync = actionAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task Start(IServiceProvider services, CancellationToken token)
|
||||||
|
{
|
||||||
|
CurrentStart = DateTime.Now;
|
||||||
|
LastStart = DateTime.Now;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SetStatus(" start");
|
||||||
|
var task = ActionAsync(Id, services, token);
|
||||||
|
await task.WaitAsync(Timeout, token);
|
||||||
|
LastComplete = DateTime.Now;
|
||||||
|
CountComplete++;
|
||||||
|
SetStatus($" {task.Status}. ExecutionTime: {CurrentExecutionTime:hh\\:mm\\:ss\\.fff}");
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
SetError(exception.Message);
|
||||||
|
if (OnErrorAsync is not null)
|
||||||
|
{
|
||||||
|
var task = Task.Run(
|
||||||
|
async () => await OnErrorAsync(Id, exception, token),
|
||||||
|
token);
|
||||||
|
await task.WaitAsync(Timeout, token);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastExecutionTime = CurrentExecutionTime;
|
||||||
|
CurrentStart = null;
|
||||||
|
SetStatus(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetStatus(string? newStatus)
|
||||||
|
{
|
||||||
|
CurrentStatus = newStatus;
|
||||||
|
if (newStatus is not null)
|
||||||
|
{
|
||||||
|
CurrentStatusUpdate = DateTime.Now;
|
||||||
|
Trace.TraceInformation($"{WorkNameForTrace} state: {newStatus}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
CurrentStatusUpdate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetError(string? errorMessage)
|
||||||
|
{
|
||||||
|
CountErrors++;
|
||||||
|
LastErrorMessage = errorMessage;
|
||||||
|
LastError = DateTime.Now;
|
||||||
|
Trace.TraceError($"{WorkNameForTrace} throw exception[{CountErrors}]: {errorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Время следующего запуска
|
/// Время следующего запуска
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime NextStart => LastStart + Period;
|
public DateTime NextStart => LastStart??DateTime.MinValue + Period;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Класс периодической работы
|
/// Класс периодической работы
|
||||||
|
@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace AsbCloudInfrastructure.Background
|
namespace AsbCloudInfrastructure.Background
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>
|
/// <para>
|
||||||
/// Очередь работ
|
/// Очередь работ
|
||||||
@ -90,7 +89,6 @@ namespace AsbCloudInfrastructure.Background
|
|||||||
if (work is null || work.NextStart > DateTime.Now)
|
if (work is null || work.NextStart > DateTime.Now)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
work.LastStart = DateTime.Now;
|
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
# Проблемы фонового сервиса
|
# Проблемы фонового сервиса
|
||||||
- нет понимания по загрузке очереди работ
|
- Нужно состояние по загрузки сервиса и очереди работ.
|
||||||
|
- Все ли задачи укладываются в таймаут,
|
||||||
|
- Сколько свободного времени остается,
|
||||||
|
- Что делает текущая задача,
|
||||||
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
|
- нет управления сервисом. Для исключения его влияния на другие процессы сервера.
|
||||||
- отключать/включать целиком
|
- отключать/включать целиком
|
||||||
- отключать/включать отдельную периодическую задачу
|
- отключать/включать отдельную периодическую задачу
|
||||||
|
|
||||||
|
# Сделать
|
||||||
|
- Разработать dto статуса задачи
|
||||||
|
- Отказаться от периодической задачи, при добавлении в хранилище задач период будет параметром метода добавления.
|
||||||
|
@ -541,7 +541,6 @@ namespace AsbCloudInfrastructure.Services.DrillingProgram
|
|||||||
|
|
||||||
var work = new WorkBase(workId, workAction)
|
var work = new WorkBase(workId, workAction)
|
||||||
{
|
{
|
||||||
ExecutionTime = TimeSpan.FromMinutes(1),
|
|
||||||
OnErrorAsync = onErrorAction
|
OnErrorAsync = onErrorAction
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests
|
|||||||
await BackgroundWorker.StartAsync(CancellationToken.None);
|
await BackgroundWorker.StartAsync(CancellationToken.None);
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
|
|
||||||
Assert.True(work.ExecutionTime > TimeSpan.Zero);
|
Assert.True(work.LastExecutionTime > TimeSpan.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
Loading…
Reference in New Issue
Block a user