6.3.2 Создание класса фоновой задачи, наследника BaseEventDispatcher / BaseDispatcher / BaseDaemon
При разработке фоновой задачи необходимо создать базовый класс ФЗ по определённому шаблону.
Класс ФЗ наследует один из классов BaseEventDispatcher / BaseDispatcher или BaseDaemon при невозможности реализации через базовые диспетчеры. Для реализации фоновых задач на платформе в большинстве случаев подойдет BaseDispatcher, это стандартизированный шаблон диспетчера, реализующий основной функционал для работы ФЗ.
Внимание!
Рекомендована реализация ФЗ на BaseEventDispatcher / BaseDispatcher. Реализация на BaseDaemon осуществляется в крайнем случае, если невозможно написание на одном из диспетчеров.
Класс ФЗ имеет атрибут описания фоновой задачи [Daemon(…)]. Атрибут содержит название группы ФЗ, название ФЗ, описание ФЗ, номер версии, вид ФЗ и тип конфигурирования.
Описание элементов атрибута Daemon:
Атрибут [Daemon(…)] – описание фоновой задачи
public DaemonAttribute(string group, string name, string description, double version, DaemonKind kind, DaemonTypeConfig typeConfig = DaemonTypeConfig.USER_CONFIG, Type defaultConfigurationType = null, Type jobConfigurationType = null);
Для атрибута [Daemon(…)] необходимо указать:
group – группа, к которой принадлежит фоновая задача;
name – название ФЗ;
description – краткое описание ФЗ;
version – версия ФЗ;
kind – вид ФЗ, объект типа DaemonKind;
typeConfig – способ конфигурирования ФЗ, объект типа DaemonTypeConfig;
defaultConfigurationType – тип класса конфигурации общих настроек;
jobConfigurationType – тип класса конфигруации экземплярных настроек.
public enum DaemonKind { JOB, MANUAL, SYSTEM }
Перечисление видов фоновых задач:
JOB – автоматическая. Данный тип ФЗ выполняется с заданной периодичностью.
MANUAL – ручная. Выполнение такого вида ФЗ производится только после нажатия на кнопку «выполнить сейчас» в управлении фоновых задач (в ДЕЛО-WEB). После окончания выполнения, статус задачи переводится в «завершена». Для повторного выполнения ФЗ необходимо снова нажать на кнопку выполнения.
SYSTEM – системная.
public enum DaemonTypeConfig { USER_CONFIG, AUTO_CONFIG }
Перечисление способов конфигурирования фоновой задачи:
USER_CONFIG - пользовательская конфигурация. Конфигурация задается с помощью файла Setup/*.json и настройки конфигурации в управлении фоновыми задачами. Имя файла, вместо * должно соответствовать имени класса фоновой задачи, для которого указывается аттрибут Daemon.
AUTO_CONFIG – автоматическая конфигурация.
Дальнейшая реализация класса зависит от наследуемого класса.
Реализация на базовых диспетчерах:
1) BaseDispatcher - общий диспетчер для фоновых задач, который реализует в себе стандартный набор функций для работы с ватермаркой и параметрами. Для работы с ним необходимо его наследовать и реализовать методы FillParams и WorkProcess.
2) В метод FillParams передается экземпляр конфигурации и логика его заполнения:
3) Params = GetDefaultAppSettingsParams<TestDispatcherCfg>(); для общих настроек конифгурации и JobParams = GetJobAppSettingsParams<TestDiapatcherJobCfg>(); для экземплярных.
Метод WorkProcess реализует необходимую обработку. Если необходимо соединение с базой данных, выполняемые конструкции пишутся в InContext(() => {//action});
BaseEventDispatcher - обертка над BaseDispatcher, которая стандартизирует работу ФЗ, работающих на событиях.
Для работы с этим диспетчером необходимо наследовать класс BaseEventDispatcher и реализовать методы Agregate, EventProcess и FillParams.
Метод Agregate отвечает за отбор событий из списка. Метод получает n-кол-во событий, из которых необходимо отобрать нужные (агрегирование по флагам) и вернуть их списком.
Метод EventProcess описывает основную логику работы с событием. В нем загружается все необходимое, производится валидация и дальнейшая обработка события производится в обрабатывающем хелпере.
У обоих диспетчеров так же есть следующие методы и поля, которые можно переопределить:
- Init - метод отвечает за инициализацию фоновой задачи. По умолчанию в нем установлен интервал запуска итерации ФЗ в 30 секунд.
- Finish - метод отвечает за окончание итерации работы фоновой задачи. По умолчанию в нем вызываются методы CalculateNextTime и WaitingProcess.
- InContext - метод создает контекст соединения с БД, внутри которого выполняется передаваемый action. (Работа в контексте соединения с БД. Переопределяется в случае, если необходимо использовать DAPI с открытием фасада.)
- SetWatermarkId - метод позволяет установить ключ ватермарки. Не рекомендуется менять.
- CS_WATERMARK_ID - поле содержит VALUE_ID ватермарки.
- LAUNCHMESSAGE - поле содержит сообщение при запуске ФЗ (по умолчанию “Запуск ФЗ”).
- FINISHMESSAGE - поле содержит сообщение при окончании работы ФЗ (по умолчанию “Ожидание следующего запуска”).
- Database - поле содержит Экземпляр соединения с БД (EF).
BaseEventDispatcher так же имеет следующие свойства и методы:
SIZE - свойство отвечает за размер пейджинга при загрузке событий (максимальное количество загружаемых событий за раз).
UseDefaultSatistic - свойство отвечает за использование статистики по умолчанию (по умолчанию включено).
GetNextEvents - метод отвечает за загрузку событий.
SaveStats - метод сохранения статистики по событию.
Диспетчер BaseDispatcher имеет метод WorkProcess, который необходимо реализовать в случае, если вы используете его, а не BaseEventDispatcher.
Для использования BaseDispatcher в своем проекте необходимо загрузить NuGet пакет Eos.Delo.Daemons.Dispatchers.
Для ДЕЛО API 2009 при наследовании BaseEventDispatcher / BaseDispatcher необходимо переопределить метод InContext.
Пример переопределения InContext для ДЕЛО API 2009:
|
protected override void InContext(Action action)
{
try
{
Logger.LogInformation("Начало InContext");
if (WorkContext.Current.Head != null)
WorkContext.DropCurrent();
Facade.Open(String.Empty);
Facade.SetUserIsn(DaemonsDomain.Instance.LoadDomainConfiguration().UserID);
action();
}
catch (Exception ex)
{
MarkInfoError(ex);
Logger.LogError(ex.ToString());
}
finally
{
WorkContext.DropCurrent();
Logger.LogInformation("Конец InContext");
}
}
|
Пример реализации ФЗ на таком диспетчере можно найти в дистрибутиве системы в архиве «APISamples.zip» (проект Eos.Delo.Daemons.TestDaemons)
Также примеры ФЗ, реализованные на диспетчерах, расположены в дистрибутиве системы в архиве «APISamples.zip» (проект Eos.Delo.Samples.Daemons).
Пример реализации ФЗ на BaseDispatcher:
|
// Реализация фоновой задачи на BaseDispatcher[Daemon("Примеры ФЗ", "Тестовая отправка Email", "Тестовая отправка Email", 1.0, DaemonKind.JOB, DaemonTypeConfig.USER_CONFIG, typeof(MailSenderCfg))]
public class MailSenderDispatcher : BaseDispatcher
{
MailSenderCfg _params;
protected override void FillParams()
{
_params = GetDefaultAppSettingsParams<MailSenderCfg>();
}
protected override void WorkProcess()
{
var sender = new MailSender(_params);
var err = sender.Send($"Тестовое тело письма\nIs Linux = {System.OperatingSystem.IsLinux()}");
if (!String.IsNullOrEmpty(err))
throw new Exception(err);
}
}
|
Пример реализации ФЗ на BaseEventDispatcher:
|
// Реализация фоновой задачи на BaseEventDispatcher[Daemon("Примеры ФЗ", "Добавление организации-адресата", "Добавление организации-адресата при взведенном флаге 'ADD_ORG_ADDRESSEE' (доп. реквизит) ...", 1.0, DaemonKind.JOB, DaemonTypeConfig.USER_CONFIG, typeof(CommonAddresseeDirectionCfg), typeof(JobAddresseeDirectionCfg))]
public class AddresseeDirectionDispatcher : BaseEventDispatcher
{
private JobAddresseeDirectionCfg jobParams;
private CommonAddresseeDirectionCfg commonParams;
protected override IEnumerable<IEvntFeed> Agregate(IEnumerable<IEvntFeed> events)
{
return events.Where(e => e.Flags.Contains("ADD_ORG_ADDRESSEE") && jobParams.Docgroups.Any(dg => e.DueDocgroup.StartsWith(dg.Due)))
.GroupBy(e => e.ObjectId)
.Select(g => g.OrderBy(e => e.IslEvent).LastOrDefault())
.OrderBy(e => e.IslEvent)
.ToList();
}
protected override void EventProcess(IDataContext context, IEvntFeed evnt)
{
Logger.LogInformation("Обработка события № {0}, РК {1}", evnt.IslEvent, evnt.ObjectId);
//логика обработки
}
protected override void FillParams()
{
jobParams = GetJobAppSettingsParams<JobAddresseeDirectionCfg>();
commonParams = GetDefaultAppSettingsParams<CommonAddresseeDirectionCfg>();
}
}
|
При реализации класса, наследующего BaseDaemon:
Класс должен иметь переопределение методов Main() – содержит основной код работы фоновой задачи, Aborted(ThreadAbortException ex) – действия при прерывании работы ФЗ, Cancelled(OperationCanceledException ex) – действия при отмене ФЗ, Exception(Exception ex) – действия при возникновении ошибки, Finished() – действия при завершении работы ФЗ.
В теле метода Main() должен содержаться вызов/вызовы метода CheckCancel() для осуществления остановки ФЗ в точках данной проверки при получении команды на остановку задачи (при остановке ФЗ из Управления ФЗ или при остановке пула). Если в Main() присутствует бесконечный цикл while (true){ /* алгоритм работы ФЗ */ }, как для Автоматических ФЗ (DaemonKind.JOB), внутри цикла обязательно должен содержаться вызов CheckCancel(), иначе остановить ФЗ будет невозможно. Также, для циклов по Any(), где внутри цикла происходит загрузка значений в переменную, для которой вызывается метод Any(), также необходимо добавлять вызов CheckCancel() перед загрузкой новых значений, например: while(events.Any()){/*Обработка событий*/ CheckCancel(); events = GetNextEvent();}.
Для записи состояний ФЗ и соответствующих им сообщений используются методы MarkInfoLaunch(…), MarkInfoStart(), MarkInfoStop(…) и др. Состояния отображаются в WEB-интерфейсе управления ФЗ.
Описание класса BaseDaemon, атрибутов и методов приведены в Приложение Б.
Пример класса:
|
// Операции с фоновым заданием: инициализация, прерывание, остановка, завершение[Daemon("Тестовая группа", "Тестовая ФЗ", "Описание ФЗ", 1.0, DaemonKind.MANUAL, DaemonTypeConfig.AUTO_CONFIG)]public class TestDispatcher : BaseDaemon
{
protected override void Main()
{
MarkInfoLaunch("Инициализация тестовой ФЗ");
MarkInfoStart();
MarkInfoMessage("Выполнение ФЗ");
//Основной код фоновой задачи
}
protected override void Aborted(ThreadAbortException ex)
{
Logger.LogInformation("ФЗ прервана.");
}
protected override void Cancelled(OperationCanceledException ex)
{
Logger.LogInformation("ФЗ остановлена.");
}
protected override void Exception(Exception ex)
{
Logger.LogError(ex.ToString());
}
protected override void Finished()
{
Logger.LogInformation("ФЗ завершена.");
}
}
|
