【问题标题】:WCF Service - queue & batch messages (sending emails via SMTP)WCF 服务 - 队列和批处理消息(通过 SMTP 发送电子邮件)
【发布时间】:2014-09-25 10:11:10
【问题描述】:

我有一个通过命名管道工作的 WCF 服务,它从 ASP.NET MVC 应用程序接收数据:

[ServiceContract]
public interface IEmailProcessor
{
    [OperationContract(AsyncPattern = true)]
    void SendEmails(string subject, string body, string from, string to);
}

服务获取电子邮件的主题、正文、发件人和收件人。然后它会将电子邮件放在一起并通过 SMTP 发送。

客户端(MVC 应用程序)一次发送几封电子邮件,所以我希望服务本身异步工作(客户端只需调用 SendEmails 方法几次,然后 WCF 服务器负责其余的工作)。但是我的电子邮件服务器有时会因为新电子邮件地址的灰名单而拒绝发送电子邮件。这就是为什么我希望 WCF 服务器也将这些电子邮件排队并尝试将它们一一发送(如果发生错误,请重试几次)。

我已经阅读了几个关于 WCF 异步和 MSMQ 方法的主题,但是在我的情况下,最好的方法是什么?我应该创建两个 WCF 服务(客户端-服务器和服务器-服务器)吗?或者也许使用多线程?不使用任何内置 SMTP 解决方案对我来说也很重要,因为我想扩展我的服务以处理其他消息,而不仅仅是电子邮件。

【问题讨论】:

  • 这在很大程度上取决于您的要求。我不会更改合同(因为您的帖子中不需要确认)所以这已经足够了。您只需要在服务内部构建一个队列(例如使用 ConcurrentQueue 集合),然后处理该队列并将未成功传递的电子邮件重新排队。如果您使用的是 .net 4/4.5,我还建议将排队的电子邮件封装到 Task.Run() 中。此外,根据数量,您可以考虑将服务设为单例。

标签: c# asp.net asp.net-mvc wcf email


【解决方案1】:

出于完全相同的要求,我为自己制作了整个电子邮件解决方案,但是我没有使用 MSMQ 队列。

以下是步骤

  1. 创建异步 WCF Web 服务

  2. 使用任务等待异步技术通过 task.run(()=>AsyncSendEmail) 在服务内部运行发送电子邮件线程

  3. 在 try catch 上跳过所有 smptpexception 并运行 sp 以更新表字段 isEmailSent=假 万一出错

  4. 通过客户端“yourwebapplication”订阅网络服务

  5. 通过 task.factory.startNew(()=> proxy.sendEmail(paramters)) 调用服务

  6. 创建一个简单的 Windows 任务调度程序任务来调用以在计划的时间段内运行服务以重试发送电子邮件Simple Window Task Schedular

  7. 要读取来自您交换帐户的未投递、失败电子邮件电子邮件,我使用了第三方服务组件 Admin System Software 并更新表字段 isDelivered=No , ErrorDescription=errorDesc

希望此解决方案对您有所帮助..

沙兹

【讨论】:

    【解决方案2】:

    我按照 zaitsman 的建议,使用ConcurrentQueue 在服务内创建了队列。我也使用了这个创建新线程的例子:http://msdn.microsoft.com/en-us/library/7a2f3ay4%28v=vs.80%29.aspx

    我向我的 WCF 服务器添加了新线程:

        var thread = new Thread(EmailThread.Instace.DoWork);
        thread.Start();
    

    然后我的 Service 中的 SendEmails 方法使用 Enqueue 将 msg 添加到 ConcurrentQueue

        public void SendEmails(string from, string to, string subject, string body)
        {
            var msg = new MailMessage(from, to, subject, body)
            {
                IsBodyHtml = true,
                BodyEncoding = Encoding.UTF8,
                DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure
            };
    
            EmailThread.Instace.Messages.Enqueue(msg);
        }
    

    这是在后台工作的 EmailThread 类(即接受新电子邮件到队列并按顺序将它们出队。

    internal class EmailThread
    {
        //Ensure that only one instance of EmailThread can exist
        private static readonly EmailThread Instance = new EmailThread();
        public static EmailThread Instace { get { return Instance; } }
    
        //Here is our Queue
        public ConcurrentQueue<MailMessage> Messages { get; private set; }
    
        private EmailThread()
        {
            Messages = new ConcurrentQueue<MailMessage>();
        }
    
        // This method is called when the thread is started and repeated once a 10 seconds
        public void DoWork()
        {
            while (!_shouldStop)
            {
                Console.WriteLine("email sender thread: working...");
    
                if (!Messages.IsEmpty)
                {
                    //define SMTP credentials here
                    var smtp = new SmtpClient()
    
                    var failed = new Queue<MailMessage>();
    
                    MailMessage message;
                    while (Messages.TryDequeue(out message))
                    {
                        try
                        {
                            smtp.Send(message);
                            Console.WriteLine("email sender thread: successfully sent email...");
                        }
                        catch (SmtpException)
                        {
                            //Enqueue again if failed
                            failed.Enqueue(message);
                            Console.WriteLine("email sender thread: error sending email, enqueuing...");
                        }
                    }
    
                    foreach (var mailMessage in failed)
                    {
                        Messages.Enqueue(mailMessage);
                    }
    
                    smtp.Dispose();
                }
                Thread.Sleep(10000);
            }
            Console.WriteLine("email sender thread: terminating gracefully.");
        }
        public void RequestStop()
        {
            _shouldStop = true;
        }
        // Volatile is used as hint to the compiler that this data
        // member will be accessed by multiple threads.
        private volatile bool _shouldStop;
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-17
      • 2019-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-09
      • 2017-05-19
      • 1970-01-01
      相关资源
      最近更新 更多