ASP.NET Core 达到指定时间时如何向用户推送通知
最优实现方案整理
你之前依赖HttpRequest触发查询的方案确实不可靠,一方面没有请求流入时任务完全不会执行,另一方面高频请求下反复查库也会造成不必要的性能损耗,以下是不同场景下的可选实现方案:
方案1:ASP.NET Core 内置后台托管服务(轻量场景首选)
不需要依赖外部中间件,直接和你现有项目集成,根据是否接受第三方依赖分为两种实现:
引入第三方调度库(推荐,省掉大量容错逻辑开发)
用Hangfire作为调度框架,自带任务持久化、失败重试、分布式锁能力,直接复用你现有的SQL Server作为存储即可,实现步骤如下:- 安装Nuget包:
Hangfire.Core、Hangfire.SqlServer、Hangfire.AspNetCore - Program.cs中注册服务:
builder.Services.AddHangfire(config => config .UseSqlServerStorage(builder.Configuration.GetConnectionString("你的SQL Server连接字符串名称"))); builder.Services.AddHangfireServer(); // 注册你自己的通知推送服务 builder.Services.AddScoped<NotificationService>();- 实现推送逻辑:
public class NotificationService { public async Task SendScheduledNotification(int notificationId) { // 1. 从数据库查询对应通知内容、接收用户信息 // 2. 执行推送(APP/短信/邮件等逻辑) // 3. 标记该条通知为已发送,避免重复推送 } }- 项目启动时批量初始化待执行任务,新增通知时也实时添加调度即可:
// 项目启动时扫表,添加所有未到时间的待推送任务 var pendingNotifications = await dbContext.Notifications .Where(n => !n.IsSent && n.ScheduledTime > DateTime.Now) .ToListAsync(); foreach (var item in pendingNotifications) { // 指定任务执行时间为你表中存储的推送时间 BackgroundJob.Schedule<NotificationService>( s => s.SendScheduledNotification(item.Id), item.ScheduledTime ); }注:多实例分布式部署场景下,Hangfire的SQL Server存储自带分布式锁,不会出现多实例重复执行同个推送任务的问题,还自带管控面板可查看任务执行状态。
- 安装Nuget包:
完全原生实现(不引入第三方依赖)
用ASP.NET Core自带的BackgroundService做常驻后台服务,定期轮询数据库拉取待推送任务,降低查库频率:public class NotificationBackgroundService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; // 轮询间隔可根据你的业务精度要求调整,建议不低于1分钟 private readonly TimeSpan _pollInterval = TimeSpan.FromMinutes(5); public NotificationBackgroundService(IServiceScopeFactory scopeFactory) { _scopeFactory = scopeFactory; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<你的DbContext类>(); // 拉取接下来一个轮询周期内需要推送的通知 var upcomingNotifications = await dbContext.Notifications .Where(n => !n.IsSent && n.ScheduledTime <= DateTime.Now.Add(_pollInterval) && n.ScheduledTime > DateTime.Now) .ToListAsync(stoppingToken); foreach (var notification in upcomingNotifications) { var delay = notification.ScheduledTime - DateTime.Now; if (delay > TimeSpan.Zero) { // 后台延迟到指定时间执行推送 _ = Task.Run(async () => { await Task.Delay(delay, stoppingToken); // 执行推送逻辑,标记为已发送 }, stoppingToken); } } // 等待下一个轮询周期 await Task.Delay(_pollInterval, stoppingToken); } } }注册服务即可生效:
builder.Services.AddHostedService<NotificationBackgroundService>();
方案2:独立调度服务(中大规模场景)
如果你的推送量很大,或者不想让调度逻辑和Web服务耦合,单独部署一个专用的调度服务即可,调度框架还是可以用Hangfire或者Quartz.NET,和Web服务共用同一个SQL Server数据库即可,优势是Web服务的重启、扩缩容完全不会影响调度任务的稳定性。
内容的提问来源于stack exchange,提问作者mohamad rahimi
相关产品推荐
相关产品推荐

