以Windows服务方式运行.NET Core程序

原文:以Windows服务方式运行.NET Core程序

在之前一篇博客《以Windows服务方式运行ASP.NET Core程序》中我讲述了如何把ASP.NET Core程序作为Windows服务运行的方法,而今,我们又遇到了新的问题,那就是:我们的控制台程序,也就是普通的.NET Core程序(而不是ASP.NET Core程序)如何以服务的方式运行呢?

这个问题我们在.NET Core之前早就遇到过,那是是.NET Framework的时代(其实距今也没多远啦),我们是用一个第三方的组件——Topshelf,来解决这个问题的,Topshelf的官网是:http://topshelf-project.com/,它的使用很简单,官网上有具体的描述,对于一个普通的控制台程序而言(通常是一个不需要图形界面的服务),开发和调试的时候,把它当做一个普通的控制台程序来使用,十分方便;而实际部署的时候,通过传入不同的命令行参数,可以使它有了新的行为:安装Windows服务、运行Windows服务、停止/重启Windows服务或者卸载Windows服务。进入跨平台的.NET Core时代之后,Topshelf自然有了支持.NET Core的版本,使用方法与之前的类似,具体在此不表了,因为接下来我们根本不打算使用它!

现在我想要的是:不要引入任何组件,不要对现在控制台程序进行任何修改(ASP.NET Core程序也是控制台程序),开发调试时候不要进行任何复杂的参数配置,一切照旧,仅仅是在部署阶段,把程序当做Windows服务去运行。——你嘚讲吼不吼?

要达到这个目标,就要借助一个神器了,此神器为NSSM,Non-Sucking Service Manager,名字有点拗口,翻译成中文就是:不嗝屁服务管理器。

NSSM的官网是:https://nssm.cc/,十分简陋,但程序功能可是非常强大和全面的,下面我来一步步演示它如何使用。

1,先构建一个简单的服务程序

构建一个简单的服务程序,程序功能描述:程序没有图形界面,仅仅是定时记录一些日志(5秒钟写一下日志),在用户按下<Ctrl>+<C>的时候,程序退出。功能明确,Okay,let's get down to work.

1. 创建一个.NET Core Application,叫MyService

2. Nuget引入Quartz和NLog.Extensions.Logging,一个用来做定时任务,另一个用来log

3. 另外,程序使用了依赖注入,还需要用Nuget引入Microsoft.Extensions.DependencyInjection

4. 给项目增加NLog.Config配置文件,内容是

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"autoReload="true"throwExceptions="false"internalLogLevel="Off"><variable name="theLayout" value="${date:format=HH\:mm\:ss.fff} [${level}][${logger}] ${callsite:className=False:fileName=True:methodName=False} ${message} ${onexception:${newline}}${exception:format=Message,ShortType,StackTrace:innerFormat=Message,ShortType,StackTrace:separator=\r\n:innerExceptionSeparator=\r\n---Inner---\r\n:maxInnerExceptionLevel=5}"/><targets><target name="asyncFile" xsi:type="AsyncWrapper"><target name="logfile" xsi:type="File" fileName="${basedir}/log/${shortdate}.log" layout="${theLayout}" encoding="UTF-8" /></target><target name="debugger" xsi:type="Debugger" layout="${theLayout}" /><target name="console" xsi:type="Console" layout="${theLayout}" /><target name="void" xsi:type="Null" formatMessage="false" /></targets><rules><logger name="Quartz.*" minlevel="Trace" maxlevel="Info" writeTo="void" final="true" /><logger name="*" minlevel="Debug" writeTo="asyncFile" /><logger name="*" minlevel="Trace" writeTo="debugger"/><logger name="*" minlevel="Trace" writeTo="console"/></rules>
</nlog>

还要注意的是这个文件必须复制到生成目录去以便程序运行时候能够加载到。

5. 增加MyServiceJobFactory.cs

using Quartz;
using Quartz.Spi;
using System;
namespace MyService {class MyServiceJobFactory : IJobFactory {protected readonly IServiceProvider _container;public MyServiceJobFactory(IServiceProvider container) {_container = container;}public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) {return _container.GetService(bundle.JobDetail.JobType) as IJob;}public void ReturnJob(IJob job) {}}
}

6. 增加PeriodLoggingJob.cs

