目录
- 基于 Web 应用的服务
- 发布
- 脚本
- 安装为服务并运行
- 停止并卸载服务
- 安装为 WindowsServer
- 删除安装失败的服务
- 重新发布安装
- 卸载服务
- 服务仅执行后台任务
- 脚本
- 依赖部署 VS 独立部署
- 总结
- 第一步:引用包
- 第二步: 修改 Program.cs
- AspNet Core 3.x、5.x
- AspNet Core 6.0
- 后台服务 Worker:BackgroundService
- 第三步:发布项目
- 第四步:创建windows服务
- 第五步:启动 windows服务
- 第六步:停止和删除 windows服务
如果不想看详细的操作步骤,直接看本文结尾的【总结】
官方文档:
https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&viewFallbackFrom=aspnetcore-3.0&tabs=visual-studiohttps://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-6.0&tabs=visual-studiohttps://docs.microsoft.com/zh-cn/dotnet/core/extensions/windows-service
官方示例:
.Net5及其以下版本:https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/host-and-deploy/windows-service
.Net6.0:https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/host-and-deploy/windows-service/samples/6.x/WebAppServiceSample/Program.cs
.Net6.0 写法不同有 .Net5
部署为windowsServer,首先要看网站是基于什么类型的!
对于使用 Razor Pages 或 MVC 框架的基于 Web 应用的服务,请在项目文件中指定 Web SDK:
<Project Sdk="Microsoft.NET.Sdk.Web">
如果服务仅执行后台任务(例如 托管服务),请在项目文件中指定辅助角色 SDK:
<Project Sdk="Microsoft.NET.Sdk.Worker">
不同的网站类型,选择不同的的依赖包;
<!--托管服务部署时的依赖-->
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="6.0.2" />
<!--基于 Web 应用的服务部署时的依赖-->
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
当然可以这两种的网站类型是可以同时存在,官方示例,就是这样子的,
项目工程文件:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<!--依赖框架的部署 (FDD)-->
<IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>
<ItemGroup>
<!--托管服务部署时的依赖-->
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="6.0.2" />
<!--基于 Web 应用的服务部署时的依赖-->
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
</ItemGroup>
</Project> |
// 托管服务
builder.Services.AddHostedService<ServiceA>();
// 基于 Web 应用的服务
builder.Host.UseWindowsService();
其中,托管服务 ServiceA
的定义如下:
public class ServiceA : BackgroundService
{
public ServiceA(ILoggerFactory loggerFactory)
{
Logger = loggerFactory.CreateLogger<ServiceA>();
}
public ILogger Logger { get; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Logger.LogInformation("ServiceA is starting.");
stoppingToken.Register(() => Logger.LogInformation("ServiceA is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
Logger.LogInformation("ServiceA is doing background work.");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
Logger.LogInformation("ServiceA has stopped.");
}
}
基于 Web 应用的服务
创建 AspNet Core 项目,项目名称为:WebWinServer1
添加包 Microsoft.Extensions.Hosting.WindowsServices
接着修改 Program.cs
AspNet Core 3.x,AspNet Core 5.x的写法:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()//指定项目可以部署为Windows服务
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseUrls("http://*:8787"); //使用的端口号
});
AspNet Core 6.0 的写法:
由于 AspNet Core 6.0 自身存在的一个 bug,见:https://github.com/dotnet/AspNetCore.Docs/issues/23387
当 AspNet Core 6.0 项目部署为 Windows服务后,由于这个bug导致程序找不到项目文件,最终Windows启动会失败!!!。
如何解决呢?在上面的这个issues下,开发者 davidfowl 给出了两个解决方案:
第一种方案:
var options = new WebApplicationOptions
{
Args = args,
ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};
var builder = WebApplication.CreateBuilder(options);
builder.Host.UseWindowsService();
//......
第二种方案:
在创建 windows服务的时候,额外添加 --contentRoot <执行程序所在目录> 参数,即:
sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
如果你已经创建了windows服务,也可以通过如下命令修改配置:
sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
注:其中,目录:E:\webpulish\web-winserver 是项目的发布目录,见 下一节发布
发布
右键工程文件,在菜单中选择【发布】,设置发布配置
注意,这里的
部署模式:选择【框架依赖】,目标框架选择 netcoreapp3.1,
但是我的电脑没有 .netcore3.1的运行时,之所以这样设置,只是为了测试和模拟真实的生成环境可能遇到的情况
多个网站版本不同,需要安装各个版本的运行时,这有不太可能,除非使用Docker部署。
其中,发布文件地址是:
E:\webpulish\web-winserver
然后点击发布
脚本
安装为服务并运行
创建安装脚本文件install.bat
set serviceName=AspNetCore-WinServer
set serviceFilePath=%cd%\WebWinServer1.exe
set serviceDescription=AspNetCoreweb程序部署为WindowsServer
sc create %serviceName% binPath=%serviceFilePath%
sc config %serviceName% start=auto
sc description %serviceName% %serviceDescription%
sc start %serviceName%
pause
停止并卸载服务
创建卸载脚本文件uninstall.bat
set serviceName=AspNetCore-WinServer
sc stop %serviceName%
sc delete %serviceName%
pause
安装为 WindowsServer
把上面创建的安装脚本 install.bat 和卸载脚本 uninstall.bat 复制到发布文件根目录,示例中的目录是:
E:\webpulish\web-winserver
使用管理员身份打开CMD,执行安装脚本 install.bat
注意:一定要是管理员身份打开,否则报错:
[SC] OpenService 失败 5:拒绝访问
>E:
>E:\>cd E:\webpulish\web-winserver
>E:\webpulish\web-winserver>install.bat
....
E:\webpulish\web-winserver>sc.exe start AspNetCore-WinServer
[SC] StartService 失败 2:
或者手动输入命令创建windows服务
AspNet Core 3.x、5.x 创建windows服务的命令如下:
E:\webpulish\web-winserver>sc.exe create AspNetCore-WinServer binPath=E:/webpulish/web-winserver/WebWinServer1.exe start=auto
[SC] CreateService 成功
binPath 必须是全路径。
AspNet Core 6.0 创建windows服务的命令如下:
sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
相较于AspNet Core 3.x、5.x,命名中多了一个参数:--contentRoot E:\webpulish\web-winserver
其中原因详见我们上面提到过的 Issues:https://github.com/dotnet/AspNetCore.Docs/issues/23387
如果你已经创建了windows服务,也可以通过如下命令修改配置:
sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
然后执行如下命令启动windows服务:
sc start AspNetCore-WinServer
服务启动失败,这是因为,我的电脑用的是 .Net 6,现在发布的目标框架是.netcoreapp3.1
本机没有.netcore3.1的运行时。所以启动失败!
删除安装失败的服务
执行以下命令,删除服务
sc delete AspNetCore-WinServer
虽然删除WindowsServer成功,但是服务列表中还是存在,要彻底删除,
安装组合键:Win + R,输入:regedit,打开系统注册表,
删掉windows服务的注册表信息,通常路径在:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services 找到你的Service服务的名字,然后把整个文件夹删掉
这时,Windows服务列表中还是存在服务:AspNetCore-WinServer,重启电脑即可。
重新发布安装
上节说到我的电脑用的是.Net 6,现在发布的目标框架是.netcoreapp3.1 ,这种情况跟实际的生成环境一样,
生成环境有多个网站,第一个网站是 .netcore3.1, 第二个网站是基于.net5, 第三个网站是.net6,
这时不可能同时安装3个版本的 .net运行时,所以发布的时候,发布配置如下:
部署模式:选择【独立】,目标框架选择 netcoreapp3.1,如下图所示:
点击发布。
复制 安装脚本 install.bat到发布目录:E:\webpulish\web-winserver
然后以管理员身份打开CMD(必须以管理员身份打开),切换到发布目录 ,执行安装脚本 install.bat
E:\webpulish\web-winserver>install.bat
输出结果:
E:\webpulish\web-winserver>set serviceName=AspNetCore-WinServer
E:\webpulish\web-winserver>set serviceFilePath=E:\webpulish\web-winserver\WebWinServer1.exe
E:\webpulish\web-winserver>set serviceDescription=AspNetCoreweb程序部署为WindowsServer
E:\webpulish\web-winserver>sc config AspNetCore-WinServer start=auto
[SC] ChangeServiceConfig 成功
E:\webpulish\web-winserver>sc description AspNetCore-WinServer AspNetCoreweb程序部署为WindowsServer
[SC] ChangeServiceConfig2 成功
E:\webpulish\web-winserver>sc start AspNetCore-WinServer
SERVICE_NAME: AspNetCore-WinServer
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 11148
FLAGS :
E:\webpulish\web-winserver>pause
AspNet Core 6.0 创建windows服务的命令如下:
sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
然后执行如下命令启动windows服务:
sc start AspNetCore-WinServer
安装成功,启动也成功后,按组合键:Win + R, 输入:services.msc
,打开服务列表
访问站点:http://localhost:8787/
卸载服务
以管理员身份打开CMD,执行如下卸载脚本 uninstall.bat
>E:
>E:\>cd E:\webpulish\web-winserver
>E:\webpulish\web-winserver>uninstall.bat
输出结果:
E:\webpulish>set serviceName=AspNetCore-WinServer
E:\webpulish>sc stop AspNetCore-WinServer
SERVICE_NAME: AspNetCore-WinServer
TYPE : 10 WIN32_OWN_PROCESS
STATE : 3 STOP_PENDING
(STOPPABLE, NOT_PAUSABLE, ACCEPTS_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
E:\webpulish>sc delete AspNetCore-WinServer
[SC] DeleteService 成功
E:\webpulish>pause
请按任意键继续. . .
或者手动输入命令执行停止和删除:
sc stop AspNetCore-WinServer
sc delete AspNetCore-WinServer
删除成功
,此时服务只是被禁用,属于标记删除,如果要彻底删除,使用注册表删除,见上一节的具体操作。
服务仅执行后台任务
创建控制台程序,项目名称为:WorkerWinService,修改项目工程文件为:
<Project Sdk="Microsoft.NET.Sdk.Worker">
添加包 Microsoft.AspNetCore.Hosting.WindowsServices
<Project Sdk="Microsoft.NET.Sdk.Worker">
...
<ItemGroup>
...
<PackageReference Include="Microsoft.AspNetCore.Hosting.WindowsServices" Version="3.1.13" />
</ItemGroup>
</Project>
接着修改 Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>(); // 添加托管服务
});
定义后台托管服务:Worker, 继承 BackgroundService
Worker.cs
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
脚本
安装和卸载脚本同上,只是修改服务名称为 WorkerWinService 即可。
依赖部署 VS 独立部署
总结
将 AspNet Core 部署为 Windows服务步骤:
第一步:引用包
- AspNet Core WebApp和 AspNet Core Web Api 添加包 Microsoft.Extensions.Hosting.WindowsServices
- 后台服务 Worker: BackgroundService , 添加包 Microsoft.AspNetCore.Hosting.WindowsServices
第二步: 修改 Program.cs
AspNet Core 3.x、5.x
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()//指定项目可以部署为Windows服务
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseUrls("http://*:8787"); //使用的端口号
});
AspNet Core 6.0
var options = new WebApplicationOptions
{
Args = args,
ContentRootPath = WindowsServiceHelpers.IsWindowsService() ? AppContext.BaseDirectory : default
};
var builder = WebApplication.CreateBuilder(options);
builder.Host.UseWindowsService();
后台服务 Worker:BackgroundService
先定义后台托管服务:Worker, 继承 BackgroundService
Worker.cs
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
再添加托管服务
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>(); // 添加托管服务
});
第三步:发布项目
框架目标:建议选择【独立】,但这也使得发布文件变大,根据实际情况而定吧
第四步:创建windows服务
AspNet Core 3.x、5.x 创建windows服务的命令如下:
sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe"
AspNet Core 6.0 创建windows服务的命令如下:
sc create AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
注意:
此时,我们并没有设置端口,默认将使用 http://localhost:5000
和 https://localhost:5001
, 执行如下命令来指定端口:
sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver --urls=http://localhost:4000"
Tips:
如果创建时发现路径、参数等有误,可以执行如下命令进行修改,不用卸载windows服务:sc config AspNetCore-WinServer binPath= "E:\webpulish\web-winserver\WebWinServer1.exe --contentRoot E:\webpulish\web-winserver"
第五步:启动 windows服务
sc start AspNetCore-WinServer
第六步:停止和删除 windows服务
sc stop AspNetCore-WinServer
sc delete AspNetCore-WinServer
执行完上面两个命令后,只是标记删除,真正删除还得到注册表中删除:
按组合键:Win + R,输入:regedit,打开系统注册表,
删掉windows服务的注册表信息,
通常路径在:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
找到你的Service服务的名字,然后把整个文件夹删掉
这时,Windows服务列表中还是存在服务:AspNetCore-WinServer,重启电脑即可。