­

翻譯 – ASP.NET Core 基本知識 – 通用主機 (Generic Host)

翻譯自 //docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-5.0

ASP.NET Core 模板創建了一個 .NET Core 通用主機 (Generic Host HostBuilder)。

本話題提供了關於在 ASP.NET Core 中使用 .NET 通用主機。關於在控制台中使用 .NET Generic Host 的資訊,查看 .NET Generic Host

主機定義

主機是指封裝了應用程式資源的對象,例如:

  • 依賴注入(DI)
  • 日誌
  • 配置
  • IHostedService 實現

當一個主機啟動的時候,它會調用 IHostedService.StartAsync 在每一個在服務容器中託管服務集合中註冊的 IHostedService 的實現。 在 Web 應用程式中,一個 IHostedService 的實現是一個啟動 HTTP server implementation 的 web 服務。

包含所有的應用程式相互依賴的資源在一個對象中的主要原因是聲明周期管理:控制應用程式啟動和優雅的關閉。

設置一個主機

主機一般在 Program 類中通過程式碼配置,創建,運行。Main 方法:

  • 調用 CreateHostBuilder 方法創建和配置一個 builder 對象
  • 在 builder 對象上調用 Build 和 Run 方法

ASP.NET Core web 模板生成下列程式碼創建一個主機:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

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

下面的程式碼創建一個 non-HTTP 工作負載,將一個 IHostedService 實現添加到 DI 容器中。

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.AddHostedService<Worker>();
            });
}

對於 HTTP 負載來說,Main 方法是相同的,但是 CreateHostBuilder 調用了 ConfigureWebHostDefaults:

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

如果應用程式使用了 Entity Framework Core,不要改變 CreateHostBuiler 方法的名稱和簽名。Entity Framework Core tools 期望發現一個 CreateHostBuilder 方法,不用運行應用程式就可以配置主機(Host)。更多資訊,查看 Design-time DbContext Creation

默認 builder 設置

CreateDefaultBuilder 方法:

  • 設置內容根目錄(content root)為 GetCurrentDirectory 返回的路徑
  • 從以下地方載入主機配置:
    前綴為 DOTNET_  的環境變數
    命令行參數
  • 從以下地方載入應用程式配置:
    appsettings.json
    appsettings.{Environment}.json
    當應用程式運行在 Development 環境時使用 User secrets
    環境變數
    命令行參數
  • 添加以下日誌(logging)providers:
    控制台
    調試
    EventSource
    EventLog (只有運行在 Windows 系統時)
  • 當環境是 Development 時使能 scope validation 和 dependency validation

ConfigureWebHostDefaults 方法:

文章中下面的 Settings for all app types 和 Settings for web apps 部分展示了如果覆蓋默認 builder 設置。

Framework-provided services

下面的服務會自動註冊:

更多關於 framework-provided services 的資訊,查看 Dependency injection in ASP.NET Core

IHostApplicationLifetime

注入  IHostApplicationLifetime (之前叫做 IApplicationLifetime) 服務到任一類中處理 post-startup 和 優雅關閉的任務。介面中的三個屬性是用來註冊應用程式啟動和停止時間處理方法的取消令牌。介面還包括一個 StopApplication 方法。

下面的示例是介面 IHostedService 的一個實現,註冊 IHostApplicationLifetime 事件:

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

    public LifetimeEventsHostedService(
        ILogger<LifetimeEventsHostedService> logger, 
        IHostApplicationLifetime 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
    }
}

 IHostLifetime

IHostLifetime 實現控制主機什麼時候啟動和什麼時候停止。最後一個註冊的實現會被使用。

Microsoft.Extensions.Hosting.Internal.ConsoleLifetime 是 IHostLifetime 的默認實現。

ConsoleLifetime:

IHostEnvironment

注入服務 IHostEnvironment 到類中獲取關於下列設置的資訊:

