伪类实现徽标
570 2023-04-03 03:57:10
我为什么要创建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)
定义任务类,实现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; }}
//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}
这里通过配置将日志信息写入到数据库中
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)
<?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>
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(); });}
编译后得到的控制台程序 xxx.exe,
以管理员的身份运行命令行程序,跳转到程序所在路径,执行该命令
xxx.exe install
win+R:services.msc 打开服务管理器,找到安装的服务并右键启动
找到自己安装的服务,可以右键属性设置其他一些功能,如服务失败后的操作等
卸载该服务,则先在停止,在按照安装的方法,管理员身份打开命令行,跳转程序路径,执行
xxx.exe uninstall
若是简单的周期执行,也可以不使用Quartz .net ,而是使用System.Timers.Timer对象实现间隔一定的时间执行一次任务
而且可以抛弃Log4net,只是简单的使用 System.IO.StreamWriter在服务本地简单的记录一个日志文本
该功能就是Topshelf官方文档中的最简的入门示例
最简Demo源码下载
上文中Demo源码下载2
Topshelf官方文档
Quartz.net官方文档
Log4net官方文档