ASP.NET Core 基礎知識(十二)【Host之通用主機】

風靈使發表於2019-02-20

.NET Core 應用配置和啟動“主機”。 主機負責應用程式啟動和生存期管理。 本主題介紹 ASP.NET Core 通用主機 (HostBuilder),該主機對於託管不處理 HTTP 請求的應用非常有用。 有關 Web 主機 (WebHostBuilder) 的介紹,請參閱 ASP.NET Core Web 主機

通用主機的目標是將 HTTP 管道從 Web 主機 API 中分離出來,從而啟用更多的主機方案。 基於通用主機的訊息、後臺任務和其他非 HTTP 工作負載可從橫切功能(如配置、依賴關係注入 [DI] 和日誌記錄)中受益。

通用主機是 ASP.NET Core 2.1 中的新增功能,不適用於 Web 承載方案。 對於 Web 承載方案,請使用 Web 主機。 通用主機正處於開發階段,用於在未來版本中替換 Web 主機,並在 HTTP 和非 HTTP 方案中充當主要的主機 API。

Visual Studio Code 中執行示例應用時,請使用外部或整合終端。 請勿在 internalConsole 中執行示例。

在 Visual Studio Code 中設定控制檯:

  1. 開啟 .vscode/launch.json 檔案。
  2. 在 .NET Core 啟動(控制檯)配置中,找到控制檯條目。 將值設定為 externalTerminalintegratedTerminal

介紹

通用主機庫位於 Microsoft.Extensions.Hosting 名稱空間中,由 Microsoft.Extensions.Hosting 包提供。Microsoft.AspNetCore.App 元包ASP.NET Core 2.1 或更高版本)中包括 Microsoft.Extensions.Hosting 包。

IHostedService 是執行程式碼的入口點。 每個 IHostedService 實現都按照 ConfigureServices 中服務註冊的順序執行。 主機啟動時,每個 IHostedService 上都會呼叫 StartAsync ,主機正常關閉時,以反向註冊順序呼叫 StopAsync

設定主機

IHostBuilder是供庫和應用初始化、生成和執行主機的主要元件:

public static async Task Main(string[] args)
{
    var host = new HostBuilder()
        .Build();

    await host.RunAsync();
}

選項

HostOptions配置 IHost的選項。

關閉超時值

ShutdownTimeout設定 StopAsync的超時值。 預設值為 5 秒。

Program.Main 中的以下選項配置將預設值為 5 秒的關閉超時值增加至 20 秒:

var host = new HostBuilder()
    .ConfigureServices((hostContext, services) =>
    {
        services.Configure<HostOptions>(option =>
        {
            option.ShutdownTimeout = System.TimeSpan.FromSeconds(20);
        });
    })
    .Build();

預設服務

在主機初始化期間註冊以下服務:

  • 環境 (IHostingEnvironment)
  • HostBuilderContext
  • 配置 (IConfiguration)
  • IApplicationLifetime(ApplicationLifetime)
  • IHostLifetime (ConsoleLifetime)
  • IHost
  • 選項 (AddOptions)
  • 日誌記錄 (AddLogging)

主機配置

主機配置的建立方式如下:

  • 呼叫 IHostBuilder上的擴充套件方法以設定“內容根”和“環境”。
  • ConfigureHostConfiguration 中的配置提供程式讀取配置。

擴充套件方法

應用程式鍵(名稱)

IHostingEnvironment.ApplicationName 屬性是在主機構造期間通過主機配置設定的。 要顯式設定值,請使用 HostDefaults.ApplicationKey

金鑰:applicationName
型別:string
預設:包含應用入口點的程式集的名稱。
設定使用HostBuilderContext.HostingEnvironment.ApplicationName
環境變數<PREFIX_>APPLICATIONNAME<PREFIX_>使用者定義的可選字首

內容根

此設定確定主機從哪裡開始搜尋內容檔案。

:contentRoot
型別:string
預設值:預設為應用程式集所在的資料夾。
設定使用UseContentRoot
環境變數<PREFIX_>CONTENTROOT<PREFIX_>使用者定義的可選字首

如果路徑不存在,主機將無法啟動。

var host = new HostBuilder()
    .UseContentRoot("c:\\<content-root>")

環境

設定應用的環境

:環境
型別:string
預設值:生產
設定使用UseEnvironment
環境變數<PREFIX_>ENVIRONMENT<PREFIX_>使用者定義的可選字首

