关于使用Topshelf创建服务

关于使用Topshelf创建服务

目录

志铭-2021年12月13日 21:21:36

0. 背景说明

我为什么要创建windows服务?因为有一个操作需要我定时执行,这个操作可以是定时的数据同步或是周期性的某种操作

之前都是按照以下方式实现的:

  • 若是数据库层面定时任务,则创建一个定时作业

  • 其他操作,则创建一个控制程序,使用windows服务自带的任务计划程序,定时周期性执行该控制台程序

而目前出现了问题:

  • 数据库作业,因为我使用的数据库账号没有看不到“SQL Server代理”,就是没有权限创建作业

  • 控制台程序因为引用其他的dll文件所以偶尔出现被杀毒软件删除,需要加入白名单。但也意味着其不够健壮。



1. 使用Topshelf组件创建Windows服务

使用Topshelf可以便捷的创建一个Windows服务

该组件可以通过创建一个控制台程序,实现一个windows服务

该组件可以配合Log4net和Quartz.net实现记录日志和定时执行

Nuget: Install-Package Topshelf -Version 4.3.0 Install-Package Topshelf.Log4Net -Version 4.3.0(注:依赖于Log4net) Install-Package Topshelf.Quartz -Version 0.4.0.1(注:依赖于Quartz)

1.1 依赖Quartz.net实现定时任务

定义任务类,实现Ijob接口

//using log4net;//using Quartz;/// <summary>/// 任务类,定义我们需要调度的任务/// </summary>public class MyJob : IJob{    private readonly ILog _logger = LogManager.GetLogger(typeof(MyJob));    public void Execute(IJobExecutionContext context)    {        //todo:你所期望实现定时执行的操作        //计入日志        _logger.InfoFormat("任务执行成功");    }}

简单的安装单例模式封装一个调度器类,
这里调度规则简单示例为每隔5s执行一次

//using Quartz;//using Quartz.Impl;/// <summary>/// 任务调度器类,用于创建调度器并配置调度计划/// </summary>public class MyScheduler{    private static readonly MyScheduler myScheduler = new MyScheduler();    //调度器    public IScheduler scheduler { private set; get; }    //任务    public IJobDetail job { private set; get; }    //触发器    public ISimpleTrigger trigger { private set; get; }    private MyScheduler()    {        scheduler = StdSchedulerFactory.GetDefaultScheduler();        job = JobBuilder.Create<MyJob>().Build();        trigger = TriggerBuilder.Create()            .StartNow()            .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())//配置每隔5s执行一次,永久执行下去            .WithIdentity("trigger", "group").Build() as ISimpleTrigger;        scheduler.ScheduleJob(job, trigger);    }    //单例模式,用于返回单例对象    public static MyScheduler GetMyScheduler()    {        return myScheduler;    }}

1.2 依赖于Topshelf创建服务类

//using Quartz;//using Topshelf;/// <summary>/// 服务类,实现相应接口,定义服务启动和停止执行的操作/// </summary>public class MyServiceRunner : ServiceControl, ServiceSuspend{    private readonly IScheduler scheduler = MyScheduler.GetMyScheduler().scheduler;    #region ServiceControl接口需要实现的方法    public bool Start(HostControl hostControl)    {        scheduler.Start();        return true;    }    public bool Stop(HostControl hostControl)    {        scheduler.Shutdown(false);        return true;    }    #endregion    #region ServiceSuspen接口需要实现的方法    public bool Continue(HostControl hostControl)    {        scheduler.ResumeAll();        return true;    }    public bool Pause(HostControl hostControl)    {        scheduler.PauseAll();        return true;    }    #endregion}

1.3 log4net的配置文件log4net.config

这里通过配置将日志信息写入到数据库中

  • 日志表建表语句
    CREATE TABLE [dbo].[Log4net] ([Id] [int] IDENTITY (1, 1) NOT NULL,[Date] [datetime] NOT NULL,[Thread] [varchar] (255) NOT NULL,[Level] [varchar] (50) NOT NULL,[Logger] [varchar] (255) NOT NULL,[Message] [varchar] (4000) NOT NULL,[Exception] [varchar] (2000) NULL)
  • 注意数据库的连接字符串需要单独写在项目的配置文件中
  • 右键log4net.config-->属性-->复制的输出目录:始终复制
<?xml version="1.0" encoding="utf-8" ?><configuration><!-- ...................................为Log4Net添加的配置.....开始.................................-->    <configSections>    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />    </configSections>    <log4net>    <root>    <level value="ALL"></level>    <appender-ref ref="AdoNetAppender"></appender-ref>    </root>    <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">    <bufferSize value="1" />    <connectionType value="System.Data.SqlClient.SqlConnection,System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />    <connectionStringName value="ConnectionStringLogging" />    <commandText value="INSERT INTO Log4net ([Date],[Thread],[Level],[Logger],[Message],[Exception])         VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />    <parameter>    <parameterName value="@log_date" />    <dbType value="DateTime" />    <layout type="log4net.Layout.RawTimeStampLayout" />    </parameter>    <parameter>    <parameterName value="@thread" />    <dbType value="String" />    <size value="255" />    <layout type="log4net.Layout.PatternLayout">    <conversionPattern value="%thread" />    </layout>    </parameter>    <parameter>    <parameterName value="@log_level" />    <dbType value="String" />    <size value="50" />    <layout type="log4net.Layout.PatternLayout">    <conversionPattern value="%level" />    </layout>    </parameter>    <parameter>    <parameterName value="@logger" />    <dbType value="String" />    <size value="255" />    <layout type="log4net.Layout.PatternLayout">    <conversionPattern value="%logger" />    </layout>    </parameter>    <parameter>    <parameterName value="@message" />    <dbType value="String" />    <size value="4000" />    <layout type="log4net.Layout.PatternLayout">    <conversionPattern value="%message" />    </layout>    </parameter>    <parameter>    <parameterName value="@exception" />    <dbType value="String" />    <size value="2000" />    <layout type="log4net.Layout.ExceptionLayout" />    </parameter>    </appender>    </log4net><!-- ...................................为Log4Net添加的配置.....结束.................................--></configuration>

1.4 主函数中配置服务信息

static void Main(string[] args){    //这里读取log4net.config    log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomainBaseDirectory + "log4net.config"));    HostFactory.Run(x =>    {        x.UseLog4Net();        x.Service<MyServiceRunner>();        x.SetDescription("服务描述");        x.SetDisplayName("服务显示名称");        x.SetServiceName("服务名称");        x.EnablePauseAndContinue();    });}

1.5 安装服务

编译后得到的控制台程序 xxx.exe,

管理员的身份运行命令行程序,跳转到程序所在路径,执行该命令

xxx.exe install

win+R:services.msc 打开服务管理器,找到安装的服务并右键启动

找到自己安装的服务,可以右键属性设置其他一些功能,如服务失败后的操作等

卸载该服务,则先在停止,在按照安装的方法,管理员身份打开命令行,跳转程序路径,执行

xxx.exe uninstall


3. Demo源码下载及参考文档

若是简单的周期执行,也可以不使用Quartz .net ,而是使用System.Timers.Timer对象实现间隔一定的时间执行一次任务
而且可以抛弃Log4net,只是简单的使用 System.IO.StreamWriter在服务本地简单的记录一个日志文本
该功能就是Topshelf官方文档中的最简的入门示例

  • 最简Demo源码下载

  • 上文中Demo源码下载2

  • Topshelf官方文档

  • Quartz.net官方文档

  • Log4net官方文档

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部