简体   繁体   English

WCF服务-队列和批处理消息(通过SMTP发送电子邮件)

[英]WCF Service - queue & batch messages (sending emails via SMTP)

I have a WCF Service working via named pipes that is receiving data from ASP.NET MVC application: 我有一个WCF服务通过命名管道工作,该管道从ASP.NET MVC应用程序接收数据:

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

Service gets subject, body, sender and recipients of an email. 服务获取电子邮件的主题,正文,发件人和收件人。 Then it puts an email together and send via SMTP. 然后将电子邮件放在一起并通过SMTP发送。

Client (MVC applicataion) is sending several emails at once, so I would like the service itself to work asynchronously (client just invokes SendEmails method few times and then the WCF Server takes care of the rest). 客户端(MVC应用程序)一次发送几封电子邮件,因此我希望服务本身能够异步工作(客户端只需多次调用SendEmails方法,然后WCF Server会处理其余部分)。 But my email server sometimes refuses to send email because of greylisting of new email addresses. 但是我的电子邮件服务器有时会因为新电子邮件地址的灰名单而拒绝发送电子邮件。 That's why I would like the WCF Server to also queue those email and try to send them one by one (retry few times if the error occurs). 这就是为什么我希望WCF服务器也将那些电子邮件排队,并尝试将它们一一发送(如果发生错误,请重试几次)。

I've read several topics on WCF async nad MSMQ methods, but what is the best approach in my situation? 我已经阅读了关于WCF异步和MSMQ方法的几个主题,但是在我所处的情况下最好的方法是什么? Should I create two WCF Services (client-server & server-server)? 我是否应该创建两个WCF服务(客户端服务器和服务器服务器)? Or maybe use multi-threading? 或者也许使用多线程? It's also important for me to not use any built-in SMTP solution because I would like to expand my Service to handle other messages not only emails. 对于我来说,不要使用任何内置的SMTP解决方案也很重要,因为我想扩展我的服务以处理其他消息,而不仅仅是电子邮件。

For exactly the same requirement I've made this whole Email solution for my self however I did not use the MSMQ queue. 出于完全相同的要求,我为自己制作了整个电子邮件解决方案,但是我没有使用MSMQ队列。

following are the steps 以下是步骤

  1. Create Asynchronous WCF web service 创建异步WCF Web服务
  2. Use Task await async technique to run the send email thread inside service using task.run(()=>AsyncSendEmail) 使用Task await异步技术通过task.run(()=> AsyncSendEmail)在服务内部运行发送电子邮件线程
  3. skip through on all smptpexception on try catch and run sp to update table field isEmailSent= false in case of error 在尝试捕获时跳过所有smptpexception,并在发生错误的情况下运行sp以更新表字段isEmailSent = false
  4. subscribe the web servive through client "yourwebapplication" 通过客户端“ yourwebapplication”订阅Web服务
  5. invoke service by doing task.factory.startNew(()=> proxy.sendEmail(paramters)) 通过执行task.factory.startNew(()=> proxy.sendEmail(paramters))来调用服务
  6. create a simple windows task scheduler task to invoke to run service on scheduled time period to retry sending emails Simple Window Task Schedular 创建一个简单的Windows任务调度任务调用运行在预定的时间周期的服务重新尝试发送邮件简单的窗口工作程序器

  7. To read no deliver, failure email Emails from your exchange account I used third party service component Admin System Software and update table fields isDelivered=No , ErrorDescription=errorDesc 要读取不发送,失败的电子邮件从您的交换帐户发送的电子邮件,我使用了第三方服务组件“ 管理系统软件”,并且更新了表字段isDelivered = No,ErrorDescription = errorDesc

Hope this solution helps you.. 希望此解决方案对您有所帮助。

Shaz Shaz

I followed zaitsman's suggestion and created queue inside the service using ConcurrentQueue . 我遵循zaitsman的建议,并使用ConcurrentQueue在服务内部创建了队列。 I also used this example of creating new Thread: http://msdn.microsoft.com/en-us/library/7a2f3ay4%28v=vs.80%29.aspx 我还使用了创建新线程的示例: http : //msdn.microsoft.com/zh-cn/library/7a2f3ay4%28v=vs.80%29.aspx

I added new Thread to my WCF Server: 我向WCF服务器添加了新线程:

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

And then SendEmails method in my Service uses Enqueue to add msg to the ConcurrentQueue . 然后我的服务中的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);
    }

And here is EmailThread class that is doing it's work in the background (ie. accepting new emails to the queue and sequentially dequeuing them. 这是在后台执行工作的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;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM