Windows服务托管

在 Windows 服务中托管 ASP.NET Core

作者:Luke Latham

不使用 IIS 时,可以在 Windows 上将 ASP.NET Core 应用作为 Windows 服务进行托管。 作为 Windows 服务进行托管时,应用将在服务器重新启动后自动启动。

查看或下载示例代码如何下载

先决条件

辅助角色服务模板

ASP.NET Core 辅助角色服务模板可作为编写长期服务应用的起点。 要使用该模板作为编写 Windows 服务应用的基础:

  1. 从 .NET Core 模板创建辅助角色服务应用。
  2. 按照应用配置部分中的指导来更新辅助角色服务应用,以便它可以作为 Windows 服务运行。

应用配置

应用需要 Microsoft.AspNetCore.Hosting.WindowsServices 的包引用。

生成主机时会调用 IHostBuilder.UseWindowsService 若应用作为 Windows 服务运行,方法为:

  • 将主机生存期设置为 WindowsServiceLifetime
  • 内容根设置为 AppContext.BaseDirectory 有关详细信息,请参阅当前目录和内容根部分。
  • 为事件日志启用日志记录:
    • 应用名称用作默认源名称。
    • 对于基于 ASP.NET Core 模板且调用 CreateDefaultBuilder 生成主机的应用,默认日志级别为“警告” 或更高级别。
    • 在 appsettings.json /appsettings.{Environment}.json 或其他配置提供程序中,使用 Logging:EventLog:LogLevel:Default 键覆盖默认日志级别。
    • 只有管理员可以创建新的事件源。 无法使用应用程序名称创建事件源时,应用程序源将记录一条警告,并禁用事件源。

在 Program.cs 的 CreateHostBuilder 中 :

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    ...

本主题附带了以下示例应用:

  • 后台辅助角色服务示例 – 一个基于辅助角色服务模板的非 Web 应用示例,该模板使用托管服务执行后台任务。
  • Web 应用服务示例 – 一个作为 Windows 服务运行的 Razor Pages Web 应用示例,通过托管服务执行后台任务。

有关 MVC 指南,请参阅 ASP.NET Core MVC 概述从 ASP.NET Core 2.2 迁移到3.0 下的文章。

部署类型

有关部署方案的信息和建议,请参阅 .NET Core 应用程序部署

SDK

对于使用 Razor Pages 或 MVC 框架的基于 Web 应用的服务,请在项目文件中指定 Web SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">

如果服务仅执行后台任务(例如 托管服务),请在项目文件中指定辅助角色 SDK:

<Project Sdk="Microsoft.NET.Sdk.Worker">

依赖框架的部署 (FDD)

依赖框架的部署 (FDD) 依赖目标系统上存在共享系统级版本的 .NET Core。 按照本文中的指南采用 FDD 方案时,SDK 会生成一个称为“依赖框架的可执行文件”的可执行文件 (.exe)。

如果使用 Web SDK,则 web.config 文件(通常在发布 ASP.NET Core 应用时生成)不是 Windows 服务应用的必要文件 。 若要禁止创建 web.config 文件,请将 <IsTransformWebConfigDisabled> 属性集添加到 true

<PropertyGroup>
  <TargetFramework>netcoreapp3.0</TargetFramework>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

独立部署 (SCD)

独立部署 (SCD) 不依赖主机系统上存在的共享框架。 运行时和应用的依赖项将与应用一起部署。

将 Windows 运行时标识符 (RID) 添加到包含目标框架的 <PropertyGroup> 中:

<RuntimeIdentifier>win7-x64</RuntimeIdentifier>

要发布多个 RID:

  • 通过以分号分隔的列表提供 RID。
  • 使用属性名称 <RuntimeIdentifiers>(复数)。

有关详细信息,请参阅 .NET Core RID 目录

服务用户帐户

在管理 PowerShell 6 命令行界面中运行 New-LocalUser cmdlet,为服务创建用户帐户。

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)或更高版本:

New-LocalUser -Name {SERVICE NAME}

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)之前的 Windows 操作系统:

powershell -Command "New-LocalUser -Name {SERVICE NAME}"

在系统提示时,提供强密码

除非将 -AccountExpires 参数提供给到期时间为 DateTimeNew-LocalUser cmdlet,否则用户帐户不会到期。

有关详细信息,请参阅 Microsoft.PowerShell.LocalAccounts服务用户帐户

使用 Active Directory 时管理用户的另一种方法是使用托管服务帐户。 有关详细信息,请参阅组托管服务帐户概述

以服务身份登录权限

为服务用户帐户创建“以服务身份登录”权限:

  1. 通过运行 secpool.msc ,打开本地安全策略编辑器。
  2. 展开“本地策略”节点,选择“用户权限分配”。
  3. 打开“以服务身份登录”策略。
  4. 选择“添加用户或组” 。
  5. 使用下列方法之一提供对象名称(用户帐户):
    1. 在对象名称字段键入用户帐户 ({DOMAIN OR COMPUTER NAME\USER}),然后选择“确定”,以将此用户添加到策略。
    2. 选择“高级” 。 选择“开始查找”。 从列表中选择该用户帐户。 选择“确定” 。 再次选择“确定”,以将该用户添加到策略。
  6. 选择“确定”或“应用”,以接受更改。

