邮件发送程序&请求超时

发布于 2024-08-21 15:53:30 字数 211 浏览 5 评论 0原文

我被要求在asp.net上开发一个自动邮件发送程序。它应该发送 5000 封电子邮件,并从数据库中读取地址。肯定会陷入请求超时的困境。所以看来我必须将它转换成Windows应用程序。但我想知道 ajaxizing 这个网络应用程序是否有帮助。如果我编写一个 Web 服务,并且我的 Web 应用程序一次发送 50 个邮件地址的列表。完成后,发送下 50 个,依此类推。这是否有助于解决http请求超时的问题?

I was asked to develop an auto-mail sending program on asp.net. It is supposed to send, say 5000 e-mails reading the addresses from a database. It will sure fall into request timeout tough. So it seems I have to convert it into a windows app. But I'd like to know if ajaxifying this web-app would help. If I write a web service, and my web app sends the mail addresses as lists of 50 at a time. when done, send the next 50 and so on. Would this help to solve the problem of http request timeout?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小伙你站住 2024-08-28 15:53:30

使用 Web 服务端点发送电子邮件是一个好主意,无论您是从 aspx 类调用它还是从使用 javascript 的客户端调用它。

只需使用 Web 服务调用来生成一个线程来发送电子邮件并立即返回。

如果您想要视觉进度提示,请编写另一个 ajax 端点或 aspx 页面来显示电子邮件线程的进度状态。

有很多方法可以实现这一目标,您应该能够根据给定的信息想出一种方法。

从 ajax 进行批处理可能会比您想要做的工作更多,并且会增加不必要的复杂性(这绝不是一件好事)。

这很有趣。我可能会增加这个并发布一些代码。

好的,我回来了。给你。 webform ui 和 ajax ui。

这些都不是成品——只是支持故事的一个尖峰。随意弯曲/折叠/旋转。

EmailService.asmx

using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Script.Services;
using System.Web.Services;

namespace EmailSendingWebApplication
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    [ScriptService]
    public class EmailService : WebService
    {
        private static EmailSendingProgress _currentProgress;
        private static Thread _emailThread;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="criteria">just an example</param>
        /// <param name="anotherCriteria">just an example</param>
        /// <returns></returns>
        [WebMethod]
        public EmailSendingProgress SendEmails(string criteria, int anotherCriteria)
        {
            try
            {
                if (_currentProgress != null && _emailThread.IsAlive)
                {
                    throw new InvalidOperationException(
                        "Email batch is already in progress. Wait for completion or cancel");
                }

                // use your criteria to cue up the emails to be sent.
                // .....
                // and derive a way for a thread to identify the emails
                // i am using a batchid

                int batchId = 1000; // contrived


                // create a thread

                _emailThread = new Thread(ProcessEmails);


                _currentProgress = new EmailSendingProgress
                                       {
                                           Status = ProcessState.Starting,
                                           BatchId = batchId
                                       };


                // you could use a 'state' object but this process/thread
                // is single use/single instance just access _currentProgress
                // by the static member

                _emailThread.Start();


                return _currentProgress;
            }
            catch (Exception ex)
            {
                _currentProgress = new EmailSendingProgress
                           {
                               Status = ProcessState.Error,
                               Message = "Error starting process:" + ex.Message
                           };
            }

            return _currentProgress;
        }

        [WebMethod]
        public EmailSendingProgress CancelEmailProcess()
        {
            if (_currentProgress != null && _emailThread.IsAlive)
            {
                _currentProgress.Cancel = true;
                _currentProgress.Message = "Cancelling";
            }

            return _currentProgress;
        }

        [WebMethod]
        public EmailSendingProgress GetProgress()
        {
            return _currentProgress;
        }

        private static void ProcessEmails()
        {
            // process your emails using the criteria, in this case, 
            // a batchId

            int totalEmails = 100;
            int currentEmail = 0;

            lock (_currentProgress)
            {
                _currentProgress.Total = totalEmails;
                _currentProgress.Status = ProcessState.Processing;
            }


            for (; currentEmail < totalEmails; currentEmail++)
            {
                lock (_currentProgress)
                {
                    if (_currentProgress.Cancel)
                    {
                        _currentProgress.Status = ProcessState.Cancelled;
                        _currentProgress.Message = "User cancelled process.";
                        break;
                    }
                    _currentProgress.Current = currentEmail + 1;
                }

                try
                {
                    // send your email
                    Thread.Sleep(100); // lallalala sending email
                }
                catch (Exception ex)
                {
                    // log the failure in your db

                    // then check to see if we should exit on error
                    // or just keep going.
                    lock (_currentProgress)
                    {
                        if (_currentProgress.CancelBatchOnSendError)
                        {
                            _currentProgress.Status = ProcessState.Error;
                            _currentProgress.Message = ex.Message;
                            break;
                        }
                    }
                }
            }

            {
                // don't want to obscure state/message from abnormal
                // termination..
                if (_currentProgress.Status == ProcessState.Processing)
                {
                    _currentProgress.Status = ProcessState.Idle;
                    _currentProgress.Message = "Processing complete.";
                }
            }
        }
    }

    public enum ProcessState
    {
        Idle,
        Starting,
        Processing,
        Cancelled,
        Error
    }

    [Serializable]
    public class EmailSendingProgress
    {
        public int BatchId;
        public bool Cancel;
        public bool CancelBatchOnSendError;
        public int Current;
        public string Message;
        public ProcessState Status;
        public int Total;
    }
}

WebFormUI.aspx

<%@ Page Language="C#" %>

<%@ Import Namespace="EmailSendingWebApplication" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        var svc = new EmailService();
        UpdateProgress(svc.GetProgress());
    }

    protected void SendEmailsButton_Click(object sender, EventArgs e)
    {
        // arbitrary params - modify to suit
        string criteria = string.Empty;
        int anotherCriteria = 0;

        var svc = new EmailService();
        UpdateProgress(svc.SendEmails(criteria, anotherCriteria));
    }

    protected void CancelEmailProcessButton_Click(object sender, EventArgs e)
    {
        var svc = new EmailService();
        UpdateProgress(svc.CancelEmailProcess());
    }

    private void UpdateProgress(EmailSendingProgress progress)
    {
        SetButtonState(progress);
        DisplayProgress(progress);
    }
    private void DisplayProgress(EmailSendingProgress progress)
    {
        if (progress != null)
        {
            EmailProcessProgressLabel.Text = string.Format("Sending {0} of {1}", progress.Current, progress.Total);
            EmailProcessStatusLabel.Text = progress.Status.ToString();
            EmailProcessMessageLabel.Text = progress.Message;
        }
        else
        {
            EmailProcessProgressLabel.Text = string.Empty;
            EmailProcessStatusLabel.Text = string.Empty;
            EmailProcessMessageLabel.Text = string.Empty;
        }
    }

    private void SetButtonState(EmailSendingProgress progress)
    {
        if (progress != null &&
            (progress.Status == ProcessState.Starting || progress.Status == ProcessState.Processing))
        {
            CancelEmailProcessButton.Visible = true;
            SendEmailsButton.Visible = false;
        }
        else
        {
            CancelEmailProcessButton.Visible = false;
            SendEmailsButton.Visible = true;
        }
    }

    protected void RefreshButton_Click(object sender, EventArgs e)
    {
        // noop just to get postback. you could also use meta headers to refresh the page automatically 
        // but why?
    }
</script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <br />
        EmailProcessStatus:
        <asp:Label ID="EmailProcessStatusLabel" runat="server" Text="EmailProcessStatus"></asp:Label>
        <br />
        EmailProcessProgress:
        <asp:Label ID="EmailProcessProgressLabel" runat="server" Text="EmailProcessProgress"></asp:Label>
        <br />
        EmailProcessMessage:<asp:Label ID="EmailProcessMessageLabel" runat="server" Text="EmailProcessMessage"></asp:Label>
        <br />
        <br />
        <asp:Button ID="SendEmailsButton" runat="server" OnClick="SendEmailsButton_Click"
            Text="Send Emails" />
         <asp:Button ID="CancelEmailProcessButton" runat="server" OnClick="CancelEmailProcessButton_Click"
            Text="Cancel Email Process" />
        <br />
        <br />
        <asp:Button ID="RefreshButton" runat="server" OnClick="RefreshButton_Click" Text="Refresh" />
    </div>
    </form>
</body>
</html>

AjaxUI.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>

    <script type="text/javascript">
        //http://www.codeproject.com/Articles/38999/Consuming-ASP-net-WebServices-WCF-Services-and-sta.aspx

        var ProcessState = ["Idle", "Starting", "Processing", "Cancelled", "Error"];

        function createXHR() {
            var xhr;

            if (window.XMLHttpRequest) {
                xhr = new XMLHttpRequest();
            }
            else if (window.ActiveXObject) {
                xhr = new ActiveXObject('Microsoft.XMLHTTP');
            }
            else {
                throw new Error("Could not create XMLHttpRequest object.");
            }
            return xhr;
        }

        function emailAjax(operation, postData, callback) {

            var xhr = createXHR();

            xhr.open("POST", "EmailService.asmx/" + operation, true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    callback(xhr.responseText);
                }
            };
            xhr.setRequestHeader("content-type", "application/json");
            xhr.send(postData);
        }

        function $(id) {
            var e = document.getElementById(id);
            return e;
        }
        function startProcess() {
            var postData = '{"criteria" : "something", "anotherCriteria" : "1"}';
            emailAjax("SendEmails", postData, displayProgress);
        }

        function cancelProcess() {
            emailAjax("CancelEmailProcess", null, displayProgress);
        }

        function getProgress() {
            emailAjax("GetProgress", null, displayProgress);
        }

        function displayProgress(json) {
            eval('var result=' + json + '; var prg=result.d;');

            $("EmailProcessMessage").innerHTML = "";
            $("EmailProcessStatus").innerHTML = "";
            $("EmailProcessProgress").innerHTML = "";
            $("CancelEmailProcessButton").style.display = "none";
            $("SendEmailsButton").style.display = "none";

            if (prg) {
                $("EmailProcessMessage").innerHTML = prg.Message;
                $("EmailProcessStatus").innerHTML = ProcessState[prg.Status];
                $("EmailProcessProgress").innerHTML = "Sending " + prg.Current + " of " + prg.Total;
            }

            if (prg && (prg.Status == 1 || prg.Status == 2)) {
                $("SendEmailsButton").style.display = "none";
                $("CancelEmailProcessButton").style.display = "inline";
            }
            else {
                $("CancelEmailProcessButton").style.display = "none";
                $("SendEmailsButton").style.display = "inline";
            }

        }

        function init() {
            $("SendEmailsButton").onclick = startProcess;
            $("CancelEmailProcessButton").onclick = cancelProcess;
            // kinda quick but we are only proccing 100 emails for demo
            window.setInterval(getProgress, 1000);


        }
    </script>

</head>
<body onload="init()">
    EmailProcessStatus:<span id="EmailProcessStatus"></span><br />
    EmailProcessProgress:<span id="EmailProcessProgress"></span><br />
    EmailProcessMessage:<span id="EmailProcessMessage"></span><br />
    <input type="button" id="SendEmailsButton" value="SendEmails" style="display: none" />
    <input type="button" id="CancelEmailProcessButton" value="CancelEmailProcess" style="display: none" />
</body>
</html>

Using a webservice endpoint to send your emails is a good idea, whether you call it from an aspx class or from the client with javascript.

Simply use the webservice call to spawn a thread to send the emails and return immediately.

If you wanted visual progress cues then write another ajax endpoint or aspx page that will display the status of the email thread's progress.

There are many ways to accomplish this, you should be able to come up with one with the information given.

Batching from ajax is probably going to be more work than you want to do and adds unnecessary complexity (which is never a good thing).

this is interesting. I may spike this and post some code.

Ok, im back. here ya go. both a webform ui and an ajax ui.

None of this is meant to be finished product - is a spike to support a story. bend/fold/spindle at will.

EmailService.asmx

using System;
using System.ComponentModel;
using System.Threading;
using System.Web.Script.Services;
using System.Web.Services;

namespace EmailSendingWebApplication
{
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    [ScriptService]
    public class EmailService : WebService
    {
        private static EmailSendingProgress _currentProgress;
        private static Thread _emailThread;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="criteria">just an example</param>
        /// <param name="anotherCriteria">just an example</param>
        /// <returns></returns>
        [WebMethod]
        public EmailSendingProgress SendEmails(string criteria, int anotherCriteria)
        {
            try
            {
                if (_currentProgress != null && _emailThread.IsAlive)
                {
                    throw new InvalidOperationException(
                        "Email batch is already in progress. Wait for completion or cancel");
                }

                // use your criteria to cue up the emails to be sent.
                // .....
                // and derive a way for a thread to identify the emails
                // i am using a batchid

                int batchId = 1000; // contrived


                // create a thread

                _emailThread = new Thread(ProcessEmails);


                _currentProgress = new EmailSendingProgress
                                       {
                                           Status = ProcessState.Starting,
                                           BatchId = batchId
                                       };


                // you could use a 'state' object but this process/thread
                // is single use/single instance just access _currentProgress
                // by the static member

                _emailThread.Start();


                return _currentProgress;
            }
            catch (Exception ex)
            {
                _currentProgress = new EmailSendingProgress
                           {
                               Status = ProcessState.Error,
                               Message = "Error starting process:" + ex.Message
                           };
            }

            return _currentProgress;
        }

        [WebMethod]
        public EmailSendingProgress CancelEmailProcess()
        {
            if (_currentProgress != null && _emailThread.IsAlive)
            {
                _currentProgress.Cancel = true;
                _currentProgress.Message = "Cancelling";
            }

            return _currentProgress;
        }

        [WebMethod]
        public EmailSendingProgress GetProgress()
        {
            return _currentProgress;
        }

        private static void ProcessEmails()
        {
            // process your emails using the criteria, in this case, 
            // a batchId

            int totalEmails = 100;
            int currentEmail = 0;

            lock (_currentProgress)
            {
                _currentProgress.Total = totalEmails;
                _currentProgress.Status = ProcessState.Processing;
            }


            for (; currentEmail < totalEmails; currentEmail++)
            {
                lock (_currentProgress)
                {
                    if (_currentProgress.Cancel)
                    {
                        _currentProgress.Status = ProcessState.Cancelled;
                        _currentProgress.Message = "User cancelled process.";
                        break;
                    }
                    _currentProgress.Current = currentEmail + 1;
                }

                try
                {
                    // send your email
                    Thread.Sleep(100); // lallalala sending email
                }
                catch (Exception ex)
                {
                    // log the failure in your db

                    // then check to see if we should exit on error
                    // or just keep going.
                    lock (_currentProgress)
                    {
                        if (_currentProgress.CancelBatchOnSendError)
                        {
                            _currentProgress.Status = ProcessState.Error;
                            _currentProgress.Message = ex.Message;
                            break;
                        }
                    }
                }
            }

            {
                // don't want to obscure state/message from abnormal
                // termination..
                if (_currentProgress.Status == ProcessState.Processing)
                {
                    _currentProgress.Status = ProcessState.Idle;
                    _currentProgress.Message = "Processing complete.";
                }
            }
        }
    }

    public enum ProcessState
    {
        Idle,
        Starting,
        Processing,
        Cancelled,
        Error
    }

    [Serializable]
    public class EmailSendingProgress
    {
        public int BatchId;
        public bool Cancel;
        public bool CancelBatchOnSendError;
        public int Current;
        public string Message;
        public ProcessState Status;
        public int Total;
    }
}

WebFormUI.aspx

<%@ Page Language="C#" %>

<%@ Import Namespace="EmailSendingWebApplication" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        var svc = new EmailService();
        UpdateProgress(svc.GetProgress());
    }

    protected void SendEmailsButton_Click(object sender, EventArgs e)
    {
        // arbitrary params - modify to suit
        string criteria = string.Empty;
        int anotherCriteria = 0;

        var svc = new EmailService();
        UpdateProgress(svc.SendEmails(criteria, anotherCriteria));
    }

    protected void CancelEmailProcessButton_Click(object sender, EventArgs e)
    {
        var svc = new EmailService();
        UpdateProgress(svc.CancelEmailProcess());
    }

    private void UpdateProgress(EmailSendingProgress progress)
    {
        SetButtonState(progress);
        DisplayProgress(progress);
    }
    private void DisplayProgress(EmailSendingProgress progress)
    {
        if (progress != null)
        {
            EmailProcessProgressLabel.Text = string.Format("Sending {0} of {1}", progress.Current, progress.Total);
            EmailProcessStatusLabel.Text = progress.Status.ToString();
            EmailProcessMessageLabel.Text = progress.Message;
        }
        else
        {
            EmailProcessProgressLabel.Text = string.Empty;
            EmailProcessStatusLabel.Text = string.Empty;
            EmailProcessMessageLabel.Text = string.Empty;
        }
    }

    private void SetButtonState(EmailSendingProgress progress)
    {
        if (progress != null &&
            (progress.Status == ProcessState.Starting || progress.Status == ProcessState.Processing))
        {
            CancelEmailProcessButton.Visible = true;
            SendEmailsButton.Visible = false;
        }
        else
        {
            CancelEmailProcessButton.Visible = false;
            SendEmailsButton.Visible = true;
        }
    }

    protected void RefreshButton_Click(object sender, EventArgs e)
    {
        // noop just to get postback. you could also use meta headers to refresh the page automatically 
        // but why?
    }
</script>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <br />
        EmailProcessStatus:
        <asp:Label ID="EmailProcessStatusLabel" runat="server" Text="EmailProcessStatus"></asp:Label>
        <br />
        EmailProcessProgress:
        <asp:Label ID="EmailProcessProgressLabel" runat="server" Text="EmailProcessProgress"></asp:Label>
        <br />
        EmailProcessMessage:<asp:Label ID="EmailProcessMessageLabel" runat="server" Text="EmailProcessMessage"></asp:Label>
        <br />
        <br />
        <asp:Button ID="SendEmailsButton" runat="server" OnClick="SendEmailsButton_Click"
            Text="Send Emails" />
         <asp:Button ID="CancelEmailProcessButton" runat="server" OnClick="CancelEmailProcessButton_Click"
            Text="Cancel Email Process" />
        <br />
        <br />
        <asp:Button ID="RefreshButton" runat="server" OnClick="RefreshButton_Click" Text="Refresh" />
    </div>
    </form>
</body>
</html>

AjaxUI.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>

    <script type="text/javascript">
        //http://www.codeproject.com/Articles/38999/Consuming-ASP-net-WebServices-WCF-Services-and-sta.aspx

        var ProcessState = ["Idle", "Starting", "Processing", "Cancelled", "Error"];

        function createXHR() {
            var xhr;

            if (window.XMLHttpRequest) {
                xhr = new XMLHttpRequest();
            }
            else if (window.ActiveXObject) {
                xhr = new ActiveXObject('Microsoft.XMLHTTP');
            }
            else {
                throw new Error("Could not create XMLHttpRequest object.");
            }
            return xhr;
        }

        function emailAjax(operation, postData, callback) {

            var xhr = createXHR();

            xhr.open("POST", "EmailService.asmx/" + operation, true);
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    callback(xhr.responseText);
                }
            };
            xhr.setRequestHeader("content-type", "application/json");
            xhr.send(postData);
        }

        function $(id) {
            var e = document.getElementById(id);
            return e;
        }
        function startProcess() {
            var postData = '{"criteria" : "something", "anotherCriteria" : "1"}';
            emailAjax("SendEmails", postData, displayProgress);
        }

        function cancelProcess() {
            emailAjax("CancelEmailProcess", null, displayProgress);
        }

        function getProgress() {
            emailAjax("GetProgress", null, displayProgress);
        }

        function displayProgress(json) {
            eval('var result=' + json + '; var prg=result.d;');

            $("EmailProcessMessage").innerHTML = "";
            $("EmailProcessStatus").innerHTML = "";
            $("EmailProcessProgress").innerHTML = "";
            $("CancelEmailProcessButton").style.display = "none";
            $("SendEmailsButton").style.display = "none";

            if (prg) {
                $("EmailProcessMessage").innerHTML = prg.Message;
                $("EmailProcessStatus").innerHTML = ProcessState[prg.Status];
                $("EmailProcessProgress").innerHTML = "Sending " + prg.Current + " of " + prg.Total;
            }

            if (prg && (prg.Status == 1 || prg.Status == 2)) {
                $("SendEmailsButton").style.display = "none";
                $("CancelEmailProcessButton").style.display = "inline";
            }
            else {
                $("CancelEmailProcessButton").style.display = "none";
                $("SendEmailsButton").style.display = "inline";
            }

        }

        function init() {
            $("SendEmailsButton").onclick = startProcess;
            $("CancelEmailProcessButton").onclick = cancelProcess;
            // kinda quick but we are only proccing 100 emails for demo
            window.setInterval(getProgress, 1000);


        }
    </script>

</head>
<body onload="init()">
    EmailProcessStatus:<span id="EmailProcessStatus"></span><br />
    EmailProcessProgress:<span id="EmailProcessProgress"></span><br />
    EmailProcessMessage:<span id="EmailProcessMessage"></span><br />
    <input type="button" id="SendEmailsButton" value="SendEmails" style="display: none" />
    <input type="button" id="CancelEmailProcessButton" value="CancelEmailProcess" style="display: none" />
</body>
</html>
时光礼记 2024-08-28 15:53:30

那么用户必须保持浏览器窗口打开,直到所有电子邮件发送完毕?听起来不太好。我将使用由 cron 运行的守护进程或简单脚本来解决这个问题(并检查数据库是否有要发送的内容),在 Windows 上我希望你可以做类似的事情(编写 Windows 服务等)。这是一个纯粹的服务器端任务,我认为 ajaxifying 它表明 Web 应用程序的作者无法以更好的方式实现它,它甚至可能使您的 Web 应用程序在 thedailywtf.com 上被提及:)

So user will have to leave the browser window open until all the e-mails are sent? Does not sound very good. I would solve this using a daemon or simple script that is run by cron (and checks db if there is something to send), on Windows I hope you can do something similar (write Windows service etc.). This is a purely server-side task, I think ajaxifying it shows that author of the web app wasn't able to make it in a better way, it may even make your web app to be mentioned on thedailywtf.com :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文