(此文只作为自己的一个平时记录,以便以后回顾)

需求:根据用户在我站点中的订阅信息,每天定时(比如凌晨1:00)去数据库中查询出要给用户主动推送的信息,并自动以邮件的形式发送到用户留下的邮箱中。

基本思路:1、定时查询要主动推送的信息:使用QuartzNet去实现;

                     2、给用户发邮件:写成单独的windows服务,并使用RabbitMQ消息队列主动推送。首先在步骤1中,将查询出来的数据写到RabbitMQ中,并尤其主动去消费(即编写发邮件的逻辑)。

基于上面的思路,我这里是将这两个步骤分别写成了一个windows服务,并设置成自动启动。

一、QuartzNetService服务:

1、项目的整体结构如下(功能比较单一,所以文件也很少),要使用Quartz需要引入Quartz.dll程序集:

RabbitTemplate rabbitTemplate 发送mq rabbitmq发邮件_邮件

RabbitTemplate rabbitTemplate 发送mq rabbitmq发邮件_推送_02

这里的windows服务都是用控制台程序来实现的,其中的Program.cs主入口中的逻辑如下:

static class Program
     {
         /// <summary>
         /// 应用程序的主入口点。
         /// </summary>
         static void Main()
         {
             ServiceBase[] ServicesToRun;
             ServicesToRun = new ServiceBase[] 
             { 
                 new QuartzNetService() 
             };
             ServiceBase.Run(ServicesToRun);
         }
     }

QuartzNetService中的逻辑,主要是重新OnStart、OnStop等方法,即当服务启动时就会执行OnStart中的逻辑(本文并没有介绍windows服务是如何编写的,具体windows服务的编写,网上有很多,稍后我也会补充我的代码,以备忘),代码如下:

using Quartz;
 using Quartz.Impl;
 using Quartz.Job;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Configuration;
 using System.Data;
 using System.Diagnostics;
 using System.Linq;
 using System.ServiceProcess;
 using System.Text;
 using Quartz.Simpl;
 using Quartz.Xml;
 using System.IO;


 namespace Cmmooc.Infomation.QuartzNetService
 {
     partial class QuartzNetService : ServiceBase
     {
         private IScheduler scheduler;
         public QuartzNetService()
         {
             InitializeComponent();
         }


         protected override void OnStart(string[] args)
         {
             try
                 {
                     ISchedulerFactory sf = new StdSchedulerFactory();
                     scheduler = sf.GetScheduler();
                     JobDetail job = new JobDetail("job1", "group1", typeof(MessagePushJob));
                     string cronExpr = ConfigurationManager.AppSettings["cronExpr"]; // 读取appconfig中的配置:多长时间执行一次。配置如下红色段
                     CronTrigger trigger = new CronTrigger("trigger1", "group1", "job1", "group1", cronExpr);
                     scheduler.AddJob(job, true);
                     DateTime ft = scheduler.ScheduleJob(trigger);
                     scheduler.Start();
                     LogHelper.RecordLog(null, "Quartz服务成功启动");
                 }
                 catch (Exception ex)
                 {
                     LogHelper.RecordLog(ex, "Quartz服务成功启动");
                     base.Stop();
                 }
         }(
         <appSettings>
     <add key="cronExpr" value="0 0 1 * * ?"/>
   </appSettings>)
         protected override void OnStop()
         {
             scheduler.Shutdown(true);
             LogHelper.RecordLog(null, "Quartz服务成功终止");
         }


         protected override void OnPause()
         {
             scheduler.PauseAll();
         }


         protected override void OnContinue()
         {
             scheduler.ResumeAll();
         }
     }
 }

这段逻辑主要是创建Quartz的实例,并初始化一些设置,具体的业务逻辑在MessagePushJob这个类中,此类须要继承IJob接口,而Execute(JobExecutionContext context)中即是要处理的逻辑。具体的代码如下:

