Project structure overview

This section gives you an overview of the worker template project provided out of the box by the .NET Core templating system. We will use this type of project to implement a health-check worker. First of all, let's start by creating a new project using the following CLI command:

dotnet new worker -n HealthCheckWorker

This command creates a new folder called HealthCheckWorker and it creates all of the files needed by a basic worker service project. Let's have a look at the files created by the template dotnet new worker command executed previously.

Secondly, we can run the tree CLI command (available both on macOS X and Windows), which shows the folder structure of the project previously created:

.
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Worker.cs
├── HealthCheckWorker.csproj
├── appsettings.Development.json
├── appsettings.json
├── bin
│ └── Debug
│ └── netcoreapp3.0
└── obj
├── Debug
└── netcoreapp3.0

The Program.cs file is the entry point of our worker service. As the webapi template, the worker template uses the Program.cs file to initialize and retrieve a new IHostBuilder instance by calling the Host.CreateDefaultBuilder and ConfigureServices methods. The Main static method in the Program.cs file initializes a list of workers using the AddHostedService extension method:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HealthCheckWorker
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
}

As previously mentioned, the preceding snippet of code uses AddHostedService to initialize the Worker class created as a part of the default worker template. It is necessary to notice that, under the hood, AddHostedService initializes the class with a Singleton life cycle. Therefore, we will have one instance of the worker for the whole execution time of the worker service. In the next section, we will go deep into the life cycle execution of a worker.

Another major characteristic that distinguishes a worker project from any other .NET Core project is the use of the Microsoft.NET.Sdk.Worker SDK. Furthermore, we should also notice that the *.csproj file refers to only one additional NuGet package that provides the hosting extension methods used by the Program class and the Main method: Microsoft.Extensions.Hosting.

The next step is to create a new class that represents the configurations of the HealthCheckWorker project:

namespace HealthCheckWorker
{
public class HealthCheckSettings
{
public string Url { get; set; }
public int IntervalMs { get; set; }
}
}

HealthCheckSettings will contain two attributes: the Url and IntervalMs attributes. The first attribute contains the HTTP URL of the health check address, specified in appsettings.jsonThe IntervalMs attribute represents the frequency life cycle (in milliseconds) of the worker. 

Furthermore, it is possible to use the configuration system of .NET Core to bind our configuration object at the execution of the Main method of the Program.cs file, using the following approach:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace HealthCheckWorker
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
var healthCheckSettings = hostContext
.Configuration
.GetSection("HealthCheckSettings")


services.Configure<HealthCheckSettings>
(healthCheckSettings);

services.AddHostedService<Worker>();
});
}
}

The preceding code uses hostContext to retrieve the Configuration instance provided by .NET Core. By default, hostContext will be populated with the settings structure written in the appsettings.json file. Moreover, it is possible to use the GetSection method to retrieve the specific configuration section from our appsettings.json file and bind it to the HealthCheckSettings type.

After that, we can proceed with the concrete implementation of the Worker class. Furthermore, we are now able to inject the HealthCheckSettings type instance using the built-in dependency injection of .NET Core. The settings are injected using the IOption interface:

public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly HealthCheckSettings _settings;
private HttpClient _client;

public Worker(ILogger<Worker> logger,
IOptions<HealthCheckSettings> options)
{
_logger = logger;
_settings = options.Value;
}
...

The preceding code defines the attributes of the Worker class. As mentioned, the class implements an _settings attribute of the HealthCheckSettings type initialized using the constructor injection technique. Moreover, we can also see an HttpClient attribute, which will be initialized by the StartAsync method exposed by the BackgroundService class, and it is used in the ExecuteAsync method:

public class Worker : BackgroundService
{
...

public override Task StartAsync(CancellationToken cancellationToken)
{
_client = new HttpClient();
return base.StartAsync(cancellationToken);
}

protected override async Task ExecuteAsync(CancellationToken
stoppingToken)

{
while (!stoppingToken.IsCancellationRequested)
{
var result = await _client.GetAsync(_settings.Url);

if (result.IsSuccessStatusCode)
_logger.LogInformation($"The web service is up.
HTTP {
result.StatusCode}");


await Task.Delay(_settings.IntervalMs, stoppingToken);

}
}

...
}

After the initialization of the client, the ExecuteAsync method implements a while loop that will continue until the stoppingToken requests the cancellation of the process. The core part of the loop uses the GetAsync method of HttpClient to check whether the health check route returns an HTTP status message. Finally, the code calls Task.Delay with the IntervalMs property populated with the _settings instance. 

As the last step, the Worker class overrides the StopAsync method exposed by the BackgroundService class:

public class Worker : BackgroundService
{
...

public override Task StopAsync(CancellationToken cancellationToken)
{
_client.Dispose();
return base.StopAsync(cancellationToken);
}

}

The StopAsync method executes the HttpClient instance disposition by calling the Dispose() method, and it calls base.StopAsync(cancellationToken) of the base BackgroundService class. 

We should notice that the StartAsync and StopAsync methods always call the parent methods using the base keyword. Furthermore, in our case, we need to hold the behavior of the base BackgroundService class.

The next part of this chapter will be focused on the execution of the worker on a Docker container. We will see how to configure the Dockerfile, and deploy and run the application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.21.5