環境可以設定為任何值。 框架定義的值包括 Development``StagingProduction。 值不區分大小寫。

var host = new HostBuilder()
    .UseEnvironment(EnvironmentName.Development)

ConfigureHostConfiguration

ConfigureHostConfiguration 使用 IConfigurationBuilder來為主機建立 IConfiguration。 主機配置用於初始化 IHostingEnvironment,以供在應用的構建過程中使用。

可多次呼叫 ConfigureHostConfiguration,並得到累計結果。 主機使用上一次在一個給定鍵上設定值的選項。

主機配置自動流向應用配置(ConfigureAppConfiguration和應用的其餘部分)。

預設情況下不包括提供程式。 必須在 ConfigureHostConfiguration 中顯式指定應用所需的任何配置提供程式,包括:

  • 檔案配置(例如,來自 hostsettings.json 檔案)。
  • 環境變數配置。
  • 命令列引數配置。
  • 任何其他所需的配置提供程式。

通過使用 SetBasePath 指定應用的基本路徑,然後呼叫其中一個檔案配置提供程式,可以啟用主機的檔案配置。 示例應用使用 JSON 檔案 hostsettings.json,並呼叫 AddJsonFile來使用檔案的主機配置設定。

要新增主機的環境變數配置,請在主機生成器上呼叫 AddEnvironmentVariablesAddEnvironmentVariables 接受使用者定義的字首(可選)。 示例應用使用字首 PREFIX_。 當系統讀取環境變數時,便會刪除字首。 配置示例應用的主機後,PREFIX_ENVIRONMENT 的環境變數值就變成 environment 金鑰的主機配置值。

在開發過程中,如果使用 Visual Studio 或通過 dotnet run 執行應用,可能會在 Properties/launchSettings.json 檔案中設定環境變數。 若在開發過程中使用 Visual Studio Code,可能會在 .vscode/launch.json 檔案中設定環境變數。 有關更多資訊,請參見在 ASP.NET Core 中使用多個環境

通過呼叫 AddCommandLine 可新增命令列配置。 最後新增命令列配置以允許命令列引數替代之前配置提供程式提供的配置。

hostsettings.json:

{
  "environment": "Development"
}

可以通過applicationNamecontentRoot鍵提供其他配置。

示例 HostBuilder 配置使用 ConfigureHostConfiguration

var host = new HostBuilder()
    .ConfigureHostConfiguration(configHost =>
    {
        configHost.SetBasePath(Directory.GetCurrentDirectory());
        configHost.AddJsonFile("hostsettings.json", optional: true);
        configHost.AddEnvironmentVariables(prefix: "PREFIX_");
        configHost.AddCommandLine(args);
    })

ConfigureAppConfiguration

通過在 IHostBuilder實現上呼叫 ConfigureAppConfiguration建立應用配置。 ConfigureAppConfiguration 使用 IConfigurationBuilder來為應用建立 IConfiguration。 可多次呼叫 ConfigureAppConfiguration,並得到累計結果。 應用使用上一次在一個給定鍵上設定值的選項。 HostBuilderContext.Configuration 中提供 ConfigureAppConfiguration 建立的配置,以供進行後續操作和在 Services 中使用。

應用配置會自動接收 ConfigureHostConfiguration 提供的主機配置。

示例應用配置使用 ConfigureAppConfiguration

var host = new HostBuilder()
    .ConfigureAppConfiguration((hostContext, configApp) =>
    {
        configApp.SetBasePath(Directory.GetCurrentDirectory());
        configApp.AddJsonFile("appsettings.json", optional: true);
        configApp.AddJsonFile(
            $"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", 
            optional: true);
        configApp.AddEnvironmentVariables(prefix: "PREFIX_");
        configApp.AddCommandLine(args);
    })

appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "AllowedHosts": "*"
}

appsettings.Development.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

appsettings.Production.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Error",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

要將設定檔案移動到輸出目錄,請在專案檔案中將設定檔案指定為MSBuild 專案項。 示例應用移動具有以下 <Content> 項的 JSON 應用設定檔案和 hostsettings.json:

<ItemGroup>
  <Content Include="**\*.json" Exclude="bin\**\*;obj\**\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

ConfigureServices

ConfigureServices將服務新增到應用的依賴關係注入容器。 可多次呼叫 ConfigureServices,並得到累計結果。

