这周接受到一个新的需求:一天内分时间段定时轮询一个第三方WebAPI,并保存第三方WebAPI结果。

需求分析:分时段、定时开启、定时结束、轮询。主要工作集中在前三个上,轮询其实就是个Http请求,比较好解决。

技术选型:

  1、最简单的方式:Windows Service、Timer、HttpClient。
  2、B格高点的方式:Topshelf、Quartz.NET、HttpClient。
之所以选用第二种方式的原因:

  1、Windows Service尝试写了一个,发现附加进程调试确实麻烦,而且以后若是需求变更,还需要重新调试发布Windows Service

  2、Timer需要在项目中建立多个,区分起来着实麻烦

  3、刚好在学习使用Quartz.NET,打算过段时间做个MVC版本的调度任务管理系统

  4、经过查找cnblog发现,使用Topshelf可以用基于Console的模式先编写、调试程序,等调试通过后,用Topshelf命令即可完成Windows Service安装,据说还可以在Linux上通过Mono安装,也算是可以支持跨平台的咯(*^_^*)或许也可以通过制作Docker镜像来实现。

Show Code:

1、添加依赖Nuget包:Topshelf、Topshelf.Log4Net、Quartz、Common.Logging、Common.Logging.Core、Common.Logging.Log4Net1211、log4Net

2、创建ServiceRunner.cs类,继承ServiceControl, ServiceSuspend,这是为了用Topshelf的Start()、Stop()、Continue()、Pause()来分别执行Quartz任务调度的Start()、Shutdown()、ResumeAll()、PauseAll()方法

     public class ServiceRunner : ServiceControl, ServiceSuspend
{
private readonly IScheduler scheduler;
public ServiceRunner()
{
scheduler = StdSchedulerFactory.GetDefaultScheduler();
}
public bool Continue(HostControl hostControl)
{
scheduler.ResumeAll();
return true;
} public bool Pause(HostControl hostControl)
{
scheduler.PauseAll();
return true;
} public bool Start(HostControl hostControl)
{
scheduler.Start();
return true;
} public bool Stop(HostControl hostControl)
{
scheduler.Shutdown(false);
return true;
}
}

3、我在这里采用Topshelf的Custom Service模式,在Main()方法中写如下代码

 HostFactory.Run(x =>
{
x.UseLog4Net();
x.Service<ServiceRunner>();
x.SetDescription("QuartzDemo服务描述");
x.SetDisplayName("QuartzDemo服务显示名称");
x.SetServiceName("QuartzDemo服务名称"); x.EnablePauseAndContinue();
});

4、到此为止,建Windows Service的工作算是基本结束,接下来就是重点了,如何用Quartz做一个定时任务。但是这个过程并不难,这里我采用的是Quartz的Cron模式,相比较Simple模式,此种模式通过配置来制定Trigger触发和Job的执行,在我的Windows Service创建好后,无须我再次编译,只需要替换进一个实现IJob接口的动态链接库,并且在Quartz_jobs.xml配置即可,实现IJob的测试代码如下:

 public class TestJob : IJob
{
private readonly ILog _log = LogManager.GetLogger(typeof(TestJob));
public void Execute(IJobExecutionContext context)
{ _log.Info("测试Job,时间:"+ DateTime.Now.ToString("r")); }
}

5、准备Quartz.NET的配置文件quartz.config、quartz_jobs.xml,Quartz的Initialize()方法默认从编译输出目录下读取quartz.config文件,并且在quartz.config文件增加quartz.plugin.xml.fileNames 节点写 ~/quartz_jobs.xml,用来配置Trigger和Job的执行

 # You can configure your scheduler in either <quartz> configuration section
# or in quartz properties file
# Configuration section has precedence quartz.scheduler.instanceName = QuartzTest # configure thread pool info
quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz
quartz.threadPool.threadCount = 10
quartz.threadPool.threadPriority = Normal # job initialization plugin handles our xml reading, without it defaults are used
quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
quartz.plugin.xml.fileNames = ~/quartz_jobs.xml # export this server to remoting context
#quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter, Quartz
#quartz.scheduler.exporter.port = 555
#quartz.scheduler.exporter.bindName = QuartzScheduler
#quartz.scheduler.exporter.channelType = tcp
#quartz.scheduler.exporter.channelName = httpQuartz

quartz.config

 <?xml version="1.0" encoding="UTF-8"?>

 <!-- This file contains job definitions in schema version 2.0 format -->

 <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0">

   <processing-directives>
