Как сделать постоянную фоновую задачу сигналом об ограниченном сервисе в ядре asp.net?

В моем веб-приложении ASP.NET Core 2.2 у меня есть эти службы в моем методе Startup.ConfigureServices (IServiceCollection services):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDirectoryBrowser();

    services.AddScoped<WebApplication.Services.DoThingAlpha>();
    services.AddScoped<WebApplication.Services.DoThingBravo>();
    services.AddScoped<WebApplication.Services.DoThingCharlie>();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

Эти сервисы с ограниченной областью видимости делают грязную работу за мои контроллеры.

Однако мне необходимо постоянно прослушивать порт UDP в фоновом режиме, а затем уведомлять соответствующую службу (DoThingsAlpha, DoThingsBravo или DoThingsCharlie), когда на прослушивателе получены определенные сообщения UDP.

Я думаю, что могу реализовать фоновую службу с помощью BackgroundService (https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/background-tasks-with-ihostedservice), а затем добавьте в коллекцию сервисов в ConfigureServices. Назовем это ListenOnUDP: BackgroundService.

Проблема в том, как мне сообщить этим другим службам с определенной областью действия, что сообщение UDP было получено в ListenOnUDP? Каков рекомендуемый шаблон для решения такого рода проблем? Несомненно, он есть.


person Timothy John Laird    schedule 01.03.2019    source источник
comment
Вы говорите, что службы с ограниченными областями должны иметь возможность блокировать, пока не будет получена определенная дейтаграмма? Или вы имеете в виду, что службы просто должны знать самую последнюю датаграмму, например когда он был получен и что в нем было?   -  person John Wu    schedule 01.03.2019
comment
То же, что и тот, который вы уже используете - используйте DI для регистрации интерфейса уведомлений, от которого зависят службы с заданной областью действия. Этот интерфейс будет реализован непосредственно фоновой службой или, что еще лучше, объектом-уведомителем, созданным этой службой. Этот notifier интерфейс, в свою очередь, может предоставлять уведомления в виде обратных вызовов или задач на основе TaskCompletionSource.   -  person Panagiotis Kanavos    schedule 01.03.2019
comment
@JohnWu службам с заданной областью действия необходимо заблокировать (подождать) примерно 1 секунду, а затем отказаться, если дейтаграмма не получена. Если прошла секунда, мы можем предположить, что она никогда не появится.   -  person Timothy John Laird    schedule 01.03.2019
comment
@TimothyJohnLaird вместо блокировки они могут ждать задачи, созданной источником задачи, управляемым фоновой службой   -  person Panagiotis Kanavos    schedule 01.03.2019
comment
@PanagiotisKanavos, если есть отмена, если прошло более одной секунды, я так думаю. Чтобы объяснить мой вариант использования ... служба с ограниченной областью видимости в настоящее время использует await UdpClient.Sendasync, чтобы дать команду другому процессу (который слушает) что-то сделать. Мне нужно добавить некоторую логику (как-то), чтобы получить подтверждение UDP, а затем вернуться к контроллеру, что либо не было получено ACK, либо было получено ACK.   -  person Timothy John Laird    schedule 01.03.2019
comment
Почему бы не вернуть сигнал по UDP клиенту, который отправил сообщение для начала? Если клиент - это служба той же области действия, это будет соответствовать требованиям, не так ли?   -  person John Wu    schedule 01.03.2019
comment
@JohnWu, поэтому мы находимся на одной странице ... HTTP-запрос от браузера = ›Контроллер =› Scoped Service = ›Udp Client to Other Process =› Udp Client in Other process ACKS back = ›Scoped service получает уведомление об ACK ( каким-то образом ???) = ›Контроллер отвечает на полученную команду пользователя =› HTTP-ответ браузеру   -  person Timothy John Laird    schedule 01.03.2019


Ответы (1)


Я нашел решение и думаю, что проблема возникла из-за моего фундаментального непонимания служб ASP.NET Core. Я объясню, если у кого-то возникнет такая же проблема (или, возможно, подтвердю или аннулирую мой ответ).

Когда вы добавляете класс / интерфейс службы в свою коллекцию IServiceCollection, эта служба становится доступной для других служб через интерфейс. Вы просто включаете интерфейс в конструктор другой службы, которую хотите вызвать в своем конструкторе, сохраняете его, и затем вы можете вызвать интерфейс (то, что я понимаю, является проявлением внедрения зависимостей). Вот так:

    private readonly InterfaceRCResponseQueue _rcResponseQueue;
    private readonly ILogger _logger;

    public UdpListenerBackgroundService(
        InterfaceRCResponseQueue rcResponseQueue,
        ILogger<UdpListenerBackgroundService> logger
        )
    {
        _rcResponseQueue = rcResponseQueue;
        _logger = logger;
    }

Это эффективно позволяет одной службе сигнализировать другой, вызывая методы интерфейса.

Уловка:

Одноэлементная служба не может зависеть от ограниченной или временной службы. Предположительно из-за того, что он вешает работу с ограниченной или временной службой. Хотя обратное - нормально.

https://blog.markvincze.com/two-gotchas-with-scoped-and-singleton-dependencies-in-asp-net-core/

В моем случае у меня есть класс UdpClient, который должен прослушивать сообщения, а затем сигнализировать другим службам.

Для этого я помещаю свой класс UdpClient в класс, который наследуется от Microsoft.Extensions.Hosting.BackgroundService и поэтому создается и удаляется вместе с жизненным циклом приложения. Я также создал одноэлементный класс, содержащий очередь.

Когда мой класс UdpClient в моем классе BackgroundService получает дейтаграмму UDP, я вызываю метод в одноэлементном классе, который помещает его в очередь одноэлементного класса.

После этого одноэлементный или временный класс с заданной областью может зависеть от одноэлементного класса и искать данные в этой очереди (исключать из очереди) на досуге. Вы просто не можете сделать обратное (сделать одноэлементный зависимым от ограниченного или транзитивного сервиса).

Это форма сигнализации, которую я придумал, чтобы косвенно уведомить меньшую службу из одноэлементной или фоновой службы.

person Timothy John Laird    schedule 14.03.2019
comment
Спасибо за вашу рецензию, она подтолкнула меня в правильном направлении. Я использовал BackgroundService, а затем задавался вопросом, почему мои службы с ограниченной областью действия действуют как одиночные. В итоге я перешел к IServiceScopeFactory.CreateScope для создания новых областей, когда моему обработчику сообщений нужно было еще немного поработать. Кажется, работает хорошо. - person JackMorrissey; 22.10.2019