(此文只作为自己的一个平时记录,以便以后回顾)
需求:根据用户在我站点中的订阅信息,每天定时(比如凌晨1:00)去数据库中查询出要给用户主动推送的信息,并自动以邮件的形式发送到用户留下的邮箱中。
基本思路:1、定时查询要主动推送的信息:使用QuartzNet去实现;
2、给用户发邮件:写成单独的windows服务,并使用RabbitMQ消息队列主动推送。首先在步骤1中,将查询出来的数据写到RabbitMQ中,并尤其主动去消费(即编写发邮件的逻辑)。
基于上面的思路,我这里是将这两个步骤分别写成了一个windows服务,并设置成自动启动。
一、QuartzNetService服务:
1、项目的整体结构如下(功能比较单一,所以文件也很少),要使用Quartz需要引入Quartz.dll程序集:
这里的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
{
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>(message);
//创建邮件内容并发送
CreateEmailAndSend(model);
int dots = message.Split('.').Length - 1;
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
///
/// 创建邮件内容并发送
///
///
private void CreateEmailAndSend(List
{
//具体的创建邮件的内容并发邮件的方法不再赘述
// ................
}
}
到此为止,主动发邮件的服务也就写好了,安装后,将以上两个服务都启动后便可以实现自动定期进行消息的推送了。当然了,这只是我的一种做法,经过实践是可以正常运行的。以上若有什么不对的地方欢迎指出,或者有更好的做法也期盼分享。
个人博客文章地址:http://www.dupengnet.com/detail/5a09593f72d82007a429b181