The next code is the abstract BackgroundService base class as implemented in .NET Core 2.1.
When deriving from the previous abstract base class, thanks to that inherited implementation, you just need to implement the ExecuteAsync() method in your own custom hosted service class, as in the following simplified code from eShopOnContainers which is polling a database and publishing integration events into the Event Bus when needed.
In this specific case for eShopOnContainers, it is executing an application method that is quering a database table looking for orders with a specific state and when applying changes, it is publishing integration events through the event bus (underneath it can be using RabbitMQ or Azure Service Bus).
Of course, you could run any other business background task, instead.
By default, the cancellation token is set with a 5 second timeout, although you can change that value when building your WebHost using the UseShutdownTimeout extension of the IWebHostBuilder. This means that our service is expected to cancel within 5 seconds otherwise it will be more abruptly killed.
The following code would be changing that time to 10 seconds.
The following image shows a visual summary of the classes and interfaced involved when implementing IHostedServices.
Figure 6-27. Class diagram showing the multiple classes and interfaces related to IHostedService
It is important to note that the way you deploy your ASP.NET Core WebHost or .NET Core Host might impact the final solution. For instance, if you deploy your WebHost on IIS or a regular Azure App Service, your host can be shut down because of app pool recycles. But if you are deploying your host as a container into an orchestrator like Kubernetes or Service Fabric, you can control the assured number of live instances of your host. In addition, you could consider other approaches in the cloud especially made for these scenarios, like Azure Functions.
But even for a WebHost deployed into an app pool, there are scenarios like repopulating or flushing application’s in-memory cache, that would be still applicable.
The IHostedService interface provides a convenient way to start background tasks in an ASP.NET Core web application (in .NET Core 2.0) or in any process/host (starting in .NET Core 2.1 with IHost). Its main benefit is the opportunity you get with the graceful cancellation to clean-up code of your background tasks when the host itself is shutting down.
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedservice
https://github.com/aspnet/Hosting/tree/dev/samples/GenericHostSample
In the reference microservice application eShopOnContainers, it is using Ocelot because it is a simple and lightweight API Gateway that you can deploy anywhere along with your microservices/containers such as in any of the following environments used by eShopOnContainers.
The following architecture diagram shows how API Gateways are implemented with Ocelot in eShopOnContainers.
Figure 6-28. eShopOnContainers architecture with API Gateways
That diagram shows how the whole application is deployed into a single Docker host or development PC with “Docker for Windows” or “Docker for Mac”. However, deploying into any orchestrator would be pretty similar but any container in the diagram could be scaled-out in the orchestrator and the infrastructure assets such as databases, cache and message brokers should be offloaded from the orchestrator and deployed into high available systems for infrastructure, like Azure SQL Database, Azure Cosmos DB, Azure Redis, Azure Service Bus, or any HA clustering solution on-premises.
As you can also notice in the diagram, having several API Gateways allows the multiple development teams to be autonomous (in this case Marketing vs. Shopping) when developing and deploying their microservices plus their own related API Gateways. If you had a single monolithic API Gateway that would mean a single point to be updated by multiple development teams which could couple all the microservices with a single part of the application.
Going much further in the design, sometimes a fine-grained API Gateway can also be limited to a single business microservice depending on the chosen architecture. Having the API Gateway’s boundaries dictated by the business or domain will help you to get a better design. For instance, fine granularity in the API Gateway tier can be especially useful for more advanced composite UI applications based on microservices, because the concept of a fine-grained API Gateway is similar to a UI composition service. We discuss this topic in the section “Creating composite UI based on microservices”.
As key takeaway, for many medium- and large-size applications, using a custom-built API Gateway product is usually a good approach, but not as a single monolithic aggregator or unique central custom API Gateway unless that API Gateway allows multiple independent configuration areas for the several development teams creating autonomous microservices.
As an example, eShopOnContainers has around 6 internal microservice-types that have to be published through the API Gateways, as shown in the following image.
Figure 6-29. Microservice folders in eShopOnContainers solution in Visual Studio
In the case of the Identity service, in our design we left it out of the API Gateway routing, but with Ocelot it is also possible to include it as part of the re-routing lists.
All those services are currently implemented as ASP.NET Core Web API services, as you can tell from the code. Let’s focus on one of the microservices like the Catalog microservice code.
Figure 6-30. Sample Web API microservice (Catalog microservice)
You can see this is a pretty typical ASP.NET Core Web API project with several controllers and methods like in the following code.
The HTTP request will end up running that kind of C# code accessing the microservice database, etc.
In regards to the microservice URL, when the containers are deployed in your local development PC (local Docker host), each microservice’s container has always an internal port (usually port 80) specified in its dockerfile, as in the following dockerfile:
The port 80 shown in the code is internal within the Docker host, so it cannot be reached by the client apps.
The client apps can access only to the external ports (if any) published when deploying with docker-compose.
Those external ports should not be published when deploying into a production environment. This is precisely why you want to use the API Gateway, to avoid the direct communication between the client apps and the microservices.
However, when developing, you want to access the microservice/container directly and run it through Swagger. That’s why in eShopOnContainers, the external ports are still specified even when they won’t be used by the API Gateway or the client apps.
Here’s an example of the docker-compose.override.yml file for the Catalog microservice
You can see how in the docker-compose.override.yml configuration the internal port for the Catalog container is port 80, but the port for external access is 5101. But this port shouldn’t be used by the application when using an API Gateway, only to debug, run and test just the Catalog microservice.
Note that you usually won’t be deploying with docker-compose into a production environment because the right production deployment environment for microservices is an orchestrator like Kubernetes or Service Fabric. When deploying to those environments you use different configuration files where you won’t publish directly any external port for the microservices but, you’ll always use the reverse proxy from the API Gateway.
Run the catalog microservice in your local Docker host either by running the full eShopOnContainers solution from Visual Studio (it’ll run all the services in the docker-compose files) or just starting the Catalog microservice with the following docker-compose command in CMD or PowerShell positioned at the folder where the docker-compose.yml and docker-compose.override.yml are placed.
This command only runs the catalog.api service container plus dependencies that are specified in the docker-compose.yml. In this case, the SQL Server container and RabbitMQ container.
Then, you can directly access the Catalog microservice and see its methods through the Swagger UI accessing directly through that “external” port, in this case http://localhost:5101
Figure 6-31. Testing the Catalog microservice with its Swagger UI
At this point you could set a breakpoint in C# code in VS, test the microservice with the methods exposed in Swagger UI, and finally clean-up everything with the docker-compose down command.
But this direct access communication to the microservice (in this case through the external port 5101) is precisely what you want to avoid in your application by setting the additional level of indirection of the API Gateway (Ocelot, in this case). Therefore, the client app won’t directly access the microservice.
Ocelot is basically a set of middlewares that you can apply in a specific order.
Ocelot is designed to work with ASP.NET Core only. It targets netstandard2.0. This means it can be used anywhere .NET Standard 2.0 is supported, including .NET Core 2.0 runtime and .NET Framework 4.6.1 runtime and up.
You install Ocelot and its dependencies in your ASP.NET Core project with Ocelot’s NuGet package, from Visual Studio.
In the case of eShopOnContainers, its API Gateway implementation is a very simple ASP.NET Core WebHost project, and Ocelot’s middlewares handle all the API Gateway features, as shown in the following image:
Figure 6-32. The OcelotApiGw base project in eShopOnContainers
This ASP.NET Core WebHost project is basically made with two simple files, the Program.cs and Startup.cs.