using Quartz;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Data;
 using RabbitMQ.Client;
 using RabbitMQ.Client.Events;
 using RabbitMQ.Client.MessagePatterns;
 using System.Configuration;
 using Newtonsoft.Json;


 namespace Cmmooc.Infomation.QuartzNetService
 {
     /// <summary>
     ///  根据订阅信息进行消息推送的任务,并将记录写到消息队列中,由RabbitMQ去处理邮件发送
     /// </summary>
     public class MessagePushJob : IJob
     {
         private string _HostName;
         private string _UserName;
         private string _Pass;
         private string _QueueName;
         public void Execute(JobExecutionContext context)
         {
             // .....      WriteToRabbitMQ(list);
         }

 如何往RabbitMQ中写入消息
         private void WriteToRabbitMQ(List<T> model)
         {
             ReadConfig("QuartzNet");
             var factory = new ConnectionFactory();
             factory.HostName = _HostName;
             factory.UserName = _UserName;
             factory.Password = _Pass;


             using (var connection = factory.CreateConnection())
             {
                 using (var channel = connection.CreateModel())
                 {
                     channel.QueueDeclare(_QueueName, false, false, false, null);
                     string message = JsonConvert.SerializeObject(model);
                     var body = Encoding.UTF8.GetBytes(message);
                     channel.BasicPublish("", _QueueName, null, body);
                 }
             }
         }


         #region 读取配置文件
         /// <summary>
         /// 读取配置文件
         /// </summary>
         private void ReadConfig(string QueueType)
         {
             _HostName = ConfigurationManager.AppSettings["RMQ_HostName"];
             _UserName = ConfigurationManager.AppSettings["RMQ_UserName"];
             _Pass = ConfigurationManager.AppSettings["RMQ_UserPass"];
             QueueName = ConfigurationManager.AppSettings["RMQ_QueueName"]; break;

             if (string.IsNullOrWhiteSpace(_HostName) ||
                 string.IsNullOrWhiteSpace(_UserName) ||
                 string.IsNullOrWhiteSpace(_Pass) ||
                 string.IsNullOrWhiteSpace(_QueueName))
             {
                 throw new ArgumentException("请先对RabbitMQ的参数在appSettings中进行配置");
             }
         }
         #endregion
     }
 }

至此,基于quartz的服务就已写好,安装后可以在服务列表中查看并启动。

 

二、RabbitMQ的服务:

这个服务主要是从RabbitMQ中的消息队列中获取步骤一中写入的内容,并进行相应的逻辑处理,最终调用发邮件的方法,完成主动发送邮件的功能。program.cs中的逻辑与上面没有什么区别,这里不再贴出,核心的是服务启动时的逻辑,即StartService方法中的代码如下:

using Newtonsoft.Json;
 using RabbitMQ.Client;
 using RabbitMQ.Client.Events;
 using System;
 using System.Collections.Generic;
 using System.Configuration;
 using System.Data;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Threading;
 using System.IO;


 namespace SendEmailService.Quartz
 {
     public class SendSubscribeEmailDeal
     {
         private static string _HostName;
         private static string _UserName;
         private static string _Pass;
         private static string _QueueName;


         private Thread ThreadSendSubscribeEmail;


         public void StartService()
         {
             try
             {
                 ThreadSendSubscribeEmail = new Thread(new ThreadStart(Send));  // 开启一个线程
                 ThreadSendSubscribeEmail.Start();
             }
             catch (Exception ex)
             {
                 LogHelper.RecordLog(ex, "");
             }
         }


         public void StopService()
         {
             try
             {
                 ThreadSendSubscribeEmail.Abort();
             }
             catch (Exception ex)
             {
                 LogHelper.RecordLog(ex, "");
             }
         }


         public void Send()
         {
             LogHelper.RecordLog(null, "启动推送职位订阅信息的邮件服务");
             ReadConfig("QuartzNet");


             var factory = new ConnectionFactory();
             factory.HostName = _HostName;
             factory.UserName = _UserName;
             factory.Password = _Pass;

            // 如何获取RabbitMQ中的消息
             using (var connection = factory.CreateConnection())
             {
                 using (var channel = connection.CreateModel())
                 {
                     bool durable = false;
                     channel.QueueDeclare(_QueueName, durable, false, false, null);
                     channel.BasicQos(0, 1, false);


                     var consumer = new QueueingBasicConsumer(channel);
                     channel.BasicConsume(_QueueName, false, consumer);
                     while (true)
                     {
                         var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                         var body = ea.Body;
                         var message = Encoding.UTF8.GetString(body);
                         List<SubsribeModel> model = JsonConvert.DeserializeObject<List<SubsribeModel>>(message);
                         //创建邮件内容并发送                        
                         CreateEmailAndSend(model);
                         int dots = message.Split('.').Length - 1;
                         channel.BasicAck(ea.DeliveryTag, false);
                     }
                 }
             }
         }


         /// <summary>
         ///  创建邮件内容并发送
         /// </summary>
         /// <param name="model"></param>
         private void CreateEmailAndSend(List<SubsribeModel> model)
         {
              //具体的创建邮件的内容并发邮件的方法不再赘述             // ................
         }
 }

到此为止,主动发邮件的服务也就写好了,安装后,将以上两个服务都启动后便可以实现自动定期进行消息的推送了。当然了,这只是我的一种做法,经过实践是可以正常运行的。以上若有什么不对的地方欢迎指出,或者有更好的做法也期盼分享。