using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Threading.Tasks;
namespace MyService {class PeriodLoggingJob  : IJob {private readonly ILogger<PeriodLoggingJob> _logger;public PeriodLoggingJob(ILogger<PeriodLoggingJob> logger, IServiceProvider serviceProvider) {_logger = logger;}private void DoLoggingJob() {_logger.LogInformation("logging...");}public Task Execute(IJobExecutionContext context) {try {DoLoggingJob();}catch (Exception ex) { //必须妥善处理好定时任务中发生的异常_logger.LogError(ex, "执行定时任务发生意外错误");}returnTask.CompletedTask;}}
}

7. Program.cs的完整内容如下

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using System;
using System.Collections.Specialized;
using System.IO;
using System.Threading;
namespace MyService {class Program {//注册各种服务static void RegisterServices(IServiceCollection services) {//日志相关services.AddSingleton<ILoggerFactory, LoggerFactory>();services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));services.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace));//定时任务相关services.AddSingleton<IJobFactory, MyServiceJobFactory>();services.AddSingleton<PeriodLoggingJob>();}static void Main(string[] args) {//注册退出事件处理(响应<Ctrl>+<C>)ManualResetEvent exitEvent = new ManualResetEvent(false);Console.CancelKeyPress += delegate (object sender, ConsoleCancelEventArgs e) {e.Cancel = true;exitEvent.Set();};//处理其它程序关闭事件(如kill),使得程序可以优雅地关闭AppDomain.CurrentDomain.ProcessExit += (sender, e) => { exitEvent.Set(); };//容器生成ServiceCollection services = new ServiceCollection();RegisterServices(services);using (ServiceProvider container = services.BuildServiceProvider()) {//日志初始化var loggerFactory = container.GetRequiredService<ILoggerFactory>();loggerFactory.AddNLog(new NLogProviderOptions {CaptureMessageTemplates = true,CaptureMessageProperties = true});string nlogConfigFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config");NLog.LogManager.LoadConfiguration(nlogConfigFile);//记录启动日志ILogger<Program> logger = container.GetService<ILogger<Program>>();logger.LogInformation("MyService启动.");//定时任务配置NameValueCollection props = new NameValueCollection { { "quartz.serializer.type", "binary" } };StdSchedulerFactory schedulerFactory = new StdSchedulerFactory(props);IScheduler scheduler = schedulerFactory.GetScheduler().Result;scheduler.JobFactory = container.GetService<IJobFactory>();//每天1:00执行APP状态更新任务ITrigger periodLoggingJobTrigger = TriggerBuilder.Create().WithIdentity("PeriodLoggingJobTrigger").StartNow().WithSimpleSchedule(x=>x.WithIntervalInSeconds(5).RepeatForever()).Build();IJobDetail checkPasswordOutOfDateJob = JobBuilder.Create<PeriodLoggingJob>().WithIdentity("PeriodLoggingJob").Build();scheduler.ScheduleJob(checkPasswordOutOfDateJob, periodLoggingJobTrigger);//开启定时服务
                scheduler.Start();//----------------------------------------↑↑↑ 程序开始 ↑↑↑----------------------------------------
                exitEvent.WaitOne();//----------------------------------------↓↓↓ 程序结束 ↓↓↓----------------------------------------//定时任务结束
                scheduler.Shutdown();//记录结束日志logger.LogInformation("MyService停止.");}}}
}

这就是整个服务程序的完整内容,本来我可以提供一个更简单的程序,这里啰里啰嗦写了这么一大堆,目的还是让初学者更加清楚.NET Core的程序结构和运行方式。其中内容包括:NLog的使用、Quartz的使用、容器及依赖注入的入门例子、如何处理程序关闭事件等,也许你想问“为什么要引入Quartz,搞这么复杂,弄个Timer不行吗?”当然行,但Quartz更强大,而且更适合给大家演示容器与依赖注入的使用。

8. 试运行程序

运行这个程序,输出几条日志信息后,以<Ctrl>+<C>来结束程序的运行,这样会在程序目录下产生log目录及日志文件,文件的内容大致如下:

19:03:37.117 [Info][MyService.Program] (d:\work\MyService\MyService\Program.cs:55) MyService启动.
19:03:37.637 [Info][MyService.PeriodLoggingJob] (d:\work\MyService\MyService\PeriodLoggingJob.cs:15) logging...
19:03:42.536 [Info][MyService.PeriodLoggingJob] (d:\work\MyService\MyService\PeriodLoggingJob.cs:15) logging...
19:03:47.535 [Info][MyService.PeriodLoggingJob] (d:\work\MyService\MyService\PeriodLoggingJob.cs:15) logging...
19:03:49.293 [Info][MyService.Program] (d:\work\MyService\MyService\Program.cs:80) MyService停止.

9. 发布程序

选择publish,在publish的目标目录下产生一堆文件,将这些文件复制到D:\Service\MyService目录下,一会儿我们要用到这个目录。

2,NSSM配置

首先要获取NSSM程序,当然是要到官网下载,版本选择最新版,尽管它声称是pre-release版,但功能杠杠的,没有任何影响,而正式版(非pre-release)则是2014年的了,太旧了。下载下来后找到对应的exe文件,叫nssm.exe。(注意有32位版和64位版的分别)
它是个绿色软件,不需要安装,仅此一个exe文件,把这个文件复制到C:\Windows\System32目录下,之后经常要用。
在Windows命令行中直接敲nssm,会出现它的帮助提示。

1. 安装服务

>nssm install MyService
出现配置界面(注意,需要管理员权限)
配置选项比较多,这是我的配置,供参考:
点“Install service”即将服务安装好了。我们打开Windows服务来查看所安装的服务:
服务已经安装完毕,一切准备就绪。
2. 启动服务
>nssm start MyService

其它一些操作
其实不用我说大家也应该知道了:

