一:背景

1. 讲故事

上周有一个项目交付,因为是医院级项目需要在客户的局域网独立部署。程序:netcore 2.0,操作系统:windows server 2012,坑爹的事情就来了, netcore sdk 一直装不上,网上找了资料说需要先安装 Visual C++ Redistributable for Visual Studio 2015, 开开心心下载下来又是安装失败,再次找资料说要打一堆 系统补丁,搞了一天!!!????????????

环境总算是装好了,因为是 Console 服务程序,还得给它做成 windows service,看公司以前的部署方式都是采用 vs 的 windows service 模板,如下图:

怎么说呢,这种方式太老旧了,这篇就来聊聊除了这种还有其他三种很有意思的服务部署方式,干脆拿在一起比较比较吧!

2. 测试代码

为了能更加正规化一些,我在 Console 中监听 Ctrl + C 事件,代码如下:

public class Program{public static void Main(string[] args){var dir = AppDomain.CurrentDomain.BaseDirectory;var cts = new CancellationTokenSource();var bgtask = Task.Factory.StartNew(() => { TestService.Run(cts.Token); });Console.CancelKeyPress += (s, e) =>{TestService.Log($"{DateTime.Now} 后台测试服务,准备进行资源清理!");cts.Cancel();bgtask.Wait();TestService.Log($"{DateTime.Now} 恭喜,Test服务程序已正常退出!");};TestService.Log($"{DateTime.Now} 后端服务程序正常启动!");bgtask.Wait();}}

有了这个模板,再定义一个 TestService,用于不断的执行后台任务,代码如下:

public class TestService{public static void Run(CancellationToken token){while (!token.IsCancellationRequested){Console.WriteLine($"{DateTime.Now}: 1. 获取mysql");System.Threading.Thread.Sleep(2000);Console.WriteLine($"{DateTime.Now}: 2. 获取redis");System.Threading.Thread.Sleep(2000);Console.WriteLine($"{DateTime.Now}: 3. 更新monogdb");System.Threading.Thread.Sleep(2000);Console.WriteLine($"{DateTime.Now}: 4. 通知kafka");System.Threading.Thread.Sleep(2000);Console.WriteLine($"{DateTime.Now}: 5. 所有业务处理完毕");System.Threading.Thread.Sleep(2000);}}public static void Log(string msg){Console.WriteLine(msg);File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "//1.log", $"{msg}\r\n");}}

二:四种服务部署方式

1. 传统的 Windows Service 模板

相信做过 windowsservice 部署的朋友都知道这种方式,需要在 vs 中新建模板,然后定义一个子类 MySerivce 继承于 ServiceBase ,重写父类的 OnStart 和 OnStop 方法,代码如下:

partial class MyService : ServiceBase{CancellationTokenSource cts = new CancellationTokenSource();Task bgtask;public MyService(){InitializeComponent();}protected override void OnStart(string[] args){// TODO: Add code here to start your service.bgtask = Task.Factory.StartNew(() => { TestService.Run(cts.Token); });}protected override void OnStop(){// TODO: Add code here to perform any tear-down necessary to stop your service.cts.Cancel();bgtask.Wait();}}

再重构一下 Main 方法:

public class Program{public static void Main(string[] args){ServiceBase.Run(new MyService());}}

最后执行 publish 发布,用 windows  自带的 sc 安装服务。


sc create MyService BinPath=E:\net5\ConsoleApp1\ConsoleApp2\bin\Release\netcoreapp3.1\publish\ConsoleApp2.exe
sc start MyService

为了验证程序是否运行正常,可以去服务面板以及安装路径查看启动日志。

接下来说说优缺点吧:

  • 缺点:需要修改代码,而且一旦代码改完后,就不能再双击 exe 执行,导致无法调试。

  • 优点:不需要额外依赖,全部采用内建技术。

2. 使用开源的 Topshelf

大家有兴趣可以看一下它的官网:http://topshelf-project.com  比较轻便简洁,使用 nuget Install-Package Topshelf 接入项目,按照官方demo我需要在 TestService 中实现 Start 和 Stop 方法,修改如下:


public class TestService{CancellationTokenSource cts = new CancellationTokenSource();CancellationToken token;Task bgtask;public TestService(){token = cts.Token;}public void Start(){bgtask = Task.Run(() =>{while (!token.IsCancellationRequested){Log($"{DateTime.Now}: 1. 获取mysql");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 2. 获取redis");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 3. 更新monogdb");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 4. 通知kafka");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 5. 所有业务处理完毕");System.Threading.Thread.Sleep(2000);}});}public void Stop(){cts.Cancel();bgtask.Wait();}public static void Log(string msg){Console.WriteLine(msg);File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "1.log", $"{msg}\r\n");}}

