个人技术分享

一、Worker Service基础概念

Worker Service 是.NET Core框架中用于创建长期运行的后台服务的一个强大组件。这类服务通常用于执行定时任务、监控、数据处理等不需要用户交互的任务。其核心优势在于提供了一种结构化、易于管理和部署的方式,使开发者能快速构建稳定、可扩展的后台服务应用。

二、创建一个基本的Worker Service

1. 创建项目

使用Visual Studio或dotnet CLI创建一个新的.NET Core Worker Service项目。在命令行中输入:

 

Bash

dotnet new worker -n MyWorkerService

这将生成一个名为MyWorkerService的项目,其中包含以下关键文件:

  • Program.cs: 应用程序入口点,负责启动主机(Host)。
  • Worker.cs: 继承自BackgroundService的类,实现后台服务的核心逻辑。

2. 编写Worker类

Worker.cs中已有一个继承自BackgroundServiceWorker类。BackgroundService实现了IHostedService接口,提供了启动、停止服务以及在后台循环执行工作的方法。

 

Csharp

using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

public class Worker : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            // 这里放置你的业务逻辑
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

            await Task.Delay(1000, stoppingToken);
        }
    }
}

注释:

  • ExecuteAsync:后台服务的主要执行逻辑所在。此方法会在服务启动后持续运行,直到收到取消信号(stoppingToken.IsCancellationRequestedtrue)。
  • _logger:通过依赖注入获得的日志记录器实例,用于记录服务运行状态。
  • Task.Delay:此处使用延时任务模拟周期性工作。实际应用中,您将替换为具体的业务处理代码。

3. 配置与启动服务

Program.cs中,CreateHostBuilder方法用于配置主机,包括添加服务、配置日志等。

 

Csharp

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

注释:

  • AddHostedService<Worker>():将Worker类注册为一个托管服务,使得主机在启动时自动初始化并运行它。

三、Worker Service的优化与进阶实践

1. 异步与并发处理

ExecuteAsync中,确保业务逻辑充分支持异步操作,避免阻塞线程。对于可并行的任务,可以使用Task.WhenAllParallel.ForEach等方法提高执行效率。

 

Csharp

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        var tasks = new List<Task>
        {
            ProcessTaskA(stoppingToken),
            ProcessTaskB(stoppingToken),
            // ...
        };

        await Task.WhenAll(tasks); // 等待所有任务完成

        _logger.LogInformation("All tasks processed at: {time}", DateTimeOffset.Now);

        await Task.Delay(60000, stoppingToken); // 每分钟执行一次
    }
}

private async Task ProcessTaskA(CancellationToken token)
{
    await Task.Run(() =>
    {
        // 异步执行任务A的同步代码
    }, token);
}

private async Task ProcessTaskB(CancellationToken token)
{
    // 直接编写异步任务B的代码
}

注释:

  • ProcessTaskAProcessTaskB:分别代表两个不同的异步任务,可根据需要调整。
  • Task.Run:用于将同步代码包装成异步任务,在单独的线程上执行,避免阻塞主线程。

2. 错误处理与重试策略

在处理网络请求、数据库操作等可能出现故障的情况时,应实现适当的错误处理和重试机制。

 

Csharp

private async Task<bool> ExecuteWithRetry(Func<Task> action, int maxRetries = 3, int delayBetweenRetries = 5000)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            await action();
            return true;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error during execution, retrying in {delay} ms (attempt {attempt})", delayBetweenRetries, i + 1);

            if (i < maxRetries - 1)
                await Task.Delay(delayBetweenRetries, stoppingToken);
        }
    }

    _logger.LogError("Maximum retries reached, giving up");
    return false;
}

注释:

  • ExecuteWithRetry:定义一个通用的重试方法,接受一个异步操作(action)作为参数,以及最大重试次数和两次重试之间的延迟。
  • catch块中捕获异常,记录错误日志,并在满足条件时进行下一次重试。

3. 配置与依赖注入

利用.UseConfiguration读取配置文件,将配置项注入到服务中,实现动态调整服务行为。

 

Csharp

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            var config = hostContext.Configuration;
            var workerSettings = config.GetSection("WorkerSettings").Get<WorkerSettings>();

            services.AddSingleton(workerSettings); // 注入配置对象
            services.AddHostedService<Worker>();
        });

public class WorkerSettings
{
    public int IntervalSeconds { get; set; }
    public string ConnectionString { get; set; }
    // 其他配置项...
}

注释:

  • GetSection("WorkerSettings").Get<WorkerSettings>():从配置文件中读取WorkerSettings部分,并将其转换为指定类型的对象。
  • services.AddSingleton(workerSettings):将配置对象注册为单例服务,供Worker或其他服务通过依赖注入访问。

4. 日志记录与监控

集成成熟的日志框架(如Serilog、NLog等)以增强日志输出能力。同时,考虑使用Application Insights、Prometheus等监控工具收集性能指标和异常信息。

 

Csharp

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSerilog() // 添加Serilog日志框架支持
        .ConfigureServices((hostContext, services) =>
        {
            // ...
        });

注释:

  • .UseSerilog():启用Serilog日志框架,需在项目中正确配置Serilog。

5. 可观测性与健康检查

实现IHealthCheck接口,提供健康检查端点,以便外部监控系统检查服务状态。

 

Csharp

public class DatabaseHealthCheck : IHealthCheck
{
    private readonly string _connectionString;

    public DatabaseHealthCheck(string connectionString)
    {
        _connectionString = connectionString;
    }

    public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        using var connection = new SqlConnection(_connectionString);
        try
        {
            await connection.OpenAsync(cancellationToken);
            return HealthCheckResult.Healthy("Database connection successful.");
        }
        catch (Exception ex)
        {
            return HealthCheckResult.Unhealthy("Failed to connect to the database.", exception: ex);
        }
    }
}

注释:

  • DatabaseHealthCheck:实现IHealthCheck接口,用于检查数据库连接是否正常。
  • CheckHealthAsync:执行实际的健康检查逻辑,返回HealthCheckResult表示检查结果。

Startup.cs中注册健康检查服务:

 

Csharp

public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks()
        .AddCheck<DatabaseHealthCheck>("Database");
}

结语

通过遵循上述步骤和优化建议,您已经掌握了如何在C#中实现一个健壮、可扩展且易于维护的.NET Core Worker Service。结合实际应用场景,持续进行性能调优、错误处理、可观测性增强等方面的改进,将确保您的后台服务在生产环境中稳定、高效地运行。