From 21484d6385a49e3072a502cd76bf2c50b048e439 Mon Sep 17 00:00:00 2001 From: Cesar De la Torre Date: Sat, 18 Nov 2017 14:24:07 -0800 Subject: [PATCH] Improved the IHostedService background task (GracePeriodManagerService) in the Ordering microservice to use the new BackgroundService abstract base class from .NET Core 2.1 (.NET Standard 2.1). We're currently using that class code embeded in eShopOnContainers project until .NET Core 2.1 is released with this new class. The new class BackgroundService is compatible with the current IHostedService interface in .NET Core 2.0. I removed the similar but nont-tested HostedService base class (it was not official from the .NET team) that we were previously using. --- .../HostedServices/BackgroundService.cs | 83 +++++++++++++++++++ .../GracePeriodManagerService.cs | 26 +++--- .../HostedServices/HostedService.cs | 48 ----------- 3 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs delete mode 100644 src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs new file mode 100644 index 000000000..205b3c75a --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Hosting; + +namespace Ordering.API.Infrastructure.HostedServices +{ + // Copyright (c) .NET Foundation. All rights reserved. + // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + /// + /// Base class for implementing a long running . + /// IMPORTANT: This base class is implemented in .NET Core 2.1 - Since this microservice is still in .NET Core 2.0, we're using the class within the project + /// When .NET Core 2.1 is released, this class should be removed and you should use the use implemented by the framework + /// https://github.com/aspnet/Hosting/blob/712c992ca827576c05923e6a134ca0bec87af4df/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs + /// + /// + public abstract class BackgroundService : IHostedService, IDisposable + { + private Task _executingTask; + private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource(); + + /// + /// This method is called when the starts. The implementation should return a task that represents + /// the lifetime of the long running operation(s) being performed. + /// + /// Triggered when is called. + /// A that represents the long running operations. + protected abstract Task ExecuteAsync(CancellationToken stoppingToken); + + /// + /// Triggered when the application host is ready to start the service. + /// + /// Indicates that the start process has been aborted. + public virtual Task StartAsync(CancellationToken cancellationToken) + { + // Store the task we're executing + _executingTask = ExecuteAsync(_stoppingCts.Token); + + // If the task is completed then return it, this will bubble cancellation and failure to the caller + if (_executingTask.IsCompleted) + { + return _executingTask; + } + + // Otherwise it's running + return Task.CompletedTask; + } + + /// + /// Triggered when the application host is performing a graceful shutdown. + /// + /// Indicates that the shutdown process should no longer be graceful. + public virtual async Task StopAsync(CancellationToken cancellationToken) + { + // Stop called without start + if (_executingTask == null) + { + return; + } + + try + { + // Signal cancellation to the executing method + _stoppingCts.Cancel(); + } + finally + { + // Wait until the task completes or the stop token triggers + await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); + } + + } + + public virtual void Dispose() + { + _stoppingCts.Cancel(); + } + } + + +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs index 3fa70e20b..26017dfd0 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs @@ -12,8 +12,7 @@ using System.Threading; using System.Threading.Tasks; - public class GracePeriodManagerService - : HostedService + public class GracePeriodManagerService : BackgroundService { private readonly OrderingSettings _settings; private readonly ILogger _logger; @@ -25,26 +24,28 @@ { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); - _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings)); } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - while (true) + _logger.LogDebug($"GracePeriod background task is starting."); + + stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriod background task is stopping.")); + + while (!stoppingToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) - { - break; - } + _logger.LogDebug($"GracePeriod background task is doing background work."); CheckConfirmedGracePeriodOrders(); - await Task.Delay(_settings.CheckUpdateTime, cancellationToken); + await Task.Delay(_settings.CheckUpdateTime, stoppingToken); continue; } + _logger.LogDebug($"GracePeriod background task is stopping."); + await Task.CompletedTask; } @@ -54,10 +55,11 @@ var orderIds = GetConfirmedGracePeriodOrders(); + _logger.LogDebug($"GracePeriod sent a ."); foreach (var orderId in orderIds) { - var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId); - _eventBus.Publish(confirmGracePeriodEvent); + var gracePeriodConfirmedEvent = new GracePeriodConfirmedIntegrationEvent(orderId); + _eventBus.Publish(gracePeriodConfirmedEvent); } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs deleted file mode 100644 index 98ee4907a..000000000 --- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.Extensions.Hosting; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Infrastructure.HostedServices -{ - public abstract class HostedService : IHostedService - { - // Example untested base class code kindly provided by David Fowler: https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3 - - private Task _executingTask; - private CancellationTokenSource _cts; - - public Task StartAsync(CancellationToken cancellationToken) - { - // Create a linked token so we can trigger cancellation outside of this token's cancellation - _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - - // Store the task we're executing - _executingTask = ExecuteAsync(_cts.Token); - - // If the task is completed then return it, otherwise it's running - return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; - } - - public async Task StopAsync(CancellationToken cancellationToken) - { - // Stop called without start - if (_executingTask == null) - { - return; - } - - // Signal cancellation to the executing method - _cts.Cancel(); - - // Wait until the task completes or the stop token triggers - await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); - - // Throw if cancellation triggered - cancellationToken.ThrowIfCancellationRequested(); - } - - // Derived classes should override this and execute a long running method until - // cancellation is requested - protected abstract Task ExecuteAsync(CancellationToken cancellationToken); - } -}