使用QuartzNet和RabbitMQ实现定时发邮件的windows服务

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

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

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

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

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

一、QuartzNetService服务:

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

使用QuartzNet和RabbitMQ实现定时发邮件的windows服务_第1张图片使用QuartzNet和RabbitMQ实现定时发邮件的windows服务_第2张图片

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

 static class Program
    {
        ///


        /// 应用程序的主入口点。
        ///

        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();
                }
        }

(

        
   
 

)


        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
{
    ///


    ///  根据订阅信息进行消息推送的任务,并将记录写到消息队列中,由RabbitMQ去处理邮件发送
    ///

    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 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 读取配置文件
        ///


        /// 读取配置文件
        ///

        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 model = JsonConvert.DeserializeObject>(message);
                        //创建邮件内容并发送                        
                        CreateEmailAndSend(model);
                        int dots = message.Split('.').Length - 1;
                        channel.BasicAck(ea.DeliveryTag, false);
                    }
                }
            }
        }


        ///


        ///  创建邮件内容并发送
        ///

        ///
        private void CreateEmailAndSend(List model)
        {
             //具体的创建邮件的内容并发邮件的方法不再赘述

             // ................
        }
}

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

个人博客文章地址:http://www.dupengnet.com/detail/5a09593f72d82007a429b181

你可能感兴趣的:(使用QuartzNet和RabbitMQ实现定时发邮件的windows服务)