接下来再改造一下 Main 方法,使用它的 HostFactory 类,代码如下:

public static void Main(string[] args){var rc = HostFactory.Run(x =>                                   //1{x.Service<TestService>(s =>                                   //2{s.ConstructUsing(name => new TestService());            //3s.WhenStarted(tc => tc.Start());                         //4s.WhenStopped(tc => tc.Stop());                          //5});x.RunAsLocalSystem();                                       //6x.StartAutomatically();x.SetDescription("TestService2 Topshelf Host");                   //7x.SetDisplayName("MyService2");                                  //8x.SetServiceName("MyService2");                                  //9});                                                             //10var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  //11Environment.ExitCode = exitCode;}

从上面代码可以看出,主要还是做一些服务的信息配置,然后就可以发布项目,使用 xxx.exe install 进行服务安装,如下图:


E:\net5\ConsoleApp1\ConsoleApp5\bin\Release\netcoreapp3.1\publish2>ConsoleApp5.exe install
Configuration Result:
[Success] Name MyService2
[Success] Description TestService2 Topshelf Host
[Success] ServiceName MyService2
Topshelf v4.2.1.215, .NET Framework v3.1.9Running a transacted installation.Beginning the Install phase of the installation.
Installing MyService2 service
Installing service MyService2...
Service MyService2 has been successfully installed.The Install phase completed successfully, and the Commit phase is beginning.The Commit phase completed successfully.The transacted install has completed.

从输出信息来看已经安装成功,大家感觉这种方式优缺点如何?

  • 缺点:需要安装第三方工具包,需要修改代码,而且还挺大的。。。

  • 优点:双击也可调试,实现了系统的一些内建监听,比如 Ctrl + C

3. 使用微软新内置的 Hosting

说到这个 Hosting 相信大家不会陌生,在 netcore 中不管是 Console, MVC,WebApi 都是 Console 模式,比如我新建一个如下 WebApi。

这里我就有想法了,能不能把 Main 中的 Hosting 扣出来给我的服务用,那真的是????????了,还别说,真的可以,安装一个 hosting + for windowsservice 即可。


nuget Install-Package Microsoft.Extensions.Hosting
nuget Install-Package Microsoft.Extensions.Hosting.WindowsServices

值得庆幸的是,包的最小依赖是 .NETStandard 2.0 ,意味着 .NET Framework 4.6.1 + 和 .NetCore 2.0 + 都可以用的上,????????

接下来就是改造,让 TestService 重写的父类 BackgroundService 中的 ExecuteAsync 方法,如下代码:

public class TestService : BackgroundService{protected override Task ExecuteAsync(CancellationToken stoppingToken){return Task.Run(() =>{while (!stoppingToken.IsCancellationRequested){Log($"{DateTime.Now}: 1. 获取mysql");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 2. 获取redis");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 3. 更新monogdb");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 4. 通知kafka");System.Threading.Thread.Sleep(2000);Log($"{DateTime.Now}: 5. 所有业务处理完毕");System.Threading.Thread.Sleep(2000);}});}public static void Log(string msg){Console.WriteLine(msg);File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + "1.log", $"{msg}\r\n");}}

然后再改造 Main 方法。

public class Program{public static void Main(string[] args){CreateHostBuilder(args).Build().Run();}public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).UseWindowsService().ConfigureServices((hostContext, services) =>{services.AddHostedService<TestService>();});}

哇!是不是熟悉的代码映入眼前,双击 Console 是不是更加熟悉了哈~~~

最后可以用 sc 命令做成服务。

  • 缺点:有少量的代码侵入性,引入的依赖稍多

  • 优点:微软正派血统,功能强大,内建日志支持

4. nssm 第三方工具

前面三种要么是内建模板,要么是安装 dll 的方式,那有没有一种真的可以对代码 零侵入 呢?大千世界无奇不有,可以看一下这款工具:http://www.nssm.cc  ,你无需修改任何代码, 直接发布代码后用下面命令安装即可:


C:\Windows\system32>cd C:\xcode\soft\nssm-2.24\win64C:\xcode\soft\nssm-2.24\win64>nssm  install TestService3 E:\net5\ConsoleApp1\ConsoleApp6\bin\Release\netcoreapp3.1\publish\ConsoleApp6.exe && nssm start TestService3
Service "TestService3" installed successfully!
TestService3: START: 操作成功完成。

看到没有,我真的没有动任何代码,服务就安装完成了。

  • 缺点:需要安装第三方工具

  • 优点:对代码零侵入

三:总结

如果让我选择的话,我喜欢 3+4 的组合,代码层面我更愿意使用 微软新的 Hosting 承载,服务部署上更喜欢 nssm,毕竟它比 sc 灵活强大的多,不知道大家更喜欢哪一种部署方式呢?欢迎留言补充!????????????

把 Console 部署成 Windows 服务,四种方式总有一款适合你!相关推荐

  1. SpringBoot打包部署成Windows服务的详细步骤

    SpringBoot打包部署成Windows服务的详细步骤 1.SpringBoot 1.1 pom.xml 1.2 项目代码 1.2.1 项目结构 1.2.2 Controller的代码 1.2.3 ...

  2. Activiti6自学之路(五)—— 部署流程资源的四种方式及数据库表更新情况

    创建了流程图资源后, 一般我们需要对创建的资源如(leave.bpmn.leave.png)进行部署,部署方式我这里列出四种方式,前三种为单个流程资源的部署,第四种方式可以同时部署多个流程资源. 一. ...

  3. 墙面有几种装修方法_墙面怎么装?四种装修方式总有一款适合你

    ‍墙面作为家庭装修必须考虑的一部分,其装修的效果直接影响整个家的整体氛围,墙面就好比我们照相时的背景图,如果背景选择的不好,即使人长得再美,也会影响整个画面的效果,在家居中,若忽略墙面的装修,即使买再 ...

  4. 第三节:Windows平台部署Asp.Net Core应用(基于IIS和Windows服务两种模式)

    一. 简介 Asp.Net Core 部署方式有两种:依赖框架和独立部署. 1. 框架依赖的部署: 顾名思义,依赖框架的部署 (FDD) 依赖目标系统上存在共享系统级版本的 .NET Core. 由于 ...

  5. minio:安装部署并安装成windows服务

    前言 关于分布式文件存储,之前我也是使用fastdfs,那为什么突然选择minio? 1.它可以多平台部署 2.搭建起来不是很复杂 3.github近30K的star 什么是minio? 构建高性能的 ...

  6. 如何把Asp.NetCore部署成window服务

    在window上部署Asp.netcore时,当不想使用IIS时,使用双击启动就会出现一个黑色的窗体,总感觉会不经意间给关闭掉.这时候就想如果可以生成winserver,这样就可以避免掉,也可以使崩溃 ...

  7. 将Carte部署为Windows服务

    1. 为何需要部署成Windows Service 如果以cmd命令行来启动Carte,如果开发者不小心将此命令行窗口给关闭了,那么Carte服务也会随之关闭.因此将Carte部署成Windows S ...

  8. Java的jar文件安装成windows 服务

    Java的jar文件安装成windows 服务: 1.下载:nssm,复制到jar文件目录下 2. jar文件目录下创建bat文件[run.bat],内容为[java -jar 文件名.jar] 3. ...

  9. 将zookeeper、Tomcat、redis部署成windos服务

    windows下zookeeper安装并发布成windows服务: https://blog.csdn.net/yzy199391/article/details/80605195 把Tomcat打成 ...

最新文章

  1. Linux 裸设备基础知识(转)
  2. linux下如何删除整行文本格式,Shell中如何删除文本比较长的行的实现方法
  3. nginx多进程,日志不乱序
  4. Java开源——常见J2EE框架简介
  5. day36-hibernate检索和优化 02-Hibernate检索方式:简单查询及别名查询
  6. 史上最全,最完美的 JAVA 技术体系思维导图总结,没有之一!
  7. 【计算机网络】OSI参考模型与TCP/IP分层模型详解(超级详细,三张图完整说明)
  8. HDU 5238 Calculator 线段树 中国剩余定理
  9. .NET Conf 2019日程(北京时间)
  10. PopTheBubble —测量媒体偏差的产品创意
  11. 实习踩坑之路:LocalDateTime计算间隔天数,compareTo/Period的beetween方法导致的bug
  12. android 外接u盘格式化,u盘格式(安卓u盘格式化工具apk)
  13. c语言prn文件,C语言prntf和scanf函数.doc
  14. Oracle获取汉字拼音码
  15. 操作Windows文件夹时,弹出文件夹正在使用,操作无法完成【解决】
  16. R语言forestplot 根据分组设置不同颜色
  17. 共享锁(S锁)和排它锁(X锁)
  18. 「雅礼集训 2018 Day10」贪玩蓝月
  19. jmeter压测学习11-模拟浏览器访问web页面
  20. 谷歌上做SEO价钱大概多少,Google优化怎么收费?

热门文章

  1. Django的第一步(第一节)
  2. SQL Server中,with as使用介绍
  3. 编译源码 JAVA out of memory
  4. SpringMVC总结帖
  5. linux oracle手动启动两个实例
  6. allegro下快捷键设置[转贴]
  7. DNS resolving 占用大量日志
  8. 2010 本年度认证目标:坐沙发的熊
  9. DotNetMagic 5.4.0破解
  10. 使用环境变量来配置 Teams App 的 manifest