利用Topshelf把.NET Core Generic Host管理的应用程序部署为Windows服务
背景
2019第一篇文章。
此文源于前公司在迁移项目到.NET Core的过程中,希望使用Generic Host来管理定时任务程序时,没法部署到Windows服务的问题,而且官方也没给出解决方案,只能关注一下官方issue #809 等他们方解决了。
官方文档只提供了一个《在 Windows 服务中托管 ASP.NET Core》的方案,可以使用Microsoft.AspNetCore.Hosting.WindowsServices类库来把Web应用部署为Windows服务。但是ASP.NET Core虽然是控制台程序,但是它本身是使用了含有HTTP管道的Web Host来负责应用程序的生命周期管理,用它来作为定时任务的话,会有很多不必要的工作负载,例如占用端口、增加了很多依赖等等。
官方意识到这个问题之后,在.NET Core 2.1版本新增了Generic Host通用主机,剥离了原来WebHost的Http管道相关的API,源码中可以发现Web Host已经基于Generic Host实现。它才是作为纯粹定时任务程序的最佳拍档。
但是由于Generic Host本身非常简单,用它运行的程序设置在注册为Windows服务启动之后会自动停止。研究很久之后才知道,想在Windows上启动服务,还是不能像Linux上那么简单——
于是尝试结合Topshelf来创建Windows服务,最终成功了。
实现方法
- 先实现
IHostLifetime接口来接管应用程序的生命周期,其实就是用空的实现来替换掉默认的ConsoleLifetime,这样就可以在之后由Topshelf框架内部去管理生命周期。
internal class TopshelfLifetime : IHostLifetime
{
public TopshelfLifetime(IApplicationLifetime applicationLifetime, IServiceProvider services)
{
ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));
}
private IApplicationLifetime ApplicationLifetime { get; }
public Task WaitForStartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
- 然后实现
IHostedService接口,把后台任务逻辑写到StartAsync方法中,参见官方文档《在 ASP.NET Core 中使用托管服务实现后台任务》,本文示例使用定时写入文本到一个文件来测试定时任务是否成功运行。
internal class FileWriterService : IHostedService, IDisposable
{
private static string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"test.txt");
private Timer _timer;
public Task StartAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return Task.FromCanceled(cancellationToken);
_timer = new Timer(
(e) => WriteTimeToFile(),
null,
TimeSpan.Zero,
TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
public void WriteTimeToFile()
{
if (!File.Exists(path))
{
using (var sw = File.CreateText(path))
{
sw.WriteLine(DateTime.Now);
}
}
else
{
using (var sw = File.AppendText(path))
{
sw.WriteLine(DateTime.Now);
}
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
- 构建Generic Host,在
ConfigureServices方法中注册TopshelfLifetime,并且注册一个托管服务FileWriterService,就能完成Generic Host的简单构建,当然完整的项目应该还包含配置、日志等等。最后,使用Topshelf来接管Generic Host,创建Windows服务。
internal class Program
{
private static void Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<IHostLifetime, TopshelfLifetime>();
services.AddHostedService<FileWriterService>();
});
HostFactory.Run(x =>
{
x.SetServiceName("GenericHostWindowsServiceWithTopshelf");
x.SetDisplayName("Topshelf创建的Generic Host服务");
x.SetDescription("运行Topshelf创建的Generic Host服务");
x.Service<IHost>(s =>
{
s.ConstructUsing(() => builder.Build());
s.WhenStarted(service =>
{
service.Start();
});
s.WhenStopped(service =>
{
service.StopAsync();
});
});
});
}
}
- 最后发布应用程序,并安装到Windows服务。
以管理员权限开启终端,执行命令:
dotnet publish -c release -r win-x64
cd path-to-project/bin/release/netcoreapp2.1/win-x64/publish
./project-name install
net start GenericHostWindowsServiceWithTopshelf

这样这个Windows服务就启动了!查看输出文件,可以看到定时写入成功,服务也一直没关闭~

