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

一:背景

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 灵活强大的多,不知道大家更喜欢哪一种部署方式呢?欢迎留言补充!????????????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AQS ReentrantLock 实现原理

参考链接 文章目录1 AQS (AbstractQuenedSynchronizer)2 Lock 接口与显式条件3 转账 Demo&#xff1a;解决死锁的两种方案4 ReentrantLock 非公平锁加锁流程5 ReentrantLock 和 synchronized 的异同6 ReentrantReadWriteLock1 AQS (AbstractQuenedSynchronizer) 基于 AQS 的同步…

TensorFlow 2学习和工业CV领域应用 心得分享

我是一名来自苏州的机器视觉开发者&#xff0c;从事传统的机器视觉算法开发有11年了&#xff0c;从2018年开始&#xff0c;因为一些复杂微弱的瑕疵检测项目遇到的传统算法瓶颈&#xff0c;开始接触到了深度学习&#xff0c;并选择了使用TensorFlow&#xff0c;期间也是不断摸索…

历史版本_新版本爆料第弹丨英雄练习新去处,荣耀历史秀出来!

《万物有灵》新版本即将到来新版本来临之前妲己宝宝给自己定下了2个小目标&#xff01;via.小五怎么不开心目标一扩展自己小得可怜的英雄勺成为一名拥有英雄海的补位大神目标二通过自己的实力获得N1个响当当的荣耀称号很多召唤师会有疑问&#xff1a;凭妲己宝宝的实力&#xff…

循环遍历多层json_面试官:JSON.stringify() 实现深拷贝有什么问题

为什么要进行深拷贝JS中的变量在内存中存储分为值类型和引用类型&#xff1a; 值类型&#xff1a; 1、占用空间固定&#xff0c;保存在栈中&#xff1b; 2、保存与复制的是值本身&#xff1b; 3、基本类型数据是值类型&#xff08;String,Number,undefined,Boolean,Null&#x…

.NET架构小技巧(6)——什么是好的架构

首先声明&#xff0c;可能本篇文章的含金量配不上这个标题&#xff0c;因为说起架构&#xff0c;可能大家都比较关注高大上的架构&#xff0c;比如分布式的&#xff0c;高并发的&#xff0c;低耦合的&#xff0c;易扩展的等等&#xff0c;本篇可能使你失望了&#xff0c;因为这…

电子工程系庆贺电贺信_创造下一代光电子集成电路

全球互联网正以每年24%的复合速度增长&#xff0c;到2021年将达到每年3.3 zb字节。高速光通信在这个不断连接的世界中是迫切需要的&#xff0c;为了跟上这种增长&#xff0c;光模块的制造的发展是迫切需要的。复旦大学电子工程系博士研究生刘晓研究了集成构成光模块的电子电路和…

禁用笔记本键盘_如何禁用/启用笔记本内置键盘?

有些小伙伴外接了USB键盘想屏蔽掉笔记本的内置键盘&#xff0c;绞尽脑汁都没有办法禁用&#xff0c;其实方法很简单只需要一个简单的命令即可。1、右键点击左下角开始图标(WinX)&#xff0c;选择Windows Powershell(管理员)。2、在打开的窗口中&#xff0c;输入cmd。3、然后输入…

IdentityServer4系列 | 资源密码凭证模式

一、前言从上一篇关于客户端凭证模式中&#xff0c;我们通过创建一个认证授权访问服务&#xff0c;定义一个API和要访问它的客户端&#xff0c;客户端通过IdentityServer上请求访问令牌&#xff0c;并使用它来控制访问API。其中&#xff0c;我们也注意到了在4.x版本中于之前3.x…

深入探究ASP.NET Core Startup的初始化

前言Startup类相信大家都比较熟悉,在我们使用ASP.NET Core开发过程中经常用到的类&#xff0c;我们通常使用它进行IOC服务注册&#xff0c;配置中间件信息等。虽然它不是必须的&#xff0c;但是将这些操作统一在Startup中做处理&#xff0c;会在实际开发中带来许多方便。当我们…

【源码】常用的人脸识别数据库以及上篇性别识别源码

上一篇《使用ML.NET模型生成器来完成图片性别识别》发布后&#xff0c;很多朋友希望得到源码&#xff0c;这里附上地址&#xff1a;https://github.com/xin-lai/GenderRecognition常用的人脸数据库对于部分朋友说&#xff0c;找不到训练的数据&#xff0c;这里也给出部分数据&a…