Web 應用程式實現了 IWebHostEnvironment 介面,它繼承了 IHostEnvironment 並添加了 WebRootPath

Host 配置

Host 配置用於 IHostEnvironment 實現的屬性。

Host 配置使用 ConfigureAppConfiguration 中的 HostBuilderContext.Configuration。在 ConfigureAppConfiguration 之後,使用應用程式配置替換 HostBuilderContext.Configuration。

為了添加主機配置,調用介面 IHostBuilder 上的 ConfigureHostConfiguration 方法。ConfigureHostConfiguration 可以被多次調用生成累積的結果。主機使用在給定鍵值最後設置值的選項。

前綴為 DOTNET_ 的環境變數和命令行參數包含在 CreateDefaultBuilder 中。對於 web 應用程式,前綴為 ASPNETCORE_ 的環境變數被添加。當環境變數被讀取的時候,前綴會被移除。例如,環境變數 ASPNETCORE_ENVIRONMENT 的值會成為主機配置中 environment 鍵的值。

下面的示例創建了主機配置:

// using Microsoft.Extensions.Configuration;

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

App 配置

App 配置通過調用 IHostBuilder 介面的 ConfigureAppConfiguration 方法創建。ConfigureAppConfiguration 可以多次調用生成累積的結果。App 使用在給定鍵上最後一個設置值得選項。

創建配置的 ConfigureAppConfiguration 在 HostBuilderContext.Configuration 中,用於後續操作,它作為一個服務從依賴注入 (DI) 中獲取。主機配置也會被添加到 App 配置中。

更多資訊查看 Configuration in ASP.NET Core

所有應用程式類型的配置

這部分列出的主機配置會被應用到 HTTP 和 non-HTTP 工作負載中。默認的用來配置這些設置的環境變數可以有一個 DOTNET_  或者 ASPNETCORE_ 的前綴。更多資訊查看 Default builder settings 部分。

 

ApplicationName

IHostEnvironment.ApplicationName 屬性在主機構造方法中通過 host 配置設置。

Key: applicationName

Type: string

Default: 包含應用程式入口點的程式集的名稱

Environment variable: <PREFIX_>APPLICATIONNAME

ContentRoot

IHostEnvironment.ContentRootPath 屬性決定主機從哪裡開始搜索內容文件。如果路徑不存在,主機啟動失敗。

Key: contentRoot

Type: string

Default: 應用程式程式集所在的目錄

Environment variable: <PREFIX_>CONTENTROOT

為了設置這個值,使用環境變數或者調用 IHostBuilder 介面的 UseContentRoot 方法:

Host.CreateDefaultBuilder(args)
    .UseContentRoot("c:\\content-root")
    //...

更多資訊查看:

EnvironmentName

IHostEnvironment.EnvironmentName 屬性可以被設置為任意值。框架本身定義的值包括 Development,Staging,和 Production。值不區分大小寫。

Key: environment

Type: string

Default: Production

Environment variable: <PREFIX_>ENVIRONMENT

使用 IHostBuilder 介面的 UseEnvironment 方法設置這個值:

Host.CreateDefaultBuilder(args)
    .UseEnvironment("Development")
    //...

ShutdownTimeout

HostOptions.ShutdownTimeout 為 StopAsync 設置超時時間。默認值是 5 秒。在超時時間內,主機會:

如果超時時間在所有託管服務停止之前過期了,任何任然活動的服務都會在應用程式關閉的時候停止。即使它們沒有完成處理,服務也會停止。如果服務需要更多的時間去停止,請增加超時時間。

Key: shutdownTimeoutSeconds

Type: int

Default: 5 second

Environment variable: <PREFIX_>SHUTDOWNTIMEOUTSECONDS

使用環境變數或者配置 HostOptions 設置這個值。下面的例子設置了超時時間為 20 秒:

Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.Configure<HostOptions>(option =>
        {
            option.ShutdownTimeout = System.TimeSpan.FromSeconds(20);
        });
    });

禁用配置改變時重新載入應用程式配置

默認的,appsetting.json 和 appsetting.{Environment}.json 文件改變時會被重新載入。在 ASP.NET Core 5.0 或者更新的版本中可以設置 hostBuilder:reloadConfigOnChange 鍵值為 false 來禁用重新載入的行為。

Key: hostBuilder:reloadCofigOnChange

Type: bool(true or 1)

Default: true

Command-line argument: hostBuilder:reloadConfigOnChange

Environment variable: <PREFIX_>hostBuilder:reloadConfigureOnChange

⚠️   警告

冒號 (:) 分隔符在所有平台分層鍵值上的環境變數是不工作的。更多資訊查看 Environment variables

Web 應用程式設置

一些主機設置會應用到 HTTP 工作負載中。默認的,用來配置這些設置的環境變數會有 DOTNET_  或者 ASPNETCORE_ 前綴。

可以使用 IWebHostBuilder 的擴展方法設置這些配置。例如下面的示例,程式碼展示了如果調用擴展方法,假設webBuilder 是 IWebHostBuilder 的實例對象:

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

CaptureStartupErrors

當設置為 false 的時候,在啟動過程中出現的錯誤會導致主機退出。當設置為 true 的時候,主機會在啟動過程中捕獲異常並且試圖啟動伺服器。

Key: captureStartupErrors

Type: bool (true 或者 1)

Default: 默認是 false,除非應用程式使用 Kestrle 運行在 IIS 之後,這時默認值是 true

Environment variable: <PREFIX_>CAPTURESTARTUPERRORS

使用配置或者調用 CaptureStartupErros 設置該值:

webBuilder.CaptureStartupErrors(true);

DetailedErrors

當使能的時候,或者 environment 環境變數是 Development 的時候,應用程式會捕捉詳細錯誤資訊。

Key: detailedErrors

Type: bool (true 或者 1)

Default: false

Environment variable: <PREFIXE_>_DETAILEDERRORS

使用配置或者調用 UseSetting 設置該值:

webBuilder.UseSetting(WebHostDefaults.DetailedErrorsKey, "true");

HostingStartupAssemblies

是一個啟動時載入的託管的啟動程式集的以逗號分隔的字元串。儘管默認配置值是一個空的字元串,託管啟動程式集總是包含應用程式的程式集。當託管啟動程式集被提供,它們在應用程式啟動時創建公共服務的過程中會被添加到應用程式的程式集中載入。

Key: hostingStartupAssemblies

Type: string

Default: Empty string

Environment variable: <PREFIX_>_HOSTINGSTARTUPASSEMBLIES

使用配置或者調用 UseSetting 設置該值:

webBuilder.UseSetting(WebHostDefaults.HostingStartupAssembliesKey, "assembly1;assembly2");

HostingStartupExcludeAssemblies

啟動時排除的託管啟動程式集,以逗號分隔的字元串

Key: hostingStartupExcludeAssemblies

Type: string

Default: Empty string

Environment variable: <PREFIX_>_HOSTINGSTARTUPEXCLUDEASSEMBLIES

使用配置或者調用 UseSetting 設置該值:

webBuilder.UseSetting(WebHostDefaults.HostingStartupExcludeAssembliesKey, "assembly1;assembly2");

HTTPS_Port

HTTPS 重定向埠。在 enforcing HTTPS 中使用。

Key: https_port

Type: string

Default: A default value isn’t set

Environment variable: <PREFIX_>HTTPS_PORT

使用配置或者調用 UseSetting 設置該值:

webBuilder.UseSetting("https_port", "8080");

PreferHostingUrls

標識主機是否應該監聽 IWebHostBuilder 配置的 URLs 而不是使用 IServer 的實現配置的 URLs。

Key: preferHostingUrls

Type: bool (true 或者 1)

Default: true

Environment varibale: <PREFIX_>PREFERHOSTINGURLS