创建和管理 Windows 服务

创建服务

使用 PowerShell 注册服务。 从 PowerShell 6 命令行界面,执行以下命令:

$acl = Get-Acl "{EXE PATH}"
$aclRuleArgs = {DOMAIN OR COMPUTER NAME\USER}, "Read,Write,ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($aclRuleArgs)
$acl.SetAccessRule($accessRule)
$acl | Set-Acl "{EXE PATH}"

New-Service -Name {SERVICE NAME} -BinaryPathName {EXE FILE PATH} -Credential {DOMAIN OR COMPUTER NAME\USER} -Description "{DESCRIPTION}" -DisplayName "{DISPLAY NAME}" -StartupType Automatic
  • {EXE PATH} – 应用在主机上的文件夹的路径(如 d:\myservice)。 请勿在此路径中包含应用的可执行文件。 尾部反斜杠是非必需项。
  • {DOMAIN OR COMPUTER NAME\USER} – 服务用户帐户(如 Contoso\ServiceUser)。
  • {SERVICE NAME} – 服务名称(如 MyService)。
  • {EXE FILE PATH} – 应用的可执行文件路径(如 d:\myservice\myservice.exe)。 请将可执行文件的文件名和扩展名包括在内。
  • {DESCRIPTION} – 服务说明(如 My sample service)。
  • {DISPLAY NAME} – 服务显示名称(如 My Service)。

启动服务

使用以下 PowerShell 6 命令启动服务:

Start-Service -Name {SERVICE NAME}

此命令需要几秒钟才能启动服务。

确信服务状态

要检查服务的状态,请使用以下 PowerShell 6 命令:

Get-Service -Name {SERVICE NAME}

状态报告为以下值之一:

  • Starting
  • Running
  • Stopping
  • Stopped

停止服务

使用以下 Powershell 6 命令停止服务:

Stop-Service -Name {SERVICE NAME}

删除服务

在停止服务一小段时间后,使用以下 Powershell 6 命令删除服务:

Remove-Service -Name {SERVICE NAME}

代理服务器和负载均衡器方案

与来自 Internet 或公司网络的请求进行交互且在代理或负载均衡器后方的服务可能需要其他配置。 有关详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

配置终结点

默认情况下,ASP.NET Core 绑定到 http://localhost:5000 通过设置 ASPNETCORE_URLS 环境变量来配置 URL 和端口。

有关其他 URL 和端口配置方法,请参阅相关服务器文章:

上述指南介绍了对 HTTPS 终结点的支持。 例如,在使用 Windows 服务进行身份验证时,为 HTTPS 配置应用。

备注

不支持使用 ASP.NET Core HTTPS 开发证书保护服务终结点。

当前目录和内容根

通过为 Windows 服务调用 GetCurrentDirectory 返回的当前工作目录是 C:\WINDOWS\system32 文件夹。 system32 文件夹不是存储服务文件(如设置文件)的合适位置 。 使用以下方法之一来维护和访问服务的资产和设置文件。

使用 ContentRootPath 或 ContentRootFileProvider

使用 IHostEnvironment.ContentRootPathContentRootFileProvider 查找应用的资源。

应用作为服务运行时,UseWindowsServiceContentRootPath 设置为 AppContext.BaseDirectory

通过调用 CreateDefaultBuilder during host construction,从应用的内容根加载应用的默认设置文件 appsettings.json 和 appsettings.{Environment}.json。

对于 ConfigureAppConfiguration 中的开发人员代码加载的其他设置文件,无需调用 SetBasePath 在下面的示例中,custom_settings.json 文件位于应用的内容根,加载它时未显式设置基本路径:

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.AddJsonFile("custom_settings.json");
            })
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
            });
}

请勿尝试使用 GetCurrentDirectory 来获取资源路径,因为 Windows 服务应用会返回“C:\WINDOWS\system32”文件夹作为其当前目录。

将服务的文件存储在磁盘中的合适位置

使用 IConfigurationBuilder 时,使用 SetBasePath 指定到包含文件的文件夹的绝对路径。

疑难解答

若要排除 Windows 服务应用的故障,请参阅 ASP.NET Core 项目疑难解答和调试

常见错误

  • 正在使用 PowerShell 的早期版本或预发布版本。
  • 注册的服务未使用来自 dotnet publish 命令的应用的已发布输出。 应用部署不支持 dotnet build 命令的输出。 已发布的资产位于以下文件夹之一中,具体取决于部署类型:
    • bin/Release/{TARGET FRAMEWORK}/publish (FDD)
    • bin/Release/{TARGET FRAMEWORK}/{RUNTIME IDENTIFIER}/publish (SCD)
  • 服务未处于“正在运行”状态。
  • 应用使用的资源(例如证书)的路径不正确。 Windows 服务的基本路径是“c:\Windows\System32” 。
  • 用户没有“以服务身份登录”权限。
  • 在执行 New-Service PowerShell 命令时,用户密码已过期,或以不正确的方式传递。
  • 应用需要进行 ASP.NET Core 身份验证,但是未配置安全连接 (HTTPS)。
  • 请求 URL 端口不正确或未在应用中正确配置。

系统和应用程序事件日志

访问系统和应用程序事件日志:

  1. 打开“开始”菜单,搜索“事件查看器” ,然后选择“事件查看器” 应用。
  2. 在“事件查看器” 中,打开“Windows 日志” 节点。
  3. 选择“系统”,以打开系统事件日志。 选择“应用程序” 以打开应用程序事件日志。
  4. 搜索与失败应用相关联的错误。

在命令提示符处运行应用

许多启动错误未在事件日志中生成有用信息。 可以通过在托管系统上在命令提示符处运行应用来找到某些错误的原因。 若要记录应用中的其他详细信息,则降低日志级别,或在开发环境中运行此应用。

清除包缓存

正常运行的应用在开发计算机上升级 .NET Core SDK 或在应用内更改包版本后可能会立即出现故障。 在某些情况下,不同的包可能在执行主要升级时中断应用。 可以按照以下说明来修复其中大部分问题:

  1. 删除 bin 和 obj 文件夹。

  2. 通过从命令行界面执行 dotnet nuget locals all --clear 清除包缓存。

    清除包缓存还可通过使用 nuget.exe 工具并执行命令 nuget locals all -clear 来完成。 nuget.exe 不是与 Windows 桌面操作系统的捆绑安装,必须从 NuGet 网站中单独获取。

  3. 还原并重新生成项目。

  4. 在重新部署应用前,在服务器上删除部署文件夹中的所有文件。

应用缓慢或挂起

故障转储是系统内存的一个快照,可帮助确定应用崩溃、启动故障或应用速度缓慢等状况的原因 。

应用崩溃或引发异常

Windows 错误报告 (WER) 中获取转储并进行分析:

  1. 创建文件夹,将崩溃转储文件保存在 c:\dumps

  2. 使用应用程序可执行文件名称运行 EnableDumps PowerShell 脚本

    .\EnableDumps {APPLICATION EXE} c:\dumps
    
  3. 在造成崩溃的条件下运行应用。

  4. 出现崩溃后,运行 DisableDumps PowerShell 脚本

    .\DisableDumps {APPLICATION EXE}
    

在应用崩溃并完成转储收集后,即可正常终止应用。 PowerShell 脚本会 WER 来按应用收集转储(最多收集 5 个)。

警告

崩溃转储可能会占用大量磁盘空间(每个最多占用数 GB)。

应用挂起、在启动期间失败或正常运行

如果应用挂起(停止响应但不崩溃)、在启动期间失败或者正常运行hangs,请参阅用户模式转储文件:选择最佳工具,以选择适合用于生成转储的工具。

分析转储

可采用几种方法来分析转储。 有关详细信息,请参阅分析用户模式转储文件

其他资源

不使用 IIS 时,可以在 Windows 上将 ASP.NET Core 应用作为 Windows 服务进行托管。 作为 Windows 服务进行托管时,应用将在服务器重新启动后自动启动。

查看或下载示例代码如何下载

先决条件

应用配置

应用需要引用 Microsoft.AspNetCore.Hosting.WindowsServicesMicrosoft.Extensions.Logging.EventLog 包。

在服务之外运行时,若要进行测试和调试,请添加代码以确定应用是否作为服务或控制台应用运行。 检查是否已连接调试器或是否存在 --console 开关。 如果其中一个条件为 true(应用不作为服务运行),请调用 Run 如果条件为 false(应用作为服务运行):

由于命令行配置提供程序需要命令行参数的名称/值对,因此将先从参数中删除 --console 开关,然后 CreateDefaultBuilder 会接收这些参数。

若要写入 Windows 事件日志,请将事件日志提供程序添加到 ConfigureLogging 使用 appsettings.Production.json 文件中的 Logging:LogLevel:Default 键设置日志记录级别。

示例应用中的以下示例调用了 RunAsCustomService,来代替 RunAsService,以处理应用内的生存期事件。 有关详细信息,请参阅处理启动和停止事件部分。

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>();
}

部署类型

有关部署方案的信息和建议,请参阅 .NET Core 应用程序部署

SDK

对于使用 Razor Pages 或 MVC 框架的基于 Web 应用的服务,请在项目文件中指定 Web SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">

如果服务仅执行后台任务(例如 托管服务),请在项目文件中指定辅助角色 SDK:

<Project Sdk="Microsoft.NET.Sdk.Worker">

依赖框架的部署 (FDD)

依赖框架的部署 (FDD) 依赖目标系统上存在共享系统级版本的 .NET Core。 按照本文中的指南采用 FDD 方案时,SDK 会生成一个称为“依赖框架的可执行文件”的可执行文件 (.exe)。