程序员过关斩将--真的可以用版本号的方式来保证MQ消费消息的幂等性?

灵魂拷问MQ消息的消费为什么有时候要求幂等性&#xff1f;你们都说可以用版本号来解决幂等性消费&#xff1f;什么才是消息幂等性消费的根本性问题&#xff1f;随着系统的复杂性不断增加&#xff0c;多数系统都会引入MQ来进行解耦&#xff0c;其实从引入MQ的初衷来说&#xff0…

spring的钩子_spring提供的钩子,你知道哪些

俗话说得好“工欲善其事必先利其器”&#xff0c;现如今springboot与springcloud已成为快速构建web应用的利器。作为一个爪洼工程师&#xff0c;知道如下的spring扩展点&#xff0c;可能会让你编写出扩展性、维护性更高的代码。spring提供的钩子&#xff0c;你知道哪些bean的生…

.Net 5性能改进

起因在.Net Core跳过4.0,避免和先.Net Framework 4.0同名,版本号变为5.0,同时也不在叫.Net Core改为.Net 5(统一的叫法),先看看官方对.Net版本规划.本文主要是根据https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/ 翻译而来.不完全翻译.顺序也有所调…

开放数字世界中的复杂图数据挑战 —— 以教育与开源场景为例

摘要&#xff1a;开源开放的数字世界开始成为时代的潮流&#xff0c;云原生、数据中台、智能PRA开始成为数字世界中的新一代中流砥柱。随着第四范式的普遍流行&#xff0c;各个行业中的数字化转型都会带了海量的具有无限关联的复杂图数据。本报告将以教育与开源两个场景为例&am…

在IIS中部署SPA应用,多么痛的领悟!

目前公司的Web项目是SPA应用&#xff0c;采用前后端分离开发&#xff0c;所以有时也会倒腾Vue框架。“前后端应用最终以容器形态、在k8s中部署, 为此我搭建了基于Gitlab flow的Devops流程。在Devops实践中&#xff0c;容器部署成为良方和事实标准。但是在开发和自测阶段&#x…

mysql闪回工具下载_MySQL闪回工具之myflash 和 binlog2sql

实践利用binlog2sql查询两个binlog之间的SQL&#xff1a;必须是两个binlog日志&#xff0c;指定start-file和stop-filebinlog2sql -h127.0.0.1 -P3309 -udba -pxxxxxx -dsakila -t employee --start-filemysql-bin.000112 --stop-filemysql-bin.000113 > /tmp/db.sql利用bin…

MySQL大表优化方案

背景阿里云RDS FOR MySQL&#xff08;MySQL5.7版本&#xff09;数据库业务表每月新增数据量超过千万,随着数据量持续增加,我们业务出现大表慢查询,在业务高峰期主业务表的慢查询需要几十秒严重影响业务方案概述一、数据库设计及索引优化MySQL数据库本身高度灵活&#xff0c;造成…

使用Azure静态Web应用部署Blazor Webassembly应用

上一次演示了如何使用Azure静态web应用部署VUE前端项目&#xff08;使用Azure静态web应用全自动部署VUE站点&#xff09;。我们知道静态web应用支持VUE&#xff0c;react&#xff0c;angular等项目的部署。除了支持这些常见前端框架&#xff0c;静态web应用同样支持微软推出的最…

TIOBE 11 月榜单:Python 挤掉 Java,Java的下跌趋势确立了?

喜欢就关注我们吧&#xff01;TIOBE 公布了 2020 年 11 月的编程语言排行榜。Python 已成功跃居榜单第二名&#xff0c;本月排名率为 12.12%&#xff1b;Java 被挤到第三位&#xff0c;排名率降至 11.68%。自有 TIOBE 榜单以来&#xff0c;C 和 Java 之前一直占据着前两名的位置…

一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试

一&#xff1a;背景 1. 讲故事每次项目预交付的时候&#xff0c;总会遇到各种奇葩的坑&#xff0c;我觉得有必要梳理一下以及如何快速解决的&#xff0c;让后来人避避坑&#xff0c;这篇就聊聊自己的所闻所遇&#xff1a;我去&#xff0c;本地环境代码跑的哧溜&#xff0c;上了…