问题描述
我们正在使用 .NET Core Web Api,并寻找一种轻量级的解决方案来将可变强度的请求记录到数据库中,但不希望客户端等待保存过程.
不幸的是,在 dnx
中没有实现 HostingEnvironment.QueueBackgroundWorkItem(..)
,并且 Task.Run(..)
并不安全.
有什么优雅的解决方案吗?
We are working with .NET Core Web Api, and looking for a lightweight solution to log requests with variable intensity into database, but don't want client's to wait for the saving process.
Unfortunately there's no HostingEnvironment.QueueBackgroundWorkItem(..)
implemented in dnx
, and Task.Run(..)
is not safe.
Is there any elegant solution?
推荐答案
QueueBackgroundWorkItem
不见了,但我们得到的是 IApplicationLifetime
而不是 IRegisteredObject
,这是前一个正在使用的.我认为,在这种情况下,它看起来很有希望.
QueueBackgroundWorkItem
is gone, but we've got IApplicationLifetime
instead of IRegisteredObject
, which is being used by the former one. And it looks quite promising for such scenarios, I think.
这个想法(我仍然不太确定,如果它是一个非常糟糕的一个;因此,当心!)是注册一个单例,它产生 并 观察新任务.在该单例中,我们还可以注册一个停止事件",以便正确等待仍在运行的任务.
The idea (and I'm still not quite sure, if it's a pretty bad one; thus, beware!) is to register a singleton, which spawns and observes new tasks. Within that singleton we can furthermore register a "stopped event" in order to proper await still running tasks.
这个概念"可用于短期运行的东西,如日志记录、邮件发送等.事情,应该不会花费太多时间,但会对当前请求产生不必要的延迟.
This "concept" could be used for short running stuff like logging, mail sending, and the like. Things, that should not take much time, but would produce unnecessary delays for the current request.
public class BackgroundPool
{
protected ILogger<BackgroundPool> Logger { get; }
public BackgroundPool(ILogger<BackgroundPool> logger, IApplicationLifetime lifetime)
{
if (logger == null)
throw new ArgumentNullException(nameof(logger));
if (lifetime == null)
throw new ArgumentNullException(nameof(lifetime));
lifetime.ApplicationStopped.Register(() =>
{
lock (currentTasksLock)
{
Task.WaitAll(currentTasks.ToArray());
}
logger.LogInformation(BackgroundEvents.Close, "Background pool closed.");
});
Logger = logger;
}
private readonly object currentTasksLock = new object();
private readonly List<Task> currentTasks = new List<Task>();
public void SendStuff(Stuff whatever)
{
var task = Task.Run(async () =>
{
Logger.LogInformation(BackgroundEvents.Send, "Sending stuff...");
try
{
// do THE stuff
Logger.LogInformation(BackgroundEvents.SendDone, "Send stuff returns.");
}
catch (Exception ex)
{
Logger.LogError(BackgroundEvents.SendFail, ex, "Send stuff failed.");
}
});
lock (currentTasksLock)
{
currentTasks.Add(task);
currentTasks.RemoveAll(t => t.IsCompleted);
}
}
}
这样的 BackgroundPool
应该注册为单例,并且可以通过 DI 被任何其他组件使用.我目前正在使用它来发送邮件,它工作正常(在应用关闭期间也测试过邮件发送).
Such a BackgroundPool
should be registered as a singleton and can be used by any other component via DI. I'm currently using it for sending mails and it works fine (tested mail sending during app shutdown too).
注意: 在后台任务中访问诸如当前 HttpContext
之类的东西应该不起作用.旧解决方案 使用 UnsafeQueueUserWorkItem
无论如何都要禁止.
Note: accessing stuff like the current HttpContext
within the background task should not work. The old solution uses UnsafeQueueUserWorkItem
to prohibit that anyway.
你怎么看?
更新:
有了 ASP.NET Core 2.0,后台任务有了新的东西,ASP.NET Core 2.1 变得更好:使用 IHostedService 和 BackgroundService 类在 .NET Core 2.x webapps 或微服务中实现后台任务
With ASP.NET Core 2.0 there's new stuff for background tasks, which get's better with ASP.NET Core 2.1: Implementing background tasks in .NET Core 2.x webapps or microservices with IHostedService and the BackgroundService class
这篇关于.NET Core 中 HostingEnvironment.QueueBackgroundWorkItem 的替代解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!