<overwrite-existing-data>true</overwrite-existing-data>
</processing-directives> <schedule> <!--TestJob测试 任务配置-->
<job>
<name>TestJob</name>
<group>Test</group>
<description>TestJob测试</description>
<job-type>WindowsService.TestJob,WindowsService</job-type>
<durable>true</durable>
<recover>false</recover>
</job>
<trigger>
<cron>
<name>TestJobTrigger</name>
<group>Test</group>
<job-name>TestJob</job-name>
<job-group>Test</job-group>
<!--<start-time>2017-08-03T16:00:00+16:00</start-time>
<end-time>2017-08-03T18:10:00+18:10</end-time>-->
<cron-expression>0/3 * 0-6 * * ?</cron-expression>
</cron>
</trigger> </schedule>
</job-scheduling-data>

quartz_jobs.xml

6、至此,我们已经基本可以把项目run起来了,但是这只能在console上看到每三秒打印一行“测试Job,时间:***”,并不能确定在以Windows Service时,定时任务调度也能按计划执行,我们还需要将日志输出到文件,需要继续配置log4Net

 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections> <log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!--日志路径-->
<param name= "File" value= "F:\App_Log\servicelog\"/>
<!--是否是向文件中追加日志-->
<param name= "AppendToFile" value= "true"/>
<!--不加utf-8编码格式,中文字符将显示成乱码-->
<param name="Encoding" value="utf-8" />
<!--log保留天数-->
<param name= "MaxSizeRollBackups" value= "10"/>
<!--日志文件名是否是固定不变的-->
<param name= "StaticLogFileName" value= "false"/>
<!--日志文件名格式为:2008-08-31.log-->
<param name= "DatePattern" value= "yyyy-MM-dd&quot;.read.log&quot;"/>
<!--日志根据日期滚动-->
<param name= "RollingStyle" value= "Date"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n %loggername" />
</layout>
</appender> <!-- 控制台前台显示日志 -->
<appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<mapping>
<level value="ERROR" />
<foreColor value="Red, HighIntensity" />
</mapping>
<mapping>
<level value="Info" />
<foreColor value="Green" />
</mapping>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%n%date{HH:mm:ss,fff} [%-5level] %m" />
</layout> <filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="Info" />
<param name="LevelMax" value="Fatal" />
</filter>
</appender> <root>
<!--(高) OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL (低) -->
<level value="all" />
<appender-ref ref="ColoredConsoleAppender"/>
<appender-ref ref="RollingLogFileAppender"/>
</root>
</log4net>
</configuration>

log4net.config

同时在Main()方法中增加一行读取log4Net配置文件的代码,另:quartz.config、quartz_jobs.xml、log4net.config这三个文件,分别选中→右键属性→复制到输入目录设为:始终复制

         static void Main(string[] args)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config"));
HostFactory.Run(x =>
{
x.UseLog4Net();
x.Service<ServiceRunner>();
x.SetDescription("QuartzDemo服务描述");
x.SetDisplayName("QuartzDemo服务显示名称");
x.SetServiceName("QuartzDemo服务名称"); x.EnablePauseAndContinue();
});
}

最后,我们还需要将Topshelf注册到Windows中,在CMD中打开debug文件夹,Topshelf命令如下,

安装:WindowsService.exe install
启动:WindowsService.exe start
卸载:WindowsService.exe uninstall
至此,算是基本完成,我接下来只需要在一个继承IJob的类中实现业务代码即可。(*^_^*)

参考

Quartz.NET

官方学习文档:http://www.quartz-scheduler.net/documentation/index.html

使用实例介绍:http://www.quartz-scheduler.net/documentation/quartz-2.x/quick-start.html

官方的源代码下载:http://sourceforge.net/projects/quartznet/files/quartznet/

Topself文档:http://topshelf-project.com/

Log4Net文档:http://logging.apache.org/log4net/

