ASP.NET Core 托管和部署

ASP.NET Core 托管和部署

翻译自 https://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 地址或者主机地址,服务器应该在这些地址上监听请求。例如,http://localhost:123。使用 "*" 来表明服务器应该在使用特定端口和协议(例如,http://*:5000)的任意 IP 地址或者主机名称上监听。协议(http:// 或者 https://)必须包含在每一个 URL 中。支持的格式因服务器而异。

Key: urls

Type: string

Default: http://localhost:5000 和 https://localhost:5001

Environment variable: <PREFIX_>URLS

使用环境变量或者调用 UseUrls 设置这个值:

webBuilder.UseUrls("http://*:5000;http://localhost:5001;https://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));
        }
    }
}
01-25 00:01