Windows 运行时标识符 (RID) (<RuntimeIdentifier>) 包含目标框架。 在以下示例中,将 RID 设置为 win7-x64 <SelfContained> 属性设置为 false 这些属性指示 SDK 生成用于 Windows 的可执行文件 (.exe ) 以及一个依赖共享 NET Core 框架的应用。

web.config 文件(通常在发布 ASP.NET Core 应用时生成)对于 Windows 服务应用来说是不必要的。 若要禁止创建 web.config 文件,请将 <IsTransformWebConfigDisabled> 属性集添加到 true

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <SelfContained>false</SelfContained>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

独立部署 (SCD)

独立部署 (SCD) 不依赖主机系统上存在的共享框架。 运行时和应用的依赖项将与应用一起部署。

将 Windows 运行时标识符 (RID) 添加到包含目标框架的 <PropertyGroup> 中:

<RuntimeIdentifier>win7-x64</RuntimeIdentifier>

要发布多个 RID:

  • 通过以分号分隔的列表提供 RID。
  • 使用属性名称 <RuntimeIdentifiers>(复数)。

有关详细信息,请参阅 .NET Core RID 目录

<SelfContained> 属性设置为 true

<SelfContained>true</SelfContained>

服务用户帐户

在管理 PowerShell 6 命令行界面中运行 New-LocalUser cmdlet,为服务创建用户帐户。

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)或更高版本:

New-LocalUser -Name {SERVICE NAME}

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)之前的 Windows 操作系统:

powershell -Command "New-LocalUser -Name {SERVICE NAME}"

在系统提示时,提供强密码

除非将 -AccountExpires 参数提供给到期时间为 DateTimeNew-LocalUser cmdlet,否则用户帐户不会到期。

有关详细信息,请参阅 Microsoft.PowerShell.LocalAccounts服务用户帐户

使用 Active Directory 时管理用户的另一种方法是使用托管服务帐户。 有关详细信息,请参阅组托管服务帐户概述

以服务身份登录权限

为服务用户帐户创建“以服务身份登录”权限:

  1. 通过运行 secpool.msc ,打开本地安全策略编辑器。
  2. 展开“本地策略”节点,选择“用户权限分配”。
  3. 打开“以服务身份登录”策略。
  4. 选择“添加用户或组” 。
  5. 使用下列方法之一提供对象名称(用户帐户):
    1. 在对象名称字段键入用户帐户 ({DOMAIN OR COMPUTER NAME\USER}),然后选择“确定”,以将此用户添加到策略。
    2. 选择“高级” 。 选择“开始查找”。 从列表中选择该用户帐户。 选择“确定” 。 再次选择“确定”,以将该用户添加到策略。
  6. 选择“确定”或“应用”,以接受更改。

创建和管理 Windows 服务

创建服务

使用 PowerShell 注册服务。 从 PowerShell 6 命令行界面,执行以下命令:

$acl = Get-Acl "{EXE PATH}"
$aclRuleArgs = {DOMAIN OR COMPUTER NAME\USER}, "Read,Write,ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($aclRuleArgs)
$acl.SetAccessRule($accessRule)
$acl | Set-Acl "{EXE PATH}"

New-Service -Name {SERVICE NAME} -BinaryPathName {EXE FILE PATH} -Credential {DOMAIN OR COMPUTER NAME\USER} -Description "{DESCRIPTION}" -DisplayName "{DISPLAY NAME}" -StartupType Automatic
  • {EXE PATH} – 应用在主机上的文件夹的路径(如 d:\myservice)。 请勿在此路径中包含应用的可执行文件。 尾部反斜杠是非必需项。
  • {DOMAIN OR COMPUTER NAME\USER} – 服务用户帐户(如 Contoso\ServiceUser)。
  • {SERVICE NAME} – 服务名称(如 MyService)。
  • {EXE FILE PATH} – 应用的可执行文件路径(如 d:\myservice\myservice.exe)。 请将可执行文件的文件名和扩展名包括在内。
  • {DESCRIPTION} – 服务说明(如 My sample service)。
  • {DISPLAY NAME} – 服务显示名称(如 My Service)。

启动服务

使用以下 PowerShell 6 命令启动服务:

Start-Service -Name {SERVICE NAME}

此命令需要几秒钟才能启动服务。

确信服务状态

要检查服务的状态,请使用以下 PowerShell 6 命令:

Get-Service -Name {SERVICE NAME}

状态报告为以下值之一:

  • Starting
  • Running
  • Stopping
  • Stopped

停止服务

使用以下 Powershell 6 命令停止服务:

Stop-Service -Name {SERVICE NAME}

删除服务

在停止服务一小段时间后,使用以下 Powershell 6 命令删除服务:

Remove-Service -Name {SERVICE NAME}

处理启动和停止事件

