Browse Source

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.
pull/410/head
Cesar De la Torre 7 years ago
parent
commit
21484d6385
3 changed files with 97 additions and 60 deletions
  1. +83
    -0
      src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs
  2. +14
    -12
      src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs
  3. +0
    -48
      src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs

+ 83
- 0
src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs View File

@ -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.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// 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
///
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
/// <summary>
/// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task that represents
/// the lifetime of the long running operation(s) being performed.
/// </summary>
/// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
/// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
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;
}
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
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();
}
}
}

+ 14
- 12
src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs View File

@ -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<GracePeriodManagerService> _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);
}
}


+ 0
- 48
src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs View File

@ -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);
}
}

Loading…
Cancel
Save