託管服務是一個類,具有實現 IHostedService介面的後臺任務邏輯。 有關更多資訊,請參見在 ASP.NET Core 中使用託管服務實現後臺任務

示例應用使用 AddHostedService 擴充套件方法嚮應用新增生存期事件 LifetimeEventsHostedService 和定時後臺任務 TimedHostedService 服務:

var host = new HostBuilder()
    .ConfigureServices((hostContext, services) =>
    {
        if (hostContext.HostingEnvironment.IsDevelopment())
        {
            // Development service configuration
        }
        else
        {
            // Non-development service configuration
        }

        services.AddHostedService<LifetimeEventsHostedService>();
        services.AddHostedService<TimedHostedService>();
    })

ConfigureLogging

ConfigureLogging 新增了一個委託來配置提供的ILoggingBuilder。 可以利用相加結果多次呼叫 ConfigureLogging

var host = new HostBuilder()
    .ConfigureLogging((hostContext, configLogging) =>
    {
        configLogging.AddConsole();
        configLogging.AddDebug();
    })

UseConsoleLifetime

UseConsoleLifetime偵聽 Ctrl+C/SIGINT 或 SIGTERM 並呼叫 StopApplication 來啟動關閉程式。 UseConsoleLifetime 解除阻止 RunAsyncWaitForShutdownAsync等擴充套件。ConsoleLifetime預註冊為預設生存期實現。 使用註冊的最後一個生存期。

var host = new HostBuilder()
    .UseConsoleLifetime()

容器配置

為支援插入其他容器中,主機可以接受 IServiceProviderFactory<TContainerBuilder>。 提供工廠不屬於 DI 容器註冊,而是用於建立具體 DI 容器的主機內部函式。 UseServiceProviderFactory(IServiceProviderFactory<TContainerBuilder>) 重寫用於建立應用的服務提供程式的預設工廠。

ConfigureContainer方法託管自定義容器配置。 ConfigureContainer 提供在基礎主機 API 的基礎之上配置容器的強型別體驗。 可以利用相加結果多次呼叫 ConfigureContainer

為應用建立服務容器:

namespace GenericHostSample
{
    internal class ServiceContainer
    {
    }
}

提供服務容器工廠:

using System;
using Microsoft.Extensions.DependencyInjection;

namespace GenericHostSample
{
    internal class ServiceContainerFactory : IServiceProviderFactory<ServiceContainer>
    {
        public ServiceContainer CreateBuilder(IServiceCollection services)
        {
            return new ServiceContainer();
        }

        public IServiceProvider CreateServiceProvider(ServiceContainer containerBuilder)
        {
            throw new NotImplementedException();
        }
    }
}

使用該工廠併為應用配置自定義服務容器:

var host = new HostBuilder()
    .UseServiceProviderFactory<ServiceContainer>(new ServiceContainerFactory())
    .ConfigureContainer<ServiceContainer>((hostContext, container) =>
    {
    })

擴充套件性

IHostBuilder 上使用擴充套件方法實現主機擴充套件性。 以下示例介紹擴充套件方法如何使用 在 ASP.NET Core 中使用託管服務實現後臺任務 中所示的 TimedHostedService示例來擴充套件 IHostBuilder 實現。

var host = new HostBuilder()
    .UseHostedService<TimedHostedService>()
    .Build();

await host.StartAsync();

應用建立 UseHostedService 擴充套件方法,以註冊在 T 中傳遞的託管服務:

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

public static class Extensions
{
    public static IHostBuilder UseHostedService<T>(this IHostBuilder hostBuilder)
        where T : class, IHostedService, IDisposable
    {
        return hostBuilder.ConfigureServices(services =>
            services.AddHostedService<T>());
    }
}

管理主機

IHost實現負責啟動和停止服務容器中註冊的 IHostedService 實現。

執行

Run執行應用並阻止呼叫執行緒,直到關閉主機:

public class Program
{
    public void Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        host.Run();
    }
}

RunAsync

RunAsync執行應用並返回在觸發取消令牌或關閉時完成的 Task

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        await host.RunAsync();
    }
}

RunConsoleAsync

RunConsoleAsync啟用控制檯支援、生成和啟動主機,以及等待 Ctrl+C/SIGINT 或 SIGTERM 關閉。