Topshelf结合Quartz.NET实现服务端定时调度任务的更多相关文章

  1. Windows下cwrsync客户端与rsync群辉存储服务端定时数据同步

    cwRsync简介 cwRsync是Rsync在Windows上的实现版本,Rsync通过使用特定算法的文件传输技术,可以在网络上传输只修改了的文件. cwRsync主要用于Windows上的远程文件 ...

  2. Spring+Quartz集群环境下定时调度的解决方案

    集群环境可能出现的问题 在上一篇博客我们介绍了如何在自己的项目中从无到有的添加了Quartz定时调度引擎,其实就是一个Quartz 和Spring的整合过程,很容易实现,但是我们现在企业中项目通常都是 ...

  3. SpringBoot系列九:SpringBoot服务整合(整合邮件服务、定时调度、Actuator监控)

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 服务整合 2.背景 在进行项目开发的时候经常会遇见以下的几个问题:需要进行邮件发送.定时的任务调 ...

  4. 使用flask_socketio实现服务端向客户端定时推送

    websocket连接是客户端与服务器之间永久的双向通信通道,直到某方断开连接. 双向通道意味着在连接时,服务端随时可以发送消息给客户端,反之亦然,这在一些需要即时通讯的场景比如多人聊天室非常重要. ...

  5. Akka(43): Http:SSE-Server Sent Event - 服务端主推消息

    因为我了解Akka-http的主要目的不是为了有关Web-Server的编程,而是想实现一套系统集成的api,所以也需要考虑由服务端主动向客户端发送指令的应用场景.比如一个零售店管理平台的服务端在完成 ...

  6. 记录初试Netty(2)-服务端心跳检测

    今天在在搭建的netty框架中添加心跳机制,特此记录一下:      1.什么是心跳机制? 心跳是在TCP长连接中,客户端和服务端定时向对方发送数据包通知对方自己还在线,保证连接的有效性的一种机制 在 ...

  7. day112:MoFang:种植园使用websocket代替http&服务端基于flask-socketio提供服务&服务端响应信息&种植园页面显示初始化

    目录 1.种植园使用websocket代替http 2.服务端基于socket提供服务 3.服务端响应信息 4.种植园页面展示 1.种植园使用websocket代替http 我们需要完成的种植园,是一 ...

  8. 朱晔的互联网架构实践心得S2E5:浅谈四种API设计风格(RPC、REST、GraphQL、服务端驱动)

    Web API设计其实是一个挺重要的设计话题,许多公司都会有公司层面的Web API设计规范,几乎所有的项目在详细设计阶段都会进行API设计,项目开发后都会有一份API文档供测试和联调.本文尝试根据自 ...

  9. API设计风格(RRC、REST、GraphQL、服务端驱动)

    API设计风格(RRC.REST.GraphQL.服务端驱动) Web API设计其实是一个挺重要的设计话题,许多公司都会有公司层面的Web API设计规范,几乎所有的项目在详细设计阶段都会进行API ...

随机推荐

  1. RGB值得计算公式

    三原色分别为:红(Red).绿(Green).蓝(Blue). 颜色值=(Red)+(Green*256)+(Blue*256*256) //由三原色值合成颜色整数值 function ColorFr ...

  2. 使用elasticsearch遇到的一些问题以及解决方法(不断更新)

    7.org.elasticsearch.transport.RemoteTransportException: Failed to deserialize exception response fro ...

  3. JSON.parse(JSON.stringify()) 实现对对象的深度拷贝,从而互不影响

    JSON.parse(JSON.stringify({"key": "value"})) 根据不包含引用对象的普通数组深拷贝得到启发,不拷贝引用对象,拷贝一个字 ...

  4. QT之二级菜单(二级菜单的箭头可以使用QSS设置图片)

    QT之二级菜单 QT之二级菜单 开场白 效果图 上代码 可参考文章 下代码 结尾 开场白 今天我们一起来了解下,在我们QT中,二级菜单是如何实现的,在上篇我们学习了QT之系统托盘,QT之自定义菜单,  ...

  5. 在.net core自带DI中服务生命周期 Transient,Scoped,Singleton

    只要是透过WebHost产生实例的类型,都可以在构造方法注入.所以Controller.View.Filter.Middleware或自定义的Service等都可以被注入. Transient是瞬时的 ...

  6. C++于public、protected和private说明(From MSDN)

    public(C# 參考): https://msdn.microsoft.com/zh-cn/library/yzh058ae.aspx protected(C# 參考):https://msdn. ...

  7. Visual C++文件扩展名解读

    VisualC++文件扩展名解读 [1] .APS:存储二进制资源的资源辅助中间文件(能否加快资源加载速度). [2] .BMP:位图资源文件. [3] .BSC:浏览信息文件.由浏览信息维护工具(B ...

  8. 使用 matlab 数据集的生成(generate datasets)

    一般手工生成的数据集(artificial datasets),通常用于实验部分最开始的演示和示意,用于对结果的一种精确计算和量化分析. 1. Swiss/Helix/Twinpeaks/Broken ...

  9. AWS核心服务概览

    1.Amazon Web Service 应该可以说,Amazon Web Service目前是云计算领域的领头羊,其业务规模.开发水平和盈利能力在业界内都是首屈一指的.从本科毕业离开学校就一直做Ja ...

  10. ASP .NET My97DatePicker

    My97DatePicker http://jingyan.baidu.com/article/e6c8503c7244bae54f1a18c7.html <input type="t ...