处理 OnStartingOnStartedOnStopping 事件:

  1. 使用 OnStartingOnStartedOnStopping 方法创建从 WebHostService 派生的类:

    [DesignerCategory("Code")]
    internal class CustomWebHostService : WebHostService
    {
        private ILogger _logger;
    
        public CustomWebHostService(IWebHost host) : base(host)
        {
            _logger = host.Services
                .GetRequiredService<ILogger<CustomWebHostService>>();
        }
    
        protected override void OnStarting(string[] args)
        {
            _logger.LogInformation("OnStarting method called.");
            base.OnStarting(args);
        }
    
        protected override void OnStarted()
        {
            _logger.LogInformation("OnStarted method called.");
            base.OnStarted();
        }
    
        protected override void OnStopping()
        {
            _logger.LogInformation("OnStopping method called.");
            base.OnStopping();
        }
    }
    
  2. 创建可将 CustomWebHostService 传递给 RunIWebHost 的扩展方法:

    public static class WebHostServiceExtensions
    {
        public static void RunAsCustomService(this IWebHost host)
        {
            var webHostService = new CustomWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    
  3. Program.Main 中,调用 RunAsCustomService 扩展方法,而不是 RunAsService

    host.RunAsCustomService();
    

    若要在 Program.Main 中查看 RunAsService 的位置,请参阅部署类型部分中所示的代码示例。

代理服务器和负载均衡器方案

与来自 Internet 或公司网络的请求进行交互且在代理或负载均衡器后方的服务可能需要其他配置。 有关详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

配置终结点

默认情况下,ASP.NET Core 绑定到 http://localhost:5000 通过设置 ASPNETCORE_URLS 环境变量来配置 URL 和端口。

有关其他 URL 和端口配置方法,请参阅相关服务器文章:

上述指南介绍了对 HTTPS 终结点的支持。 例如,在使用 Windows 服务进行身份验证时,为 HTTPS 配置应用。

备注

不支持使用 ASP.NET Core HTTPS 开发证书保护服务终结点。

当前目录和内容根

通过为 Windows 服务调用 GetCurrentDirectory 返回的当前工作目录是 C:\WINDOWS\system32 文件夹。 system32 文件夹不是存储服务文件(如设置文件)的合适位置 。 使用以下方法之一来维护和访问服务的资产和设置文件。

设置应用文件夹的内容根路径

ContentRootPath 是创建服务时提供给 binPath 参数的同一路径。 请调用包含应用内容根目录的路径的 SetCurrentDirectory,而不是调用 GetCurrentDirectory 来创建设置文件的路径。

Program.Main 中,确定服务可执行文件的文件夹路径,并使用该路径来建立应用的内容根:

var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);

CreateWebHostBuilder(args)
    .Build()
    .RunAsService();

将服务的文件存储在磁盘中的合适位置

使用 IConfigurationBuilder 时,使用 SetBasePath 指定到包含文件的文件夹的绝对路径。

疑难解答

若要排除 Windows 服务应用的故障,请参阅 ASP.NET Core 项目疑难解答和调试

常见错误

  • 正在使用 PowerShell 的早期版本或预发布版本。
  • 注册的服务未使用来自 dotnet publish 命令的应用的已发布输出。 应用部署不支持 dotnet build 命令的输出。 已发布的资产位于以下文件夹之一中,具体取决于部署类型:
    • bin/Release/{TARGET FRAMEWORK}/publish (FDD)
    • bin/Release/{TARGET FRAMEWORK}/{RUNTIME IDENTIFIER}/publish (SCD)
  • 服务未处于“正在运行”状态。
  • 应用使用的资源(例如证书)的路径不正确。 Windows 服务的基本路径是“c:\Windows\System32” 。
  • 用户没有“以服务身份登录”权限。
  • 在执行 New-Service PowerShell 命令时,用户密码已过期,或以不正确的方式传递。
  • 应用需要进行 ASP.NET Core 身份验证,但是未配置安全连接 (HTTPS)。
  • 请求 URL 端口不正确或未在应用中正确配置。

系统和应用程序事件日志

访问系统和应用程序事件日志:

  1. 打开“开始”菜单,搜索“事件查看器” ,然后选择“事件查看器” 应用。
  2. 在“事件查看器” 中,打开“Windows 日志” 节点。
  3. 选择“系统”,以打开系统事件日志。 选择“应用程序” 以打开应用程序事件日志。
  4. 搜索与失败应用相关联的错误。

在命令提示符处运行应用

许多启动错误未在事件日志中生成有用信息。 可以通过在托管系统上在命令提示符处运行应用来找到某些错误的原因。 若要记录应用中的其他详细信息,则降低日志级别,或在开发环境中运行此应用。

清除包缓存

正常运行的应用在开发计算机上升级 .NET Core SDK 或在应用内更改包版本后可能会立即出现故障。 在某些情况下,不同的包可能在执行主要升级时中断应用。 可以按照以下说明来修复其中大部分问题:

  1. 删除 bin 和 obj 文件夹。

  2. 通过从命令行界面执行 dotnet nuget locals all --clear 清除包缓存。

    清除包缓存还可通过使用 nuget.exe 工具并执行命令 nuget locals all -clear 来完成。 nuget.exe 不是与 Windows 桌面操作系统的捆绑安装,必须从 NuGet 网站中单独获取。

  3. 还原并重新生成项目。

  4. 在重新部署应用前,在服务器上删除部署文件夹中的所有文件。

应用缓慢或挂起

故障转储是系统内存的一个快照,可帮助确定应用崩溃、启动故障或应用速度缓慢等状况的原因 。

应用崩溃或引发异常

Windows 错误报告 (WER) 中获取转储并进行分析:

  1. 创建文件夹,将崩溃转储文件保存在 c:\dumps

  2. 使用应用程序可执行文件名称运行 EnableDumps PowerShell 脚本

    .\EnableDumps {APPLICATION EXE} c:\dumps
    
  3. 在造成崩溃的条件下运行应用。

  4. 出现崩溃后,运行 DisableDumps PowerShell 脚本

    .\DisableDumps {APPLICATION EXE}
    

在应用崩溃并完成转储收集后,即可正常终止应用。 PowerShell 脚本会 WER 来按应用收集转储(最多收集 5 个)。

警告

崩溃转储可能会占用大量磁盘空间(每个最多占用数 GB)。

应用挂起、在启动期间失败或正常运行

如果应用挂起(停止响应但不崩溃)、在启动期间失败或者正常运行hangs,请参阅用户模式转储文件:选择最佳工具,以选择适合用于生成转储的工具。

分析转储

可采用几种方法来分析转储。 有关详细信息,请参阅分析用户模式转储文件

其他资源

不使用 IIS 时,可以在 Windows 上将 ASP.NET Core 应用作为 Windows 服务进行托管。 作为 Windows 服务进行托管时,应用将在服务器重新启动后自动启动。

查看或下载示例代码如何下载

先决条件

应用配置

应用需要引用 Microsoft.AspNetCore.Hosting.WindowsServicesMicrosoft.Extensions.Logging.EventLog 包。

在服务之外运行时,若要进行测试和调试,请添加代码以确定应用是否作为服务或控制台应用运行。 检查是否已连接调试器或是否存在 --console 开关。 如果其中一个条件为 true(应用不作为服务运行),请调用 Run 如果条件为 false(应用作为服务运行):

由于命令行配置提供程序需要命令行参数的名称/值对,因此将先从参数中删除 --console 开关,然后 CreateDefaultBuilder 会接收这些参数。

若要写入 Windows 事件日志,请将事件日志提供程序添加到 ConfigureLogging 使用 appsettings.Production.json 文件中的 Logging:LogLevel:Default 键设置日志记录级别。

示例应用中的以下示例调用了 RunAsCustomService,来代替 RunAsService,以处理应用内的生存期事件。 有关详细信息,请参阅处理启动和停止事件部分。

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>();
}

部署类型

有关部署方案的信息和建议,请参阅 .NET Core 应用程序部署

SDK

对于使用 Razor Pages 或 MVC 框架的基于 Web 应用的服务,请在项目文件中指定 Web SDK:

<Project Sdk="Microsoft.NET.Sdk.Web">

如果服务仅执行后台任务(例如 托管服务),请在项目文件中指定辅助角色 SDK:

<Project Sdk="Microsoft.NET.Sdk.Worker">

依赖框架的部署 (FDD)

依赖框架的部署 (FDD) 依赖目标系统上存在共享系统级版本的 .NET Core。 按照本文中的指南采用 FDD 方案时,SDK 会生成一个称为“依赖框架的可执行文件”的可执行文件 (.exe)。

Windows 运行时标识符 (RID) (<RuntimeIdentifier>) 包含目标框架。 在以下示例中,将 RID 设置为 win7-x64 <SelfContained> 属性设置为 false 这些属性指示 SDK 生成用于 Windows 的可执行文件 (.exe ) 以及一个依赖共享 NET Core 框架的应用。

<UseAppHost> 属性设置为 true 此属性为服务提供 FDD 的激活路径(一个可执行文件,格式为 .exe )。

web.config 文件(通常在发布 ASP.NET Core 应用时生成)对于 Windows 服务应用来说是不必要的。 若要禁止创建 web.config 文件,请将 <IsTransformWebConfigDisabled> 属性集添加到 true

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <UseAppHost>true</UseAppHost>
  <SelfContained>false</SelfContained>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

独立部署 (SCD)

独立部署 (SCD) 不依赖主机系统上存在的共享框架。 运行时和应用的依赖项将与应用一起部署。

将 Windows 运行时标识符 (RID) 添加到包含目标框架的 <PropertyGroup> 中:

<RuntimeIdentifier>win7-x64</RuntimeIdentifier>

要发布多个 RID:

  • 通过以分号分隔的列表提供 RID。
  • 使用属性名称 <RuntimeIdentifiers>(复数)。

有关详细信息,请参阅 .NET Core RID 目录

<SelfContained> 属性设置为 true

<SelfContained>true</SelfContained>

服务用户帐户

在管理 PowerShell 6 命令行界面中运行 New-LocalUser cmdlet,为服务创建用户帐户。

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)或更高版本:

New-LocalUser -Name {SERVICE NAME}

对于 Windows 10 2018 年 10 月更新(版本 1809/内部版本 10.0.17763)之前的 Windows 操作系统:

powershell -Command "New-LocalUser -Name {SERVICE NAME}"