使用環境變數或者調用 PreferHostingUrls 設置該值:

webBuilder.PreferHostingUrls(false);

PreventHostingStartup

阻止託管啟動程式集的自動載入,包含應用程式程式集配置的託管啟動程式集。更多資訊查看 Use hosting startup assemblies in ASP.NET Core

Key: preventHostingStartup

Type:bool (true 或者 1)

Default: false

Environment variable: <PREFIX_>PREVENTHOSTINGSTARTUP

使用環境變數或者調用 UseSetting 設置這個值:

webBuilder.UseSetting(WebHostDefaults.PreventHostingStartupKey, "true");

StartupAssembly

用來查詢 Startup 類的程式集。

Key: startupAssembly

Type: string

Default: 應用程式程式集

Environment variable: <PREFIX_>STARTUPASSEMBLY

使用環境變數或者調用 UseStartup 設置這個值。UseStartup 可以接收一個程式集的名稱(string)或者一個類型 (TStartup)。如果多個 UseStartup 方法被調用,最後一個優先順序最高:

webBuilder.UseStartup("StartupAssemblyName");
webBuilder.UseStartup<Startup>();

URLs

一組逗號分隔的帶有埠和協議的 IP 地址或者主機地址,伺服器應該在這些地址上監聽請求。例如,//localhost:123。使用 “*” 來表明伺服器應該在使用特定埠和協議(例如,//*:5000)的任意 IP 地址或者主機名稱上監聽。協議(// 或者 //)必須包含在每一個 URL 中。支援的格式因伺服器而異。

Key: urls

Type: string

Default: //localhost:5000 和 //localhost:5001

Environment variable: <PREFIX_>URLS

使用環境變數或者調用 UseUrls 設置這個值:

webBuilder.UseUrls("//*:5000;//localhost:5001;//hostname:5002");

Kestrel 有它自己的 enpoint 配置 API。更多資訊查看 Configure endpoints for the ASP.NET Core Kestrel web server

WebRoot

IWebHostEnvironment.WebRootPath 屬性決定了應用程式靜態資源的相對路徑。如果路徑不存在,一個 no-op 的文件被使用。

Key: webroot

Type: string

Default: 默認是 wwwroot。路徑{content root}/wwwroot 必須存在

Environment variable: <PREFIX_>WEBROOT

使用環境變數或者 IWebHostBuilder 介面的 UseWebRoot 方法設置這個值:

webBuilder.UseWebRoot("public");

更多資訊查看:

管理主機生命周期

調用 IHost 的實現方法啟動和停止應用程式。這些方法會影響所有註冊在服務容器中的 IHostedService 的實現。

Run

Run 運行應用程式,阻塞調用執行緒直到主機關閉。

RunAsync

RunAsync 運行應用程式,當一個取消令牌或者關閉被觸發的時候返回一個完成 Task

RunConsoleAsync

RunConsoleAsync 使能控制台支援,創建,啟動主機,等待 Ctrl + C / SIGINT 或者 SIGTERM 關閉。

Start

Start 以同步方式啟動主機

StartAsync

StartAsync 啟動主機,當取消令牌或者關閉被觸發了,會返回一個完成的 Task

WaitForStartAsync 在 StartAsync 的開始會被調用,繼續運行之前會一直等待它完成。這可以用來延遲啟動,直到接收到外部事件的通知。

StopAsync

StopAsync 嘗試使用提供的超時時間停止主機

WaitForShutdown

WaitForShutdown 阻塞調用執行緒直到生命周期 IHostLifttime 的關閉被觸發,例如通過 Ctrl + C / SIGINT  或者 SIGTERM。

WaitForShutdownAsync

WaitForShutdownAsync 在通過給定的令牌或者調用 StopAsync 觸發關閉的時候回返回一個完成的 Task

External control

可以通過使用可以在外部調用的方法獲取主機聲明周期的直接控制權:

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));
        }
    }
}