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改造下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[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");//不管邮件是否发送成功就返回响应了
}

  

posted @   悠哉大斌  阅读(3776)  评论(3编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示