在系统提示时,提供强密码

除非将 -AccountExpires 参数提供给到期时间为 DateTimeNew-LocalUser cmdlet,否则用户帐户不会到期。

有关详细信息,请参阅 Microsoft.PowerShell.LocalAccounts服务用户帐户

使用 Active Directory 时管理用户的另一种方法是使用托管服务帐户。 有关详细信息,请参阅组托管服务帐户概述

以服务身份登录权限

为服务用户帐户创建“以服务身份登录”权限:

  1. 通过运行 secpool.msc ,打开本地安全策略编辑器。
  2. 展开“本地策略”节点,选择“用户权限分配”。
  3. 打开“以服务身份登录”策略。
  4. 选择“添加用户或组” 。
  5. 使用下列方法之一提供对象名称(用户帐户):
    1. 在对象名称字段键入用户帐户 ({DOMAIN OR COMPUTER NAME\USER}),然后选择“确定”,以将此用户添加到策略。
    2. 选择“高级” 。 选择“开始查找”。 从列表中选择该用户帐户。 选择“确定” 。 再次选择“确定”,以将该用户添加到策略。
  6. 选择“确定”或“应用”,以接受更改。

创建和管理 Windows 服务

创建服务

使用 PowerShell 注册服务。 从 PowerShell 6 命令行界面,执行以下命令:

$acl = Get-Acl "{EXE PATH}"
$aclRuleArgs = {DOMAIN OR COMPUTER NAME\USER}, "Read,Write,ReadAndExecute", "ContainerInherit,ObjectInherit", "None", "Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($aclRuleArgs)
$acl.SetAccessRule($accessRule)
$acl | Set-Acl "{EXE PATH}"

New-Service -Name {SERVICE NAME} -BinaryPathName {EXE FILE PATH} -Credential {DOMAIN OR COMPUTER NAME\USER} -Description "{DESCRIPTION}" -DisplayName "{DISPLAY NAME}" -StartupType Automatic
  • {EXE PATH} – 应用在主机上的文件夹的路径(如 d:\myservice)。 请勿在此路径中包含应用的可执行文件。 尾部反斜杠是非必需项。
  • {DOMAIN OR COMPUTER NAME\USER} – 服务用户帐户(如 Contoso\ServiceUser)。
  • {SERVICE NAME} – 服务名称(如 MyService)。
  • {EXE FILE PATH} – 应用的可执行文件路径(如 d:\myservice\myservice.exe)。 请将可执行文件的文件名和扩展名包括在内。
  • {DESCRIPTION} – 服务说明(如 My sample service)。
  • {DISPLAY NAME} – 服务显示名称(如 My Service)。

启动服务

使用以下 PowerShell 6 命令启动服务:

Start-Service -Name {SERVICE NAME}

此命令需要几秒钟才能启动服务。

确信服务状态

要检查服务的状态,请使用以下 PowerShell 6 命令:

Get-Service -Name {SERVICE NAME}

状态报告为以下值之一:

  • Starting
  • Running
  • Stopping
  • Stopped

停止服务

使用以下 Powershell 6 命令停止服务:

Stop-Service -Name {SERVICE NAME}

删除服务

在停止服务一小段时间后,使用以下 Powershell 6 命令删除服务:

Remove-Service -Name {SERVICE NAME}

处理启动和停止事件