public class Program
{
    public static async Task Main(string[] args)
    {
        var hostBuilder = new HostBuilder();

        await hostBuilder.RunConsoleAsync();
    }
}

StartStopAsync

Start同步啟動主機。

StopAsync 嘗試在提供的超時時間內停止主機。

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        using (host)
        {
            host.Start();

            await host.StopAsync(TimeSpan.FromSeconds(5));
        }
    }
}

StartAsyncStopAsync

StartAsync啟動應用。

StopAsync停止應用。

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        using (host)
        {
            await host.StartAsync();

            await host.StopAsync();
        }
    }
}

WaitForShutdown

WaitForShutdown通過 IHostLifetime觸發,例如 ConsoleLifetime(偵聽 Ctrl+C/SIGINT 或 SIGTERM)。 WaitForShutdown 呼叫 StopAsync

public class Program
{
    public void Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        using (host)
        {
            host.Start();

            host.WaitForShutdown();
        }
    }
}

WaitForShutdownAsync

WaitForShutdownAsync返回在通過給定的令牌和呼叫 StopAsync來觸發關閉時完成的 Task

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = new HostBuilder()
            .Build();

        using (host)
        {
            await host.StartAsync();

            await host.WaitForShutdownAsync();
        }

    }
}

外部控制元件

使用可從外部呼叫的方法,能夠實現主機的外部控制元件:

public class Program
{
    private IHost _host;

    public Program()
    {
        _host = new HostBuilder()
            .Build();
    }

    public async Task StartAsync()
    {
        _host.StartAsync();
    }

    public async Task StopAsync()
    {
        using (_host)
        {
            await _host.StopAsync(TimeSpan.FromSeconds(5));
        }
    }
}

StartAsync 開始時呼叫 WaitForStartAsync,在繼續之前,會一直等待該操作完成。 它可用於延遲啟動,直到外部事件發出訊號。

IHostingEnvironment 介面

IHostingEnvironment提供有關應用託管環境的資訊。 使用建構函式注入獲取 IHostingEnvironment 以使用其屬性和擴充套件方法:

public class MyClass
{
    private readonly IHostingEnvironment _env;

    public MyClass(IHostingEnvironment env)
    {
        _env = env;
    }

    public void DoSomething()
    {
        var environmentName = _env.EnvironmentName;
    }
}

有關更多資訊,請參見在 ASP.NET Core 中使用多個環境

IApplicationLifetime 介面

IApplicationLifetime允許啟動後和關閉活動,包括正常關閉請求。 介面上的三個屬性是用於註冊 Action 方法(用於定義啟動和關閉事件)的取消標記。

取消標記 觸發條件
ApplicationStarted 主機已完全啟動。
ApplicationStopped 主機正在完成正常關閉。 應處理所有請求。 關閉受到阻止,直到完成此事件。
ApplicationStopping 主機正在執行正常關閉。 仍在處理請求。 關閉受到阻止,直到完成此事件。

建構函式將 IApplicationLifetime 服務注入到任何類中。 示例應用將建構函式注入到 LifetimeEventsHostedService 類(一個 IHostedService 實現)中,用於註冊事件。

LifetimeEventsHostedService.cs:

internal class LifetimeEventsHostedService : IHostedService
{
    private readonly ILogger _logger;
    private readonly IApplicationLifetime _appLifetime;

    public LifetimeEventsHostedService(
        ILogger<LifetimeEventsHostedService> logger, IApplicationLifetime appLifetime)
    {
        _logger = logger;
        _appLifetime = appLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _appLifetime.ApplicationStarted.Register(OnStarted);
        _appLifetime.ApplicationStopping.Register(OnStopping);
        _appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("OnStarted has been called.");

        // Perform post-startup activities here
    }

    private void OnStopping()
    {
        _logger.LogInformation("OnStopping has been called.");

        // Perform on-stopping activities here
    }

    private void OnStopped()
    {
        _logger.LogInformation("OnStopped has been called.");

        // Perform post-stopped activities here
    }
}

StopApplication請求終止應用。 以下類在呼叫類的 Shutdown 方法時使用 StopApplication 正常關閉應用:

public class MyClass
{
    private readonly IApplicationLifetime _appLifetime;

    public MyClass(IApplicationLifetime appLifetime)
    {
        _appLifetime = appLifetime;
    }

    public void Shutdown()
    {
        _appLifetime.StopApplication();
    }
}

相關文章