如何在Hangfire中配置速率限制以适配邮件服务的限流规则?
在Hangfire中配置邮件发送的速率限制方案
完全可以在Hangfire中配置速率限制,确保邮件发送不超出服务商的每分钟100封、每小时1000封限制,以下是两种实用实现方式:
方式一:使用第三方限流扩展
通过Hangfire.RateLimit包可以快速实现多维度速率限制:
- 安装NuGet包:
Install-Package Hangfire.RateLimit
- 在Hangfire配置中添加限流规则:
services.AddHangfire(config => { config.UseRateLimiter(new RateLimiterOptions { Limits = new List<RateLimit> { // 每分钟100封限制 new RateLimit { Key = "email-job", Period = TimeSpan.FromMinutes(1), Limit = 100 }, // 每小时1000封限制 new RateLimit { Key = "email-job", Period = TimeSpan.FromHours(1), Limit = 1000 } } }); });
- 给邮件发送任务标记限流键:
[RateLimit("email-job")] public async Task SendEmailAsync(string to, string subject, string body) { // 邮件服务发送逻辑 }
方式二:自定义限流过滤器(无依赖)
如果不想引入第三方包,可通过Hangfire的JobFilterAttribute自定义限流逻辑,结合缓存实现计数控制:
- 实现自定义限流过滤器:
public class EmailRateLimitFilter : JobFilterAttribute, IElectStateFilter { private readonly IDistributedCache _distributedCache; public EmailRateLimitFilter(IDistributedCache distributedCache) { _distributedCache = distributedCache; } public async void OnStateElection(ElectStateContext context) { var now = DateTime.UtcNow; var minuteKey = $"email-limit:minute:{now:yyyyMMddHHmm}"; var hourKey = $"email-limit:hour:{now:yyyyMMddHH}"; // 检查分钟限制 var minuteCount = await _distributedCache.GetAsync<int>(minuteKey) ?? 0; if (minuteCount >= 100) { context.CandidateState = new ScheduledState(TimeSpan.FromMinutes(1)) { Reason = "触发每分钟邮件限制,延迟执行" }; return; } // 检查小时限制 var hourCount = await _distributedCache.GetAsync<int>(hourKey) ?? 0; if (hourCount >= 1000) { context.CandidateState = new ScheduledState(TimeSpan.FromHours(1)) { Reason = "触发每小时邮件限制,延迟执行" }; return; } // 更新计数 await _distributedCache.SetAsync(minuteKey, minuteCount + 1, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1) }); await _distributedCache.SetAsync(hourKey, hourCount + 1, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }); } }
- 注册过滤器到Hangfire:
services.AddHangfire(config => { config.GlobalJobFilters.Add(new EmailRateLimitFilter(serviceProvider.GetRequiredService<IDistributedCache>())); });
- 也可为单个任务单独应用过滤器:
BackgroundJob.Enqueue<EmailService>(x => x.SendEmailAsync(to, subject, body)) .WithFilter(new EmailRateLimitFilter(distributedCache));
注意事项
- 分布式部署场景下,必须使用Redis等分布式缓存替代内存缓存,避免多节点计数不一致
- 需处理任务重试逻辑,可在过滤器中判断任务是否为重试,避免重复计数
- 建议将邮件任务分配到专属队列,便于单独管控限流规则
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

