我们总是会遇到这样的业务,
1、监测会员卡是否到期了
2、监测我们服务器的CPU使用情况,硬盘以及内存的占用情况
3、悄悄的将用户数据同步至另外一台服务器
一旦遇到这种情况,我理所应当想到Windows服务。
作为一个合格的开发人员,我怎么可以一直保持固有想法,渐渐的发现Windows服务的各种弊端跟缺陷
1、明明我们的业务系统已经停止使用了,但是开发人员忘记了停止并卸载Windows服务,导致服务器上有很多无效windows服务,白白浪费着服务器性能
2、我们用时候遇到跟支付宝以及微信推送支付消息相同的业务,支付完成后,马上推送一次请求端,1分钟,3分钟,5分钟...半小时各推送一次,直至客户端返回Scuess,我们才停止推送,这样的推送方式,导致我们在写Window服务时,徒增很多困难。
我们安于现状,不需求改变,明知有各种弊端,但是我们一旦遇到困难,总是想从最简单的方式开始入手,寻求解决方案。
突然有一天,我们的Boss在开完晨会后,
跟我说,.NetCore已经支持跨平台了,是不是?
我马上回答,是,这扬眉吐气,搞Java的Diss搞C#开发的这么多年,不就是因为不跨平台么
我们Boss说,好,从现在开始,为了节省成本,我们现有的业务系统迁移至linux服务器。
万万没想到,我仅仅一个是,跟我们项目组带来这么大麻烦。
老板看我犹豫,问我,跨平台迁移有困难?
我咬着牙说,没有,就算咬碎了牙也不能立马打脸啊
windows迁移linux,对于一个linux小白来说,遇到了很多困难,这些困难以及坑我会在后续的文章中,慢慢跟大家分享。今天着重说一下Windows服务。
我遇到的第一个问题就是,我们的用于监测业务数据的Window服务怎么办?
linux可不能随随便便就部署一个Windows服务
一次找资料中无意间看到任务调度框架,任务调度框架,可以完美解决我们的Windows服务问题
任务调度框架的开源代码有很多,在众多框架中找到了Quartz,救命稻草
废话不多说,直接上代码,毕竟关于Quartz,很多大神已经写了很多代码
我们只需要在程序启动的过程中加入任务调度框架即可,在代码:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
#region 创建作业调度
RunProgram().GetAwaiter().GetResult();
#endregion
}
ok,剩下我们就需要在RunProgram方法中配置我们的作业调度框架信息
我的代码如下:
#region 创建微信消息推送任务调度
/// <summary>
/// 创建微信消息推送任务调度
/// </summary>
/// <returns></returns>
private static async Task RunProgram()
{
try
{
// Grab the Scheduler instance from the Factory
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
IScheduler scheduler = await factory.GetScheduler();
IJobDetail job = JobBuilder.Create<WeChatAuthMessagePush>()
.WithIdentity("jobPushWeChatAuthMessage", "groupWeChatAuth")
.Build();
ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
.WithIdentity("triggerPushWeChatAuthMessage", "groupWeChatAuth")
.StartNow().WithSimpleSchedule(x => x.WithIntervalInMinutes(10).RepeatForever()).Build();
await scheduler.ScheduleJob(job, trigger);
// and start it off
await scheduler.Start();
// some sleep to show what's happening
//await Task.Delay(TimeSpan.FromSeconds(3));
// and last shut down the scheduler when you are ready to close your program
//await scheduler.Shutdown();
}
catch (SchedulerException se)
{
await Console.Error.WriteLineAsync(se.ToString());
}
}
#endregion
下面我一一介绍一下,这些参数的意思,也方便自己加深记忆
IScheduler:调度器。所有的调度都是由它控制。
Trigger: 定义触发的条件。例子中,它的类型是SimpleTrigger,每隔10分钟执行一次
JobDetail & Job: JobDetail 定义的是任务数据,而真正的执行的业务代码是在Job中,例子中是WeChatAuthMessagePush。 为什么设计成JobDetail + Job,不直接使用Job?这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
ok,下一步,我们的作业调度方法继承自IJob接口,实现IJob接口中的Execute方法,Execute方法中即可以处理我们的业务计划。
代码如下:
public async Task Execute(IJobExecutionContext context)
{
await Console.Out.WriteLineAsync("任务计划调度成功!");
}
我们看一下接口IJobExecutionContext中的方法以及参数
//
// 摘要:
// A context bundle containing handles to various environment information, that
// is given to a Quartz.IJobExecutionContext.JobDetail instance as it is executed,
// and to a Quartz.ITrigger instance after the execution completes.
public interface IJobExecutionContext
{
//
// 摘要:
// Get the Quartz.IJobExecutionContext.JobDetail associated with the Quartz.IJob.
IJobDetail JobDetail { get; }
}
我们针对这些参数一一解释
在JobDetail中,我们可以获取作业调度框架的详细信息以及描述信息,如:
context.JobDetail.Key.Group
context.JobDetail.Key.Name