一 定时任务基础:
MQ:Message Queue
消息队列服务器:MSMQ、ActiveMQ、Redis等
项目任务:确定邮件的发送,重置密码的发送(发送可能会很慢,而且有可能还需要重试),用消息队列把注册过程和邮件发送过程分开
二 示例:
//testMessageQueue.csProj
namespace testMessageQueue { class Program { static void Main(string[] args) { /* while(true) { string email=Console.ReadLine(); using(IRedisClient client=RedisManager.ClientManager.GetClient()) { client.EnqueueItemOnList("TestEmail", email); } } */ using(IRedisClient client=RedisManager.ClientManager.GetClient()) { while(true) { string email = client.DequeueItemFromList("TestEmail"); if(email==null) { Console.WriteLine("等待输入"); Thread.Sleep(100); continue; } Console.WriteLine("发送邮箱:eamil=" + email); } } Console.ReadKey(); } } public class RedisManager { public static PooledRedisClientManager ClientManager { get; private set; } //内部可写,外部只能读 static RedisManager() { RedisClientManagerConfig redisConfig = new RedisClientManagerConfig(); redisConfig.MaxWritePoolSize = 128; redisConfig.MaxReadPoolSize = 128; ClientManager = new PooledRedisClientManager(new string[] { "127.0.0.1" }, new string[] { "127.0.0.1" }, redisConfig); //在哪几个ip上进行集群的读写分离操作 } } }
三 定时任务框架:
//testQuartz.csProj
namespace testQuartz { public partial class Form1 : Form { public Form1() { InitializeComponent(); } /// <summary> /// 定时任务 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnQuartz_Click(object sender, EventArgs e) { //每隔一段时间执行一个任务 ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); //获得一个计划任务 JobDetail job = new JobDetail("job1", "group1", typeof(MyJog)); ////MyJog为实现了IJob接口的类 DateTime dt = TriggerUtils.GetNextGivenSecondDate(null, 2); //5秒后开始第一次运行 TimeSpan interval = TimeSpan.FromSeconds(5); //时间段--每隔5s执行一次 //每若干小时运行一次,小时间隔由appsettings中的IndexIntervalHour参数指定 Trigger trigger = new SimpleTrigger("trigger1", "group1", "job1", "group1", dt, null, SimpleTrigger.RepeatIndefinitely, interval); sched.AddJob(job, true); sched.ScheduleJob(trigger); sched.Start(); } } class MyJog : IJob { public void Execute(JobExecutionContext context) { MessageBox.Show("我执行了l"); } } }
四 实例:
(1)原理:
Quartz.Net实现:每10分钟定时发送系统数据:新增用户数据
每次用户注册的时候,都把用户的注册信息:用户名、邮箱、手机号等.除了正常的注册之外,额外再把这些数据放入消息队列。
每隔5分钟,定时从消息队列中取出新注册用户信息,然后发送邮件给业务人员.
SendNewRegUserEmailJob.cs
1 直接Global不行: RupengTimer类库 //如鹏定时任务类库(因为在Global中进行会有兼容性问题)
2 Console控制台程序也不可以: Application.Run(new FormMain()); //加载WinForm时直接启动WinForm程序,因为上面是在WinForm中测试成功的
3 WinForm中执行定时任务还是不行: 因为时间(与世界标准时间是差8h) ---革零为致时间与北京(东八区)时间差8h
DateTime.Now 应该是: DateTime dt = TriggerUtils.GetNextGivenSecondDate(null, 1);
(2)逻辑:
每10分钟定时发送系统数据:新增用户数据
1 当用户邮箱被激活,更新激活状态后,获得用户数据
2 拼接用户数据的字符串 (用户名、邮箱、手机号)
3 把这个字符串 入队列集合 (Redis_Front_QueueList_AddUser)
4 执行定时任务框架 (单独RupengTimer类库与进程中,Global中有兼容性问题)
5 执行出队列任务,获得字符串
6 如果字符串不为null或长度大于0,发送邮件
(3)步骤:
//入队列 (把新增用户信息入队列)
/// <summary> /// 入队列:用于存储新增用户,出队列时发送给指定邮箱 /// </summary> /// <param name="context"></param> /// <param name="username">用户名</param> public static void RedisEnqueueForUserRegister(HttpContext context, string username) { #region 逻辑 //1 当用户邮箱被激活,更新激活状态后,获得用户数据 //2 拼接用户数据的字符串 (用户名、邮箱、手机号) //3 把这个字符串 入队列集合 (Redis_Front_QueueList_AddUser) #endregion #region 获得用户数据 object obj = new MyORM_BLL().SelectModelByUserName(typeof(TD_USER), 1, "T", username); if (obj == null) { RazorHelper.RazorParse(context, "~/error.cshtml", new { Msg = "数据库错误,未找到该激活用户 username=" + username }); context.Response.End(); } TD_USER user = obj as TD_USER; #endregion StringBuilder sb = new StringBuilder(); #region 拼接用户数据的字符串 sb.Append("用户名:" + user.USERNAME + "|"); sb.Append("姓名:" + user.REALNAME + "|"); sb.Append("手机:" + user.MOBILE + "|"); sb.AppendLine("邮箱:" + user.EMAIL + "|"); #endregion using(IRedisClient client=RedisManager.ClientManager.GetClient()) { client.EnqueueItemOnList(ConstStringHelper.REDIS_FRONT_QUEUELIST_ADDUSER, sb.ToString()); } }
//窗体加载,就执行定时任务
namespace DIDAO.TimerForm { public partial class FormMain : Form { public FormMain() { InitializeComponent(); } /// <summary> /// 窗体一加载,就执行定时任务 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void FormMain_Load(object sender, EventArgs e) { Timer.TimeSchedule.TimeExecuteSchedule(typeof(SendEmailForAddUser), "triggerAddUser", "groupAddUser", "jobAddUser"); } }
//定时任务
namespace DIDAO.Timer { /// <summary> /// 定时任务 /// </summary> public class TimeSchedule { /// <summary> /// 执行定时任务(用于类型type中的任务) /// </summary> public static void TimeExecuteSchedule(Type type, string triggerName, string groupName, string jobName) { //每隔一段时间执行一个任务 ISchedulerFactory sf = new StdSchedulerFactory(); IScheduler sched = sf.GetScheduler(); //获得一个计划任务 JobDetail job = new JobDetail(jobName, groupName, type); ////MyJog为实现了IJob接口的类 //DateTime dt = TriggerUtils.GetNextGivenSecondDate(null, 5); //5秒后开始第一次运行 //DateTime dt = DateTime.Now; //立即执行 DateTime dt = TriggerUtils.GetNextGivenSecondDate(null, 1); //1s后开始执行 TimeSpan interval = TimeSpan.FromMinutes(1); //时间段--每隔60s执行一次 //每若干小时运行一次,小时间隔由appsettings中的IndexIntervalHour参数指定 Trigger trigger = new SimpleTrigger(triggerName, groupName, jobName, groupName, dt, null, SimpleTrigger.RepeatIndefinitely, interval); sched.AddJob(job, true); sched.ScheduleJob(trigger); sched.Start(); } }
//出队列 发送邮件(新注册用户信息)
namespace DIDAO.Timer { public class SendEmailForAddUser:IJob { /// <summary> /// 接收方邮件地址(发送 新用户注册信息时) /// </summary> private string receiveEmailAddress = ConfigurationManager.AppSettings["receiveEmailAddress"]; //发送方邮箱地址 /// <summary> /// 出队列 发送新注册用户信息 /// </summary> /// <param name="context"></param> public void Execute(JobExecutionContext context) { #region 逻辑 //4 执行定时任务框架 (单独RupengTimer类库与进程中,Global中有兼容性问题) //5 执行出队列任务,获得字符串 //6 如果字符串不为null或长度大于0,发送邮件 #endregion using(IRedisClient client=RedisManager.ClientManager.GetClient()) { StringBuilder sb = new StringBuilder(); while(true) { string item = client.DequeueItemFromList(ConstStringHelper.REDIS_FRONT_QUEUELIST_ADDUSER); if (item == null) { if(sb==null || sb.Length<=0) { return; //执行下一次定时任务 } //发送邮件 SendEmailHelper.SendMail(receiveEmailAddress, "DIDAO邮件:新用户注册信息", sb.ToString()); sb.Clear(); } else { sb.AppendLine(item); } } } } } }