处理 OnStartingOnStartedOnStopping 事件:

  1. 使用 OnStartingOnStartedOnStopping 方法创建从 WebHostService 派生的类:

    [DesignerCategory("Code")]
    internal class CustomWebHostService : WebHostService
    {
        private ILogger _logger;
    
        public CustomWebHostService(IWebHost host) : base(host)
        {
            _logger = host.Services
                .GetRequiredService<ILogger<CustomWebHostService>>();
        }
    
        protected override void OnStarting(string[] args)
        {
            _logger.LogInformation("OnStarting method called.");
            base.OnStarting(args);
        }
    
        protected override void OnStarted()
        {
            _logger.LogInformation("OnStarted method called.");
            base.OnStarted();
        }
    
        protected override void OnStopping()
        {
            _logger.LogInformation("OnStopping method called.");
            base.OnStopping();
        }
    }
    
  2. 创建可将 CustomWebHostService 传递给 RunIWebHost 的扩展方法:

    public static class WebHostServiceExtensions
    {
        public static void RunAsCustomService(this IWebHost host)
        {
            var webHostService = new CustomWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    
  3. Program.Main 中,调用 RunAsCustomService 扩展方法,而不是 RunAsService

    host.RunAsCustomService();
    

    若要在 Program.Main 中查看 RunAsService 的位置,请参阅部署类型部分中所示的代码示例。

代理服务器和负载均衡器方案

与来自 Internet 或公司网络的请求进行交互且在代理或负载均衡器后方的服务可能需要其他配置。 有关详细信息,请参阅 配置 ASP.NET Core 以使用代理服务器和负载均衡器

配置终结点

默认情况下,ASP.NET Core 绑定到 http://localhost:5000 通过设置 ASPNETCORE_URLS 环境变量来配置 URL 和端口。

有关其他 URL 和端口配置方法,请参阅相关服务器文章:

上述指南介绍了对 HTTPS 终结点的支持。 例如,在使用 Windows 服务进行身份验证时,为 HTTPS 配置应用。

备注

不支持使用 ASP.NET Core HTTPS 开发证书保护服务终结点。

当前目录和内容根

通过为 Windows 服务调用 GetCurrentDirectory 返回的当前工作目录是 C:\WINDOWS\system32 文件夹。 system32 文件夹不是存储服务文件(如设置文件)的合适位置 。 使用以下方法之一来维护和访问服务的资产和设置文件。

设置应用文件夹的内容根路径

ContentRootPath 是创建服务时提供给 binPath 参数的同一路径。 请调用包含应用内容根目录的路径的 SetCurrentDirectory,而不是调用 GetCurrentDirectory 来创建设置文件的路径。

Program.Main 中,确定服务可执行文件的文件夹路径,并使用该路径来建立应用的内容根:

var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);

CreateWebHostBuilder(args)
    .Build()
    .RunAsService();

将服务的文件存储在磁盘中的合适位置

使用 IConfigurationBuilder 时,使用 SetBasePath 指定到包含文件的文件夹的绝对路径。

疑难解答

若要排除 Windows 服务应用的故障,请参阅 ASP.NET Core 项目疑难解答和调试

常见错误

  • 正在使用 PowerShell 的早期版本或预发布版本。
  • 注册的服务未使用来自 dotnet publish 命令的应用的已发布输出。 应用部署不支持 dotnet build 命令的输出。 已发布的资产位于以下文件夹之一中,具体取决于部署类型:
    • bin/Release/{TARGET FRAMEWORK}/publish (FDD)
    • bin/Release/{TARGET FRAMEWORK}/{RUNTIME IDENTIFIER}/publish (SCD)
  • 服务未处于“正在运行”状态。
  • 应用使用的资源(例如证书)的路径不正确。 Windows 服务的基本路径是“c:\Windows\System32” 。
  • 用户没有“以服务身份登录”权限。
  • 在执行 New-Service PowerShell 命令时,用户密码已过期,或以不正确的方式传递。
  • 应用需要进行 ASP.NET Core 身份验证,但是未配置安全连接 (HTTPS)。
  • 请求 URL 端口不正确或未在应用中正确配置。

系统和应用程序事件日志

访问系统和应用程序事件日志:

  1. 打开“开始”菜单,搜索“事件查看器” ,然后选择“事件查看器” 应用。
  2. 在“事件查看器” 中,打开“Windows 日志” 节点。
  3. 选择“系统”,以打开系统事件日志。 选择“应用程序” 以打开应用程序事件日志。
  4. 搜索与失败应用相关联的错误。

在命令提示符处运行应用

许多启动错误未在事件日志中生成有用信息。 可以通过在托管系统上在命令提示符处运行应用来找到某些错误的原因。 若要记录应用中的其他详细信息,则降低日志级别,或在开发环境中运行此应用。

清除包缓存

正常运行的应用在开发计算机上升级 .NET Core SDK 或在应用内更改包版本后可能会立即出现故障。 在某些情况下,不同的包可能在执行主要升级时中断应用。 可以按照以下说明来修复其中大部分问题:

  1. 删除 bin 和 obj 文件夹。

  2. 通过从命令行界面执行 dotnet nuget locals all --clear 清除包缓存。

    清除包缓存还可通过使用 nuget.exe 工具并执行命令 nuget locals all -clear 来完成。 nuget.exe 不是与 Windows 桌面操作系统的捆绑安装,必须从 NuGet 网站中单独获取。

  3. 还原并重新生成项目。

  4. 在重新部署应用前,在服务器上删除部署文件夹中的所有文件。

应用缓慢或挂起

故障转储是系统内存的一个快照,可帮助确定应用崩溃、启动故障或应用速度缓慢等状况的原因 。

应用崩溃或引发异常

Windows 错误报告 (WER) 中获取转储并进行分析:

  1. 创建文件夹,将崩溃转储文件保存在 c:\dumps

  2. 使用应用程序可执行文件名称运行 EnableDumps PowerShell 脚本

    .\EnableDumps {APPLICATION EXE} c:\dumps
    
  3. 在造成崩溃的条件下运行应用。

  4. 出现崩溃后,运行 DisableDumps PowerShell 脚本

    .\DisableDumps {APPLICATION EXE}
    

在应用崩溃并完成转储收集后,即可正常终止应用。 PowerShell 脚本会 WER 来按应用收集转储(最多收集 5 个)。

警告

崩溃转储可能会占用大量磁盘空间(每个最多占用数 GB)。

应用挂起、在启动期间失败或正常运行

如果应用挂起(停止响应但不崩溃)、在启动期间失败或者正常运行hangs,请参阅用户模式转储文件:选择最佳工具,以选择适合用于生成转储的工具。

分析转储

可采用几种方法来分析转储。 有关详细信息,请参阅分析用户模式转储文件

其他资源