mvc中使用Hangfire處理后台任務


考慮下如下代碼,在數據保存后,需要發送郵件,發送郵件是個耗時的工作。

我們的目的是,數據保存成功后,就可以返回響應了,發送郵件不重要,不需要等待郵件發送成功

[HttpPost]
public ActionResult Create(Comment model)
{
    if (ModelState.IsValid)
    {
        _db.Comments.Add(model);
        _db.SaveChanges();
 MailMessage message = new MailMessage(); message.To.Add("xx@126.com"); message.Subject = "主題是"; message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter); message.BodyEncoding = System.Text.Encoding.UTF8; message.From = new MailAddress(From); message.SubjectEncoding = System.Text.Encoding.UTF8; message.IsBodyHtml = true; SmtpClient client = new SmtpClient("relay.mail.server");
        client.Send(Message);//耗時操作 
}
return RedirectToAction("Index");
}

改成異步是否能達到這個效果呢?

答案是否定的!!雖然加入了異步方法,但是只有action里所有的代碼執行完畢后才能返回響應!

await(await表達式表示等待異步方法執行完,並取返回值,因此遇到await關鍵字,會阻塞線程) 后面的異步方法還是要執行完畢后,才會繼續執行下面的代碼,跟同步方法一樣,並不會節省時間。

所以異步可以提高效率/吞吐量,但是不能節省時間。

[HttpPost]
public async  Task<ActionResult> Create(Comment model)
{
    if (ModelState.IsValid)
    {
        _db.Comments.Add(model);
        _db.SaveChanges();

//異步范例1

HttpClient client = new HttpClient();
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Console.WriteLine("上面的異步方法是否執行完跟我沒關系,我還是執行到這里了");
string urlContents = await getStringTask;//必須等client.GetStringAsync執行完

Console.WriteLine(urlContents.Length.ToString());上面的語句執行完才輪到我。

//異步反例2
       MailMessage message = new MailMessage();          
        message.To.Add("xx@126.com");          
            message.Subject = "主題是";
            message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter);
            message.BodyEncoding = System.Text.Encoding.UTF8;
            message.From = new MailAddress(From);
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            message.IsBodyHtml = true;

            SmtpClient client = new SmtpClient("relay.mail.server");

          await  client.SendMailAsync(message);  //await 這里會阻塞線程,直到郵件發送完畢
    }
       return RedirectToAction("Index");//發送完郵件才執行到這里!
}

可以用后台線程嗎?

答案也是否定的!

IIS工作線程是用於處理請求的,不適合運行后台任務,當應用程序池回收的時候,會丟掉。

 

最后,介紹 Hangfire 

http://docs.hangfire.io/en/latest/tutorials/send-email.html#id3

mvc項目,添加nuget包 hanfire

安裝包完畢后,可以看到,默認使用了sqlserver作為存儲,並依賴Owin

 

mvc根目錄創建startup.cs,並配置sqlserver連接字符串

using Hangfire;
using Microsoft.Owin;
using Owin;
using System;

[assembly: OwinStartupAttribute(typeof(HangFire.Startup))]
namespace HangFire
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            string connectionStr = "Database=yourdb;Server=.;Uid=xxx;Pwd=xxx;Enlist=False;Pooling=true;Connection Reset=false;Trusted_Connection=no;Connect TimeOut=3000;";
            GlobalConfiguration.Configuration
                .UseSqlServerStorage(connectionStr);

            BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!"));//測試

            app.UseHangfireDashboard();
            app.UseHangfireServer();
        }
    }
}

運行mvc項目,因為在startup.cs里,加了BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!"));//測試

所以Hangfire第一次執行的時候,會在sqlserver里創建相關的表

 

下面把發郵件的action改造下

[HttpPost]
public ActionResult Create(Comment model)
{
    if (ModelState.IsValid)
    {
        _db.Comments.Add(model);
        _db.SaveChanges();
 MailMessage message = new MailMessage();          
        message.To.Add("xx@126.com");          
            message.Subject = "主題是";
            message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter);
            message.BodyEncoding = System.Text.Encoding.UTF8;
            message.From = new MailAddress(From);
            message.SubjectEncoding = System.Text.Encoding.UTF8;
            message.IsBodyHtml = true;

            SmtpClient client = new SmtpClient("relay.mail.server");
            BackgroundJob.Enqueue(() => client.Send(message));//發送工作交給Hangfire去后台處理了 
  } 
return RedirectToAction("Index");//不管郵件是否發送成功就返回響應了
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM