控制台程序秒变Windows服务(Topshelf)
项目中有些时候需要写服务,一般我们都是先创建控制台程序,测试,运行,成功之后再创建windows服务程序,这样好麻烦啊,有没有简单的控制台程序直接变成widnows服务,经过查找,找到了topshelf。topshelf是一个托管使用.net框架编写的服务的框架,简化了服务的创建,允许开发人员创建一个简单的控制台应用程序,可以使用topshelf作为服务安装。
topshelf介绍
topshelf是一个托管使用.net框架编写的服务的框架。简化了服务的创建,允许开发人员创建一个简单的控制台应用程序,可以使用topshelf作为服务安装。原因很简单:调试控制台应用程序比使用服务要容易得多。一旦应用程序经过测试并可以投入生产,topshelf便可以轻松地将应用程序作为服务进行安装。这是一个开源的项目,项目地址,nuget上可以搜到响应的库。
topshelf使用
1.创建控制台程序
2.安装topshelf,在nuget上搜下
3.安装nlog、nlog.config,目的是为了看日志,可以清楚的知道服务在运行,可以不要
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" xsi:schemalocation="http://www.nlog-project.org/schemas/nlog.xsd nlog.xsd" autoreload="true" throwexceptions="false" internalloglevel="off" internallogfile="c:\temp\nlog-internal.log"> <!-- optional, add some variables https://github.com/nlog/nlog/wiki/configuration-file#variables --> <variable name="myvar" value="myvalue"/> <!-- see https://github.com/nlog/nlog/wiki/configuration-file for information on customizing logging rules and outputs. --> <targets> <!-- add your targets here see https://github.com/nlog/nlog/wiki/targets for possible targets. see https://github.com/nlog/nlog/wiki/layout-renderers for the possible layout renderers. --> <!-- write events to a file with the date in the filename. --> <target xsi:type="file" name="f" filename="${basedir}/logs/${shortdate}.log" layout="${longdate} ${uppercase:${level}} ${message}" /> </targets> <rules> <!-- add your logging rules here --> <!-- write all events with minimal level of debug (so debug, info, warn, error and fatal, but not trace) to "f" --> <logger name="*" minlevel="debug" writeto="f" /> </rules> </nlog>
4.代码实现
class program { private static readonly logger logger = logmanager.getlogger("program"); static void main(string[] args) { logger.info($"main主程序{datetime.now}"); var rc = hostfactory.run(x => //1.启动程序 { logger.info($"主程序{datetime.now}"); x.service<towncrier>(s => //2.设置服务类型 { s.constructusing(name => new towncrier()); //3.创建服务实例 s.whenstarted(tc => tc.start()); //4.启动程序 s.whenstopped(tc => tc.stop()); //5.停止程序 }); x.runaslocalsystem(); //6.本地系统运行 x.setdescription("超级简单的windows服务"); //7.windows服务的描述 x.setdisplayname("simplewindowsservice 服务"); //8.windows服务的显示名称 x.setservicename("simplewindowsservice"); //9.windows服务的服务名称 }); var exitcode = (int)convert.changetype(rc, rc.gettypecode()); //11.退出程序 environment.exitcode = exitcode; } } public class towncrier { private static readonly logger logger = logmanager.getlogger("logtest"); readonly timer _timer; //system.timers不要引用错误 public towncrier() { _timer = new timer(1000) { autoreset = true }; _timer.elapsed += (sender, eventargs) => { console.writeline($"it is {datetime.now} and all is well"); logger.info($"it is {datetime.now} and all is well"); }; } public void start() { _timer.start(); } public void stop() { _timer.stop(); } }
必须有启动方法(start)和停止方法(stop)
4. 运行控制台程序,也可以是调试,效果如下
5. 程序调试运行成功,一切ok,现在可以安装服务了,以管理员身份运行cmd找到对应路径,开始安装
安装:simplewindowsservice.exe install
查看服务
启动:simplewindowsservice.exe start
服务已经运行,查看运行情况,日志
卸载:simplewindowsservice.exe uninstall
可以看到服务已经没有了
停止:simplewindowsservice.exe stop
停止之后可以再次启动,这个功能不介绍了,卸载服务的时候会调用这个方法。
topshelf的其他功能
安装动作之前:topshelf允许指定在安装服务之前执行的操作。请注意,只有在安装服务时才会执行此操作。
hostfactory.new(x => { x.beforeinstall(settings => { ... }); });
安装动作后:topshelf允许指定在安装服务后执行的操作。请注意,只有在安装服务时才会执行此操作。
hostfactory.new(x => { x.afterinstall(settings => { ... }); });
在卸载操作之前:topshelf允许指定在卸载服务之前执行的操作。请注意,只有在卸载服务时才会执行此操作。
hostfactory.new(x => { x.beforeuninstall(() => { ... }); });
卸载操作后:topshelf允许指定在卸载服务后执行的操作。请注意,只有在卸载服务时才会执行此操作。
hostfactory.new(x => { x.afteruninstall(() => { ... }); });
异常:为服务运行时抛出的异常提供回调。此回调不是处理程序,不会影响topshelf已提供的默认异常处理。它旨在提供对触发外部操作,日志记录等的抛出异常的可见性。
hostfactory.new(x => { x.onexception(ex => { // do something with the exception }); });
其他的一些功能,如果需要可以查看
定时任务的服务
一般的服务都没有这么简单,一般都需要定时任务,这里的定时任务服务用到了fluentscheduler,fluentscheduler定时器介绍 ,这篇文章对fluentscheduler定时器进行了详细的介绍,这里不再介绍,只展示使用。
/// <summary> /// 用myschedule的任务定时功能 /// </summary> public class myjob { private static readonly logger logger = logmanager.getlogger("myjob"); public myjob() { } public void start() { logger.info($"myschedule启动 {datetime.now}"); jobmanager.initialize(new myschedule()); } public void stop() { logger.info($"myschedule停止 {datetime.now}"); jobmanager.stop(); } } /// <summary> /// 定时器 /// </summary> public class myschedule : registry { private static readonly logger logger = logmanager.getlogger("myschedule"); public myschedule() { setnewsschedule(); } /// <summary> /// 设置任务 /// </summary> private void setnewsschedule() { //获取链接发送邮件 schedule(() => { logger.info($"myschedule运行 {datetime.now}"); } ).torunnow().andevery(1000).milliseconds(); } }
控制台程序调用
class program { private static readonly logger logger = logmanager.getlogger("program"); static void main(string[] args) { logger.info($"main主程序{datetime.now}"); var rc = hostfactory.run(x => //1.启动程序 { logger.info($"主程序{datetime.now}"); x.service<myjob>(s => //2.设置服务类型 { s.constructusing(name => new myjob()); //3.创建服务实例 s.whenstarted(tc => tc.start()); //4.启动程序 s.whenstopped(tc => tc.stop()); //5.停止程序 }); x.runaslocalsystem(); //6.本地系统运行 x.setdescription("超级简单的windows服务"); //7.windows服务的描述 x.setdisplayname("simplewindowsservice 服务"); //8.windows服务的显示名称 x.setservicename("simplewindowsservice"); //9.windows服务的服务名称 }); var exitcode = (int)convert.changetype(rc, rc.gettypecode()); //11.退出程序 environment.exitcode = exitcode; } }
服务安装两步走,管理员cmd,simplewindowsservice.exe install, simplewindowsservice.exe start
总结
1. 写控制台程序
2.管理员cmd,simplewindowsservice.exe install
3.启动服务simplewindowsservice.exe start
4.卸载服务simplewindowsservice.exe uninstall