正确使用 Worker Service 并从 Web Api 注册其服务(相同的解决方案)

Correct use of Worker Service and registration of its services from Web Api (same solution)

我正在尝试寻找合适的架构来解决这个问题: 我需要构建一个服务于 REST 控制器的 API。这个 REST 控制器只是访问存储在字典中的值并将它们提供给用户。 Dictionary 数据结构中有什么?有数据是 scraping 外部 api.

的结果

解决方案 1:我没有构建 worker 服务,只构建了我的 API。我的 API 当它启动时将 运行 一个不同的线程异步保持 scraping 外部 API 并将数据保存在这个字典中。

解决方案 2 - 我采用的解决方案是因为我认为更好(如果我错了请纠正我):我构建了一个 Worker Service,它在我的 API 启动时被调用,其作用是抓取外部 API 并将数据保存在字典中。此类数据将由我的 API.

中的控制器提供

所以,首先我不知道我对第一个的选择是否正确。如果是...我在配置 worker 时遇到问题。

Program.cs 我的 API:

public class Program
{
    public static void Main(string[] args)
    {
        var webHost = CreateHostBuilder(args).Build();

        var configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
            .Build();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Startup.cs 我的 API:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddHostedService<Worker>(); //here I register my worker service
}

Program.cs 我的工人:

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.AddSingleton<IScraperService, ScraperService>();

                // Inject IHttpClientFactory
                services.AddHttpClient();
                services.AddHostedService<Worker>();
            });
}

我的工人:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IScraperService _scraperService;

    public Worker(ILogger<Worker> logger, IScraperService scraperService)
    {
        _logger = logger;
        _scraperService = scraperService;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _scraperService.ScrapeObjects();
        }
    }
}

所以当我的 API 开始时会打电话给工作人员(或至少尝试)但投诉。它说我的工作人员对 IScraperService 的注册一无所知。当然不是..因为我在我的Worker Service项目的ConfigureServices注册了,从来没有调用过,因为我没有自己启动它。

可能的解决方案:在我的 API 启动时注册 ISCraperService (services.AddSingleton();) 是否正确?如果整个错误,请告诉我。谢谢!

让两者一起工作

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.AddSingleton<IScraperService, ScraperService>();

                // Inject IHttpClientFactory
                services.AddHttpClient();
                services.AddHostedService<Worker>();
            })
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>();
            });
}

启动

public void ConfigureServices(IServiceCollection services) {
    services.AddControllers();    
    //services.AddHostedService<Worker>(); //worker already registered in Main

    //...
}

服务容器现在应该知道所有服务。

当主机 运行 时,API 将侦听请求并启动 worker。

public class Worker : BackgroundService {
    private readonly ILogger<Worker> _logger;
    private readonly IScraperService _scraperService;

    public Worker(ILogger<Worker> logger, IScraperService scraperService) {
        _logger = logger;
        _scraperService = scraperService;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
            await Task.Run(() => _scraperService.ScrapeObjects());
        }
    }
}