示例代码
https://github.com/ElderJames/GenericHostWindowsServiceWithTopshelf
参考链接
官方文档《.NET 通用主机》
官方文档《在 ASP.NET Core 中使用托管服务实现后台任务》
利用Topshelf把.NET Core Generic Host管理的应用程序部署为Windows服务的更多相关文章
- .NET Core Generic Host Windows服务部署使用Topshelf
此文源于前公司在迁移项目到.NET Core的过程中,希望使用Generic Host来管理定时任务程序时,没法部署到Windows服务的问题,而且官方也没给出解决方案,只能关注一下官方issue # ...
- .NET Core Generic Host项目使用Topshelf部署为Windows服务
1..NET Core Generic Host是什么? 在.NET Core 2.1版本加入了一种新的Host,即Generic Host(通用主机). 现在在2.1版本的Asp.Net Core中 ...
- 使用NSSM把.Net Core部署至 Windows 服务
为什么部署至Windows Services 在很多情况下,很少会把.Net Core项目部署至Windows服务中,特别是Asp.net Core就更少了.一般情况下,Asp.net Core会部署 ...
- NSSM把.Net Core部署至 Windows 服务
NSSM把.Net Core部署至 Windows 服务 https://www.cnblogs.com/emrys5/p/nssm-netcore.html 为什么部署至Windows Servic ...
- ASP.NET Core教程:ASP.NET Core 程序部署到Windows系统
一.创建项目 本篇文章介绍如何将一个ASP.NET Core Web程序部署到Windows系统上.这里以ASP.NET Core WebApi为例进行讲解.首先创建一个ASP.NET Core We ...
- .net core工具组件系列之Redis—— 第一篇:Windows环境配置Redis(5.x以上版本)以及部署为Windows服务
Cygwin工具编译Redis Redis6.x版本是未编译版本(官方很调皮,所以没办法,咱只好帮他们编译一下了),所以咱们先下载一个Cygwin,用它来对Redis进行编译. Cygwin下载地址: ...
- net core 部署到windows 服务
NSSM是一个服务封装程序,它可以将普通exe程序封装成服务,使之像windows服务一样运行.同类型的工具还有微软自己的srvany,不过nssm更加简单易用,并且功能强大.它的特点如下: 支持普通 ...
- ASP.NET Core Web程序托管到Windows 服务
前言 在 .NET Core 3.1和WorkerServices构建Windows服务 我们也看到了,如何将workerservices构建成服务,那么本篇文章我们再来看看如何将web应用程序托管到 ...
- 控制台程序秒变Windows服务(Topshelf)
项目中有些时候需要写服务,一般我们都是先创建控制台程序,测试,运行,成功之后再创建windows服务程序,这样好麻烦啊,有没有简单的控制台程序直接变成Widnows服务,经过查找,找到了Topshel ...
随机推荐
- 图 Graph-图的表示及其遍历
2018-03-05 16:19:46 图是计算机科学中的一个非常重要的概念,图是一种多对多的关系.从某种角度上来说树和链表都是图的一种特例. 一.图的抽象数据类型 二.表示图的方法 图是由结点和边构 ...
- ajax实现用户注册
需求分析 页面中给出注册表单: 在username input标签中绑定onblur事件处理函数. 当input标签失去焦点后获取 username表单字段的值,向服务端发送AJAX请求: djang ...
- hdu1520树形dp第一题
判断最大的欢喜值,如果上司来了,直系下属就不来 如果子节点j不来那么dp[i][1]+=dp[j][0];如果子节点j来那么dp[i][0]+=max(dp[j][0],dp[j][1]);//因为j ...
- C#/JAVA 程序员转GO/GOLANG程序员笔记大全(DAY 03)
go语言当中,没有 class 的概念,那么面向对象的编程思想如何展现呢,go语言中对结构体的使用 struct. package main import "fmt" type P ...
- filter-mapping中的dispatcher使用
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABGAAAAEJCAIAAABUr8bLAAAgAElEQVR4nO3dX2/bVoL3cb4h+WYnwN
- Hibernate所用15个jar包
Hbernate3.jar-------------------核心包antlr.jar----------------------------语言转换工具,hibernate用他将hql语句转换为s ...
- Python之路day13 web 前端(JavaScript,DOM操作)
参考链接:http://www.cnblogs.com/wupeiqi/articles/5433893.html day13 1. CSS示例 2. JavaScript 3. DOM操作 上节内容 ...
- 【hive】null值判断
hive用作null值的判断是不能用 = , != 来判断的 只能用is [not] null来完成 不支持ifnull()函数(mysql支持) 适用于所有数据类型 (1)条件中判断是否为空 whe ...
- ios 不兼容 报错NaN
function GetDateDiff(DiffTime) { //将xxxx-xx-xx的时间格式,转换为 xxxx/xx/xx的格式 Time = DiffTime.replace(/\-/g, ...
- Hrbust 1535 相爱
Description 静竹在斐波那契的帮助下,渐渐的和数学相爱了.和数学在一起最有意思的就是它能够出一些特别有意思并且巧妙的题目让静竹来思考.这次也不例外,给静竹两个数a,b,又给出了加,减,乘,除 ...