【CSDN上的原文地址】:http://blog.csdn.net/seven_coder/article/details/50760819

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

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

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

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

一、QuartzNetService服务:

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

这里的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)
    {
       //具体的创建邮件的内容并发邮件的方法不再赘述
       // ................
    }
}

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


作者: 一蓑烟雨

本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类: RabbitMQ
posted 阅读(156 ) 评论(1 )

评论内容: