Servicios de trabajo en .NET
Hay numerosas razones para crear servicios de ejecución larga, como:
- Procesamiento de datos de uso intensivo de CPU.
- Cola de elementos de trabajo en segundo plano
- Realización de una operación basada en el tiempo según una programación
Normalmente, el procesamiento de servicios en segundo plano no implica una interfaz de usuario (UI), pero se pueden crear interfaces de usuario en torno a ellos. En los inicios de .NET Framework, los desarrolladores de Windows podían crear servicios de Windows para estos fines. Ahora, con .NET, puede usar BackgroundService, que es una implementación de IHostedService, o implementar el suyo propio.
Con .NET, ya no está limitado a Windows. Puede desarrollar servicios en segundo plano multiplataforma. Los servicios hospedados están listos para el registro, la configuración y la inserción de dependencias (ID). Forman parte del conjunto de extensiones de bibliotecas, lo que significa que son fundamentales para todas las cargas de trabajo de .NET que funcionan con el host genérico.
Importante
La instalación del SDK de .NET también instala Microsoft.NET.Sdk.Worker
y la plantilla de trabajo. Es decir, después de instalar el SDK de .NET, puede crear un nuevo trabajo (“new worker”) mediante el comando dotnet new worker. Si usa Visual Studio, la plantilla se oculta hasta que se instala la carga de trabajo opcional de ASP.NET y desarrollo web.
Terminología
Muchos términos se usan erróneamente como sinónimos. En esta sección se definen algunos de estos términos para que su intención en este artículo sea más evidente.
- Servicio en segundo plano: el tipo BackgroundService.
- Servicio hospedado: implementaciones de IHostedService o del propio IHostedService.
- Servicio de ejecución larga: cualquier servicio que se ejecuta continuamente.
- Windows Service: la infraestructura de Windows Service, originalmente basada en .NET Framework, a la que se puede acceder ahora desde .NET.
- Worker Service: la plantilla Servicio de trabajo.
Plantilla Worker Service
La plantilla Servicio de trabajo está disponible para la CLI de .NET y Visual Studio. Para más información, vea la plantilla dotnet new worker
de la CLI de .NET. La plantilla consta de una clase Program
y Worker
.
using App.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
La clase Program
anterior:
- Crea una interfaz HostApplicationBuilder.
- Llama AddHostedService a para registrar
Worker
como un servicio hospedado. - Compila una interfaz IHost a partir del generador.
- Llama a
Run
en la instancia dehost
, que ejecuta la aplicación.
Valores predeterminado de plantilla
La plantilla del rol de trabajo no habilita la recolección de elementos no utilizados del servidor (GC) de forma predeterminada, ya que hay numerosos factores que desempeñan un papel para determinar su necesidad. Todos los escenarios que requieren servicios de ejecución prolongada deben tener en cuenta las implicaciones de rendimiento de este valor predeterminado. Para habilitar la recolección de elementos no utilizados del servidor, agregue el nodo ServerGarbageCollection
al archivo del proyecto:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
Ventajas y consideraciones
habilitado | Disabled |
---|---|
Administración eficaz de memoria: reclama automáticamente memoria no utilizada para evitar pérdidas de memoria y optimizar el uso de recursos. | Rendimiento mejorado en tiempo real: evita posibles pausas o interrupciones causadas por la recolección de elementos no utilizados en aplicaciones sensibles a la latencia. |
Estabilidad a largo plazo: ayuda a mantener un rendimiento estable en servicios de larga duración mediante la administración de memoria durante períodos prolongados. | Eficiencia de los recursos: puede conservar los recursos de CPU y memoria en entornos con restricción de recursos. |
Mantenimiento reducido: minimiza la necesidad de administración manual de memoria, lo que simplifica el mantenimiento. | Control de memoria manual: proporciona un control específico sobre la memoria para aplicaciones especializadas. |
Comportamiento predecible: contribuye al comportamiento de aplicación coherente y predecible. | Adecuado para procesos de corta duración: minimiza la sobrecarga de recolección de elementos no utilizados para procesos efímeros o de corta duración. |
Para obtener más información sobre las consideraciones de rendimiento, vea Recolección de elementos no utilizados del servidor. Para obtener más información sobre cómo configurar la recolección de elementos no utilizados del servidor, vea Ejemplos de configuración de la recolección de elementos no utilizados del servidor.
Clase del rol de trabajo
En cuanto a Worker
, la plantilla proporciona una implementación sencilla.
namespace App.WorkerService;
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}
La clase Worker
anterior es una subclase de BackgroundService, que implementa IHostedService. BackgroundService es abstract class
y requiere la subclase para implementar BackgroundService.ExecuteAsync(CancellationToken). En la implementación de la plantilla, los bucles ExecuteAsync
se recorren una vez por segundo, registrando la fecha y hora actuales hasta que se señale el proceso para la cancelación.
El archivo del proyecto
La plantilla Worker se basa en el siguiente archivo del proyecto Sdk
:
<Project Sdk="Microsoft.NET.Sdk.Worker">
Para más información, vea SDK de proyectos de .NET.
Paquete de NuGet
Una aplicación basada en la plantilla Worker usa el SDK de Microsoft.NET.Sdk.Worker
y tiene una referencia de paquete explícita al paquete Microsoft.Extensions.Hosting.
Contenedores y adaptabilidad a la nube
Con las cargas de trabajo de .NET más modernas, los contenedores son una opción viable. Al crear un servicio de ejecución larga a partir de la plantilla de Worker en Visual Studio, puede optar por la compatibilidad con Docker. Al hacerlo, se crea un Dockerfile que contiene la aplicación .NET. Un Dockerfile es un conjunto de instrucciones para compilar una imagen. En el caso de las aplicaciones .NET, el Dockerfile normalmente se encuentra en la raíz del directorio junto a un archivo de solución.
# See https://aka.ms/containerfastmode to understand how Visual Studio uses this
# Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/runtime:8.0@sha256:e6b552fd7a0302e4db30661b16537f7efcdc0b67790a47dbf67a5e798582d3a5 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /src
COPY ["background-service/App.WorkerService.csproj", "background-service/"]
RUN dotnet restore "background-service/App.WorkerService.csproj"
COPY . .
WORKDIR "/src/background-service"
RUN dotnet build "App.WorkerService.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "App.WorkerService.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App.WorkerService.dll"]
Los pasos anteriores del Dockerfile incluyen:
- Establecer la imagen base de
mcr.microsoft.com/dotnet/runtime:8.0
como el aliasbase
. - Cambiar el directorio de trabajo a /app.
- Establecer el alias
build
de la imagenmcr.microsoft.com/dotnet/sdk:8.0
. - Cambiar el directorio de trabajo a /src.
- Copiar el contenido y publicar la aplicación .NET:
- La aplicación se publica mediante el comando
dotnet publish
.
- La aplicación se publica mediante el comando
- Retransmitir la imagen del SDK de .NET desde
mcr.microsoft.com/dotnet/runtime:8.0
(el aliasbase
). - Copiar la salida de compilación publicada de /publish.
- Definir el punto de entrada, que delega en
dotnet App.BackgroundService.dll
.
Sugerencia
MRC en mcr.microsoft.com
significa "Microsoft Container Registry", y es el catálogo de contenedores sindicados de Microsoft del centro de Docker oficial. El artículo Catálogo de contenedores sindicados de Microsoft contiene información adicional.
Al establecer Docker como destino de una estrategia de implementación para la plantilla de Worker Service de .NET, hay algunas consideraciones en el archivo del proyecto:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WorkerService</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
En el archivo del proyecto anterior, el elemento <DockerDefaultTargetOS>
especifica Linux
como destino. Para establecer los contenedores de Windows como destino, use Windows
en su lugar. El paquete de NuGet Microsoft.VisualStudio.Azure.Containers.Tools.Targets
se agrega automáticamente como una referencia de paquete cuando se selecciona la compatibilidad con Docker en la plantilla.
Para más información sobre Docker con .NET, vea Tutorial: Incluir una aplicación de .NET Core en un contenedor. Para más información sobre la implementación en Azure, vea Tutorial: Implementación de una plantilla Worker Service en Azure.
Importante
Si quiere utilizar los secretos de usuario con la plantilla Worker, tendría que hacer referencia explícitamente al paquete NuGet Microsoft.Extensions.Configuration.UserSecrets
.
Extensibilidad del servicio hospedado
La interfaz IHostedService define dos métodos:
Estos dos métodos sirven como métodos de ciclo de vida: se invocan durante los eventos de inicio y de detección del host, respectivamente.
Nota:
Al invalidar los métodos StartAsync o StopAsync, debe llamar y usar await
con el método de clase base
para garantizar que el servicio se inicia o se cierra adecuadamente.
Importante
La interfaz actúa como una restricción de parámetro de tipo genérico en el método de extensión AddHostedService<THostedService>(IServiceCollection), lo que significa que solo se permiten implementaciones. Puede usar la clase BackgroundService proporcionada con una subclase, o bien implementar la suya propia por completo.
Finalización de señal
En los escenarios más comunes, no es necesario indicar explícitamente la finalización de un servicio hospedado. Cuando el host inicia los servicios, están diseñados para ejecutarse hasta que se detenga el host. Sin embargo, en algunos escenarios, es posible que tenga que indicar la finalización de toda la aplicación host cuando se complete el servicio. Para señalar la finalización, considere el uso de la siguiente clase Worker
:
namespace App.SignalCompletionService;
public sealed class Worker(
IHostApplicationLifetime hostApplicationLifetime,
ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// TODO: implement single execution logic here.
logger.LogInformation(
"Worker running at: {Time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
// When completed, the entire app host will stop.
hostApplicationLifetime.StopApplication();
}
}
En el código anterior, el método ExecuteAsync
no realiza un bucle y, cuando se completa, llama a IHostApplicationLifetime.StopApplication().
Importante
Esto indicará al host que debe detenerse y sin esta llamada al StopApplication
host seguirá ejecutándose indefinidamente.
Para más información, consulte:
- .NET Generic Host: IHostApplicationLifetime
- Host genérico de .NET: apagado del host
- Host genérico de .NET: proceso de apagado de hospedaje
Consulte también
- Tutoriales de la subclase BackgroundService:
- Implementación de la interfaz IHostedService personalizada: