设置后台进程并允许用户无需等待即可继续

发布于 2024-08-07 21:08:58 字数 3026 浏览 3 评论 0原文

我有一个时事通讯工具,我正在尝试将其设置为作为后台进程运行来发送电子邮件。下面的代码工作没有任何问题,但我遇到的问题是它很慢。

如果有 50 封电子邮件要发送,对于最终用户来说,发送速度可能会非常慢,因为他们必须盯着屏幕长达 1 分 30 秒。如果他们的客户向更多的人发送电子邮件,这对我来说就成为一个更大的问题。

我单独发送每封邮件而不是发送 1 封邮件并密件抄送电子邮件列表的原因是,每封电子邮件都包含每个用户的某些特定内容 - 例如取消订阅链接代码、邮件开头的个人姓名等。

我正在寻找一个解决方案,我可以让用户单击一个按钮,让 .net 在后台运行发送电子邮件部分,同时将前端用户带到一个页面,显示他们的电子邮件正在发送。理想情况下,所有这些发生的时间不应超过常规回发的时间 - 而不是当前的几分钟。

关于如何最好地实现这一目标有什么想法吗?

感谢您的帮助, 富有的

if (Page.IsPostBack)
    {
        if (JustMeButton.Checked)
        {
            SendMail("emailme@address", EmailTemplate);
        }
        if (EveryoneButton.Checked)
        {
            //setup background process
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();

            //bring user to next screen and display message
            Response.Redirect("emailSendingMessageScreen.aspx");
        }
    }

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    DataTable emailTable = (DataTable)Session["emailTable"];
    foreach (DataRow row in emailTable.Rows)
    {
        SendMail(row["email"], row["name"], EmailTemplate);
    }
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!(e.Error == null))
    {
        SendMail("admin@address", "Error sending <br><br>" + e.Error.Message);
    }
    else
    {
        SendMail("admin@address", "emails sent successfully.");
    }
    //clear out the sessions created for sending this email
    Session.Remove("emailTable");
}

private void SendMail(string email, string emailMessage)
{
    MailMessage mailMessage = new MailMessage();
    mailMessage.From = new MailAddress("from@address");
    mailMessage.To.Add(new MailAddress(email));
    mailMessage.Subject = Server.HtmlEncode(EmailSubject.Text.Trim());
    mailMessage.Body = emailMessage;
    mailMessage.IsBodyHtml = true;

    SmtpClient smtpClient = new SmtpClient();

    Object userState = mailMessage;

    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
    smtpClient.Timeout = 10000;

    try
    {
        smtpClient.SendAsync(mailMessage, userState);
    }
    catch (SmtpException smtpExc)
    {
        MailMessageTxt.Text += "Error Code: " + smtpExc.StatusCode;
        MailMessageTxt.Visible = true;
    }
    catch (Exception ex)
    {
        MailMessageTxt.Text += "Error is: " + ex;
        MailMessageTxt.Visible = true;
    }
}

void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    MailMessage mailMessage = e.UserState as MailMessage;

    if (e.Error != null)
    {
        MailMessageTxt.Text = "Error occured, info=" + e.Error.Message;
        MailMessageTxt.Visible = true;
    }
}

I have a newsletter tool that I am trying to setup to run as a background process to send out the emails. The code below works without any issues but the problem I have is that it is slow.

If there are 50 emails to send it can be very slow for the end user as they have to stare at the screen for up to 1min 30secs. This becomes a bigger problem for me if they client is sending an email to a larger group of people.

The reason I send each mail individually as apposed to sending 1 and bcc'ing the email list is that each email contains certain specific content for each user - like unsubscribe link codes, personal name at the start of the mail, etc.

I am looking for a solution where I can let the user click on a button and have .net run the sending email part in the background while the front end user is brought to a page saying that their email is being sent. Ideally, it should take no longer than a regular postback for all that to occur - not the current few minutes.

Any thoughts on how best to achieve this?

Thanks for your help,
Rich

if (Page.IsPostBack)
    {
        if (JustMeButton.Checked)
        {
            SendMail("emailme@address", EmailTemplate);
        }
        if (EveryoneButton.Checked)
        {
            //setup background process
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = false;
            bw.WorkerSupportsCancellation = false;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();

            //bring user to next screen and display message
            Response.Redirect("emailSendingMessageScreen.aspx");
        }
    }

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    DataTable emailTable = (DataTable)Session["emailTable"];
    foreach (DataRow row in emailTable.Rows)
    {
        SendMail(row["email"], row["name"], EmailTemplate);
    }
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!(e.Error == null))
    {
        SendMail("admin@address", "Error sending <br><br>" + e.Error.Message);
    }
    else
    {
        SendMail("admin@address", "emails sent successfully.");
    }
    //clear out the sessions created for sending this email
    Session.Remove("emailTable");
}

private void SendMail(string email, string emailMessage)
{
    MailMessage mailMessage = new MailMessage();
    mailMessage.From = new MailAddress("from@address");
    mailMessage.To.Add(new MailAddress(email));
    mailMessage.Subject = Server.HtmlEncode(EmailSubject.Text.Trim());
    mailMessage.Body = emailMessage;
    mailMessage.IsBodyHtml = true;

    SmtpClient smtpClient = new SmtpClient();

    Object userState = mailMessage;

    smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
    smtpClient.Timeout = 10000;

    try
    {
        smtpClient.SendAsync(mailMessage, userState);
    }
    catch (SmtpException smtpExc)
    {
        MailMessageTxt.Text += "Error Code: " + smtpExc.StatusCode;
        MailMessageTxt.Visible = true;
    }
    catch (Exception ex)
    {
        MailMessageTxt.Text += "Error is: " + ex;
        MailMessageTxt.Visible = true;
    }
}

void smtpClient_SendCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
    MailMessage mailMessage = e.UserState as MailMessage;

    if (e.Error != null)
    {
        MailMessageTxt.Text = "Error occured, info=" + e.Error.Message;
        MailMessageTxt.Visible = true;
    }
}

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

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

发布评论

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

评论(5

猥琐帝 2024-08-14 21:08:58

我就是这样做的,在新版本中向 BeerHouse 发送新闻通讯。您现在就可以获取这本书,源代码位于 CodePlex,http://thebeerhouse.codeplex.com/
http:// professionalaspnet.com/archive/2009/10/07/ASP.NET-3.5-Problem-1320-Design-2D00-Solution.aspx
替代文本 http://Professionalaspnet.com/images/187586-fg0705.jpg

解决方案使用 AJAX 发送电子邮件,并允许用户继续浏览网站,而不必担心新闻通讯的发送。完成后,它会自行处理,用户可以根据需要进行检查。书中第七章,欣赏。

I did this very thing sending a newsletter with the BeerHouse in the new version. You can get the book now and the source code is on CodePlex, http://thebeerhouse.codeplex.com/
http://professionalaspnet.com/archive/2009/10/07/ASP.NET-3.5-Problem-1320-Design-2D00-Solution.aspx
alt text http://Professionalaspnet.com/images/187586-fg0705.jpg

The Solution uses AJAX to send the e-mails and allows the user to keep browsing around the site without being concerned about the newsletter being sent out. When it is done it just takes care of itself and the user can check on as they want. Its chapter 7 in the book, enjoy.

泪眸﹌ 2024-08-14 21:08:58

如果 ASP 工作进程因某种原因被回收,则在 ASP 页面中创建的线程将被终止。通过消息队列执行任务的 Windows 服务非常适合长时间运行的作业。另一个“技巧”解决方案是使用缓存过期,解释如下: http://www.codeproject .com/KB/aspnet/ASPNETService.aspx

A thread created within an ASP page will get killed if the ASP worker process is recycled for whatever reason. A Windows service that performs the task via a message queue is ideal for long running jobs. Another "trick" solution is using cache expiry, explained here: http://www.codeproject.com/KB/aspnet/ASPNETService.aspx

情场扛把子 2024-08-14 21:08:58

将发送电子邮件的所有工作移至单独的类并使用 ThreadPool 运行它

MailSender sender = new MailSender(parameters. ....);
ThreadPool.EnqueueUserItem(sender.sendAllEmails)

使用后台工作程序将不起作用。当它脱离上下文时,它将被处理,这意味着在 Response.End 上

Move all the work of sending the email to separate class and run it using ThreadPool

MailSender sender = new MailSender(parameters. ....);
ThreadPool.EnqueueUserItem(sender.sendAllEmails)

Using background worker won't work. It will be disposed when it goes out of context, meaning on Response.End

棒棒糖 2024-08-14 21:08:58

我发现尝试在 ASP.NET 进程中执行此类任务是有问题的,因为您无法保证该进程将完成或成功。如果该过程被中断,您将无法恢复。我将首先将所有电子邮件保存到数据库中,然后提供一项服务来轮询此数据库或表中的新条目,以处理电子邮件的实际发送。

这样做的优点是,如果您的电子邮件提供商或 ASP.NET 进程出现故障,您不会丢失任何电子邮件,并且您有所有发送电子邮件的历史记录,其中包含发送时间、发送者等详细信息...您也可以撕掉或者更改电子邮件发送器以执行更多操作,例如发送到 Twitter 或电话短信等。这可以有效地将您的通知与应用程序分离。

我最近制作的所有应用程序都使用这种类型的模型,它已经阻止了由于服务故障和其他原因导致的电子邮件丢失。它还可以查找已通过系统的所有电子邮件并获取指标,使我能够通过在电子邮件记录中存储额外信息(例如发送原因、是否报告错误等)来优化发送电子邮件的需求。 ..可以在不更改主应用程序的情况下,根据一天中的时间或用户添加路由通知(例如,如果是电子邮件,则转到短信)等附加功能。

I have found trying to do tasks like this within the ASP.NET process is problematic as you cannot guarantee the process will complete or be successful. If the process gets cut off you have no recovery. I would have all your emails saved to a DB first and then have a service that polls for new entries in this database or table that handles the actual sending of the emails.

The advantage to this is that if your email provider or ASP.NET process goes down you don't lose any emails and you have a history of all emails sent with their details of when, who, etc... You can also rip out or change the emailer to do more, like send to Twitter or a phone text message etc. This effectively decouples your notifications from your application.

All applications I have made recently use this type of model and it has stopped emails from being lost due to service failures and other reasons. It has also made it possible to lookup all emails that have gone through the system and get metrics that allows me to optimize the need to send emails by storing extra information in the email record like reason sent, if it's to report an error, etc... Adding on additions such as routing notifications (eg go to text message instead if email) based on time of day or user has been possible with no changes to the primary applicaton.

眼前雾蒙蒙 2024-08-14 21:08:58

只需使用 ajax 来执行该流程。让用户继续其活动,而服务器承担负担。

Simply use ajax to execute the process.let the user continue their activity while the server bares the burden.

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