65.9K
CodeProject 正在变化。 阅读更多。
Home

MS SQL Server 邮件队列

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.45/5 (9投票s)

2004 年 5 月 14 日

CPOL

2分钟阅读

viewsIcon

80118

使用 SQL 数据库的邮件队列。

在 Microsoft 环境中,有许多方法可以实现队列,从消息队列到文件系统,但我们将讨论使用 MS SQL Server 的选项。 本文同样适用于任何关系数据库和任何平台。

使用数据库的优点

  • 事务完整性
  • 并发控制
  • 故障恢复
  • 可扩展性
  • 易于编码
  • 统计/报告
  • 批处理

有很多方法可以解决这个问题,我们鼓励大家提出意见和批评。 为了本文的目的,该解决方案将采取最简单的形式。

一些问题需要在良好、简单、可扩展的设计中解决。 让我们从需要发送电子邮件的代码开始。 除了通常的内容(收件人、发件人、正文)之外,可能还需要优先级、重试和状态。 根据这些要求,我们可以在数据库中设计一个简单的表

CREATE TABLE [dbo].[MailQueue] (
[ID] [int] IDENTITY (10000, 1),
[dtStamp] [datetime],
[DateToProcess] [datetime],
[DateProcessed] [datetime],
[FromName] [varchar] (100),
[FromAddress] [varchar] (400),
[ToAddress] [varchar] (400),
[CC] [varchar] (400),
 [varchar] (400),
[Status] [varchar] (800),
[ThreadLock] UNIQUEIDENTIFIER,
[AttemptsRemaining] [int],
[Priority] [int],
 [text],
)

这个简单的表模式将提供队列。 代码会将行插入到表中,每封电子邮件一行。 实际工作将在从队列中读取的 Windows 服务中完成。 让我们深入了解哪些列是不明确的。

状态

  • 插入该行时,状态将开始为“UnSent”。
  • 如果电子邮件已成功发送,则状态将更改为“Sent”。
  • 如果邮件未发送,则状态将表示一些错误。

尝试剩余次数

每次电子邮件服务尝试发送电子邮件失败时,此值都会递减。 从队列中提取邮件时,电子邮件服务将仅读取此值大于零的行。

优先级

通过使此数字更大,电子邮件服务将首先使用此数字按其优先级选择电子邮件行。

线程锁定

电子邮件服务尝试发送电子邮件时,将设置此字段。 服务处理完该行后,该字段将设置回 null。

用于获取行的 SQL

BEGIN TRANSACTION

 -- retry old mails that failed

UPDATE MailQueue
SET ThreadLock = NULL
WHERE ThreadLock IS NOT NULL
AND DateProcessed < DATEADD( minute, 15, GETDATE() )
AND Status != 'Sent'

 -- select mails to send

SELECT TOP 10 *
INTO #tmpRows
FROM MailQueue
WHERE ThreadLock IS NULL
AND DateToProcess > GETDATE()
AND AttemptsRemaining > 0
ORDER BY Priority DESC

 -- update to lock them

UPDATE MailQueue
SET ThreadLock = '<MY GUID>',
DateProcessed = GETDATE()
FROM #tmpRows r
WHERE r.ID = MailQueue.ID
AND ThreadLock IS NULL
AND DateToProcess > GETDATE()
AND AttemptsRemaining > 0
COMMIT TRANSACTION

 -- select rows

SELECT *
FROM MailQueue
WHERE ThreadLock = '<MY GUID>'

需要传入三个变量; 每批的行数(在本例中为 10),由将处理批次的线程生成的 GUID(在本例中为“<MY GUID>”)以及用于重试电子邮件的线程超时时间(在本例中为 15 分钟)。

电子邮件服务

该服务将运行上述 SQL 并迭代行,发送电子邮件。 该服务可以是多线程的,在这种情况下,每个线程将运行 SQL 并处理其自己的批次。 该服务可以在许多机器上运行 - 仍然不应该有两个线程同时处理同一行。

电子邮件服务的任务是更新数据库。 以下是成功和失败的示例 SQL

 -- success

UPDATE MailQueue
SET ThreadLock = NULL,
DateProcessed = GETDATE(),
Status = 'Sent'
WHERE ID = 10001
-- failure UPDATE MailQueue SET ThreadLock = NULL, DateProcessed = GETDATE(), Status = 'Cannot access CDO.Message object' AttemptsRemaining = AttemptsRemaining - 1 WHERE ID = 10002

如果电子邮件服务或线程由于某种原因死亡,则该行将在超时时间段后由下一个运行的 select 语句放回队列中。

接下来,一个 C# 示例。

© . All rights reserved.