  • nssm status MyService 查看服务状态
  • nssm stop MyService 停止服务
  • nssm restart MyService 重启服务
  • nssm edit MyService 重新配置服务的参数
  • nssm remove MyService 删除服务

其余的请自行参考nssm的使用手册。

注意事项:需要用管理员身份来执行上面这些命令,否则会出现访问拒绝的错误。

3,分享一些想法

2018年快过去了,回顾这一年来,我觉得我在公司所做的最大且重要的一件事情就是推动了.NET Core的应用,将能迁移的.NET Framework的程序都迁移至.NET Core了,为什么要这么干?最最主要的原因当然是要跨平台,原先ASP.NET开发的网站,只能运行于Windows平台,它们得依赖于IIS!Windows(作为服务器)本身就是一个非常复杂的系统,有着各种令人眼花缭乱的配置,加上IIS,就更加令人感到困惑,我同意IIS是功能强大的服务器程序,但它真的过于复杂,设计不合理,很难用,让我等菜鸟频频掉到它的坑里爬不出来。IIS并不是一个能够自由选择版本的软件,它的版本通常认为与Windows操作系统绑定,微软官方并不建议安装与Windows操作系统原生版本不一致的IIS,所以现在甚至还有公司继续在用IIS6,而各个版本的IIS的行为却不尽相同,默认IIS并不带安装ASP.NET组件,所以在Windows系统和IIS刚部署好的时候,想直接运行ASP.NET网站居然还不行,要自己去安装ASP.NET的支持,完成后还需要使用一条额外的命令来注册ASP.NET组件,另外还可能遇到稀奇古怪的问题,大多数问题可以通过安装若干个补丁解决(如ASP.NET MVC的路由不起作用导致网站无法访问的问题),而有时则不会那么顺利,你得仔细看看这些补丁是否符合当前操作系统及IIS版本,甚至操作系统的语言版本也会影响你所要安装的补丁。IIS与ASP.NET程序之间的关系也是令人很懵逼,我想让我的ASP.NET程序自始至终运行着就是做不到,尽管应用程序池里似乎有这个选项,我在StackOverflow上针对相关问题进行过讨论,有不少人顶我,但也有人说不行(我猜跟IIS版本还有关系),ASP.NET程序空闲一段时间后便被IIS踢掉——即便你的主机不差内存,你无法肯定IIS一运行你的程序就跟着跑起来,也无法肯定你的程序什么时候在运行,什么时候被踢掉,这是个类似薛定谔的猫的问题,你的ASP.NET程序就通常处于这么一种“叠加态”,你得看一看才知道确切它是否在运行,这一看,才使得程序从“叠加态”坍缩为“生态”或“死态”,且从“死态”转入“生态”还需要耗费好些时间,表现为第一次打开页面时候的长时间卡顿,跟客户演示系统,有时候会很尴尬。我曾经为了让程序不被IIS踢掉,还手工写了一个KeepAlive的小程序,定时去get我的网站的首页,实在奇葩。微软对此的解释是:IIS并不是为long-term程序设计的,你想在IIS里做一个准时的定时服务,那是相当不妥,根本不是为这种事情设计的,所以不好用不能怪我。我承认这当然是一种设计,但ASP.NET网站除了提供网页之外,跑一些后台服务也应该是很正常的吧?没办法,于是我将服务和网站分开,中间用总线沟通,听起来很cool?——其实这是一段悲伤的往事,不过说来话长,以后有机会再提了。.NET Core出现了,ASP.NET Core也和它一起到来,2.0版开始就是一个很完善的版本,我想是时候上了,这是工作量很大的差事,但为了将来更好的发展,我们必须经历这个艰难的爬坡,所幸的是现在一切都已转入正轨,我预想的目的达到了。

.NET Core的一大特点就是程序都可以独立运行,包括ASP.NET Core程序,不再依赖于IIS,我可以根据业务的需要,将系统划分为多个模块,方便开发分工和测试,这些模块甚至不需要部署在同一台主机上,极大提高了灵活性。一般来说,我还是推荐将程序部署至Linux环境,理由依旧是Linux作为服务器操作系统的使用体验远远好于Windows,Windows实在太过复杂了!但也有例外,如果遇到缺乏Linux支持技术的客户的情况,那就把程序部署到他们的Windows主机上吧,无所谓,反正.NET Core是跨平台的。

不知这是不是我2018年的最后一篇博客,如果是,上面这段文字就算是我对今年自己的主要工作总结吧。

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

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

相关文章

好用的shell工具_精选5个酷毙的Python工具

来自&#xff1a;Python之禅工欲善其事必先利其器&#xff0c;一个好的工具能让起到事半功倍的效果&#xff0c;Python社区提供了足够多的优秀工具来帮助开发者更方便的实现某些想法&#xff0c;下面这几个工具给我的工作也带来了很多便利&#xff0c;推荐给追求美好事物的你。…

承载辉煌历史 畅想无线未来

看了JustDI的文章“手机也能当电脑用&#xff1f;&#xff0d;&#xff0d;谈谈未来智能手机操作系统的走向”&#xff0c;作为嵌入式爱好者&#xff0c;我也想谈谈自己的看法。首先&#xff0c;从网络发展的角度看&#xff0c;移动互联网的宽带化&#xff0c;宽带互联网的移动…

6款国内外SNS开源软件 搭建社交网站利器

SNS(Social Network Service)&#xff0c;有时称为社交网络&#xff0c;有时称为社会化网络&#xff0c;专指旨在帮助人们建立社会性网络的互联网应用服务。如果对SNS概念还很模糊&#xff0c;说到人人网、开心网你就明白了。 去年360圈、蚂蚁网接连关站给SNS前景蒙上一层阴影&…

aop实现原理_从宏观的实现原理和设计本质入手,带你理解 AOP 框架的原理

点击上方“Java知音”&#xff0c;选择“置顶公众号”技术文章第一时间送达&#xff01;作者&#xff1a;FeelsChaoticjuejin.im/post/5c57b2d5e51d457ffd56ffbb前言本文将从另一个角度讲解 AOP&#xff0c;从宏观的实现原理和设计本质入手。大部分讲 AOP 的博文都是一上来就罗…

xxl-job源码分析

xxl-job源码分析 xxl-job 系统说明 安装 安装部署参考文档&#xff1a;分布式任务调度平台xxl-job 功能 定时调度、服务解耦、灵活控制跑批时间&#xff08;停止、开启、重新设定时间、手动触发&#xff09; XXL-JOB是一个轻量级分布式任务调度平台&#xff0c;其核心设计目标是…

定制jQuery File Upload为微博式单文件上传

原文链接&#xff1a;http://avnpc.com/pages/single-file-upload-component-by-jquery-file-upload jQuery File Upload是一个非常优秀的上传组件&#xff0c;主要使用了XHR作为上传方式&#xff0c;并且利用了相当多的现代浏览器功能&#xff0c;所以可以实现诸如批量上传、超…

vb趣味编程弹球小游戏_最好玩的微信小游戏集合,总有一款是你没玩过的

大家好&#xff0c;这里是小雅龙生活趣味时间&#xff0c;自从17年微信推出小游戏程序以来&#xff0c;微信小游戏行业可谓是炙手可热&#xff0c;知道2019年不断有许许多多的微信小游戏如雨后春笋般的生根发芽。下面就由我带大家来看看今年最好玩&#xff0c;最受欢迎的微信小…

Golang——垃圾回收GC(2)

1 垃圾回收中的重要概念 1.1 定义 In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objects that are no longer in use by the pro…

java gui框架_推荐!程序员整理的Java资源大全

构建这里搜集了用来构建应用程序的工具。Apache Maven&#xff1a;Maven使用声明进行构建并进行依赖管理&#xff0c;偏向于使用约定而不是配置进行构建。Maven优于Apache Ant。后者采用了一种过程化的方式进行配置&#xff0c;所以维护起来相当困难。Gradle&#xff1a;Gradle…

帆软报表(finereport)控件背景色更改

setTimeout(function() {$(.fr-trigger-btn-up).css({"background-color": "#003399" });}, 100); 转载于:https://www.cnblogs.com/Williamls/p/11571586.html

mybatis 大于_酸爽!IDEA 中这么玩 MyBatis,让编码速度飞起!

作者&#xff1a;Orsoncnblogs.com/java-class/p/6237564.html1. 搭建 MyBatis Generator 插件环境a. 添加插件依赖 pom.xmlb. 配置文件 generatorConfig.xmlc. 数据库配置文件 jdbc.propertiesd. 配置插件启动项2.项目实战a. 比如在一个项目 我们要删除某个小组下某个用户的信…

scatter函数_matplotlib.pyplot常用函数scatter讲解大全(三)

前言这篇文章再来总结一个常用画图函数scatter-散点图。参数常用参数示例import matplotlib.pyplot as plt import numpy as np#导入需要的包 datanp.random.multivariate_normal([0,1],[[1,0],[0,1]],200)#准备数据&#xff0c;二维正态分布plt.rcParams["axes.unicode_m…

c++万能头文件_初学Python,与C对比

✎背景学了一学年的C的基础&#xff0c;下学年开课Python&#xff0c;现在正在自学中...C也不是不学了&#xff0c;而是之前买了一本《CPrimer》在学校里&#xff0c;就准备先学一下Python&#xff0c;下学期利用自由时间接着学习C。这里分析了一下二者的优缺点&#xff0c;供大…

本地无法启动MySQL服务,报的错误:1067,进程意外终止---解决

原文链接&#xff1a;http://blog.csdn.net/shenhonglei1234/article/details/5928873 在本地计算机无法启动MYSQL服务错误1067进程意外终止 这种情况一般是my.ini文件配置出错了 首先找到这个文件&#xff1a; 默认安装路径 C:/Program Files/MySQL/MySQL Server 5.1/my.ini …

团队升级

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/yulongblog/blog/2988702

Css3: gradient背景渐变

Css3: gradient背景渐变 原文链接&#xff1a;http://kk073000.blog.163.com/blog/static/34826942012123111322691/ css3实现了背景渐变。 <gradient> [ <linear-gradient> | <radial-gradient> | <repeating-linear-gradient> | <repeating-r…

聚类 python_python中实现k-means聚类算法详解

算法优缺点&#xff1a; 优点&#xff1a;容易实现 缺点&#xff1a;可能收敛到局部最小值&#xff0c;在大规模数据集上收敛较慢 使用数据类型&#xff1a;数值型数据 算法思想 k-means算法实际上就是通过计算不同样本间的距离来判断他们的相近关系的&#xff0c;相近的就会放…

python笔试常见题

1、冒泡排序&#xff1a; 冒泡排序算是最基本的python算法了。也算python面试遇到问的最多的了。 如果是封装成函数。代码如下&#xff1a; 如果初始就一个字典。那么代码为&#xff1a; 冒泡排序的本质就是两两比较。根据结果调换位置。最终达到一个排序的效果。 注&#xff1…

centos 关闭防火墙_CentOS7操作系统下如何关闭防火墙

centos系统如果不关闭防火墙在使用中会遇到不少问题&#xff0c;而且centos7和centos6关闭防火墙的方式不一样。centos6:1.永久性生效&#xff0c;重启后不会复原开启&#xff1a; chkconfig iptables on关闭&#xff1a; chkconfig iptables off2.即时生效&#xff0c;重启后复…

Apache的认证、授权、访问控制

原文链接&#xff1a; http://man.chinaunix.net/newsoft/Apache2.2_chinese_manual/howto/auth.html Apache认证、授权、访问控制 认证(Authentication)是指任何识别用户身份的过程。授权(Authorization)是允许特定用户访问特定区域或信息的过程。 相关模块和指令 认证和授权…