Hangfire+MailKit邮件任务超时连接失败,直接调用却正常的原因
Hangfire结合MailKit发送邮件时后台任务超时失败,但直接调用正常的问题
问题描述
在ASP.NET Core应用中使用Hangfire+MailKit发送邮件时,遇到以下异常情况:
- 邮件发送逻辑加入Hangfire后台任务队列后,任务因超时失败,报错:
A connection attempt failed because the connected party did not properly respond... - 直接在控制器中调用相同的邮件发送逻辑可成功执行,仅连接阶段耗时约3-4分钟。
相关代码
Program.cs中的Hangfire配置
var storageOptions = new SqlServerStorageOptions() { CommandTimeout = TimeSpan.FromMinutes(10), CommandBatchMaxTimeout = TimeSpan.FromMinutes(10), }; builder.Services.AddHangfire(configuration => configuration .SetDataCompatibilityLevel(CompatibilityLevel.Version_180) .UseSimpleAssemblyNameTypeSerializer() .UseRecommendedSerializerSettings() .UseSqlServerStorage(builder.Configuration.GetConnectionString("HangfireConnection"), storageOptions)); builder.Services.AddHangfireServer();
邮件发送逻辑
using (var client = new SmtpClient()) { client.Timeout = 300000; // 5 minutes await client.ConnectAsync(_settings.Server, _settings.Port, _settings.UseSSL); if (_settings.RequireAuth) { await client.AuthenticateAsync(_settings.Username, _settings.Password); } await client.SendAsync(mimeMessage); await client.DisconnectAsync(true); }
已尝试的操作
- 将
SmtpClient.Timeout调整为5分钟 - 将
SqlServerStorageOptions的CommandTimeout和CommandBatchMaxTimeout调整为10分钟
原因分析及解决方案
1. 异步操作未配置超时令牌
你设置的SmtpClient.Timeout仅对同步操作生效,异步方法(如ConnectAsync)需要通过CancellationToken来控制超时。Hangfire执行异步任务时会默认关联取消令牌,若任务执行时间接近Hangfire内部阈值,可能触发取消导致连接失败。
修改邮件发送代码,添加异步超时令牌:
using (var client = new SmtpClient()) { client.Timeout = 300000; // 同步操作超时设为5分钟 using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5)); // 异步操作超时令牌 await client.ConnectAsync(_settings.Server, _settings.Port, _settings.UseSSL, cts.Token); if (_settings.RequireAuth) { await client.AuthenticateAsync(_settings.Username, _settings.Password, cts.Token); } await client.SendAsync(mimeMessage, cts.Token); await client.DisconnectAsync(true, cts.Token); }
2. Hangfire Server配置未适配长耗时任务
Hangfire Server默认的服务器超时(ServerTimeout)和工作线程数可能不适合3-4分钟的长耗时任务,导致任务被误判为失败或无法及时分配线程。
修改Hangfire Server配置:
builder.Services.AddHangfireServer(options => { // 根据服务器CPU核心数调整工作线程数量,避免线程耗尽 options.WorkerCount = Environment.ProcessorCount * 2; // 延长服务器超时时间,避免长耗时任务被标记为失败 options.ServerTimeout = TimeSpan.FromMinutes(15); });
3. 网络上下文差异
控制器运行在Web应用池上下文下,可能继承了系统代理或防火墙规则;而Hangfire后台线程的网络上下文独立,可能因未配置代理导致连接SMTP服务器异常。
排查方向:检查服务器的代理设置,确保Hangfire后台线程能正常访问目标SMTP服务器,必要时在邮件发送逻辑中显式配置代理。
4. 日志排查辅助
开启Hangfire详细日志,记录任务执行的每个阶段耗时,明确超时发生在连接、认证还是发送环节,便于精准定位问题。
内容的提问来源于stack exchange,提问作者bristivojevic
相关产品推荐
相关产品推荐

