从Controller返回,无需等待异步方法完成

时间:2016-03-19 04:53:34

标签: c# asynchronous asp.net-web-api

我已经读过自己的蓝色,希望有一个简单的答案。

我有一个网络API,可以处理来自各种应用程序的遥测技术#34;在我的一个控制器中,我希望收到一个请求,将错误记录到我的中央监控数据库并尽可能快地返回响应(我没有真正的方法知道调用者的最终性能有多关键)并且已经成为制作初始Web服务请求的重要因素。)

基本上,我正在寻找的是这样的:

public IHttpActionResult Submit() {
    try {
        var model = MyModel.Parse(Request.Content.ReadAsStringAsync().Result);

        // ok, I've got content, now log it but don't wait
        // around to see the results of the logging, just return
        // an Accepted result and begone

        repository.SaveSubmission(model); // <-- fire and forget, don't wait

        return Accepted();

    } catch (Exception)
        return InternalServerError();
    }
}

它似乎应该是直截了当的,但显然不是。我已经阅读过任何数量的各种帖子,表明来自是的所有内容,只需使用Task.Run()这是一个可怕的错误,你永远无法达到你想要的效果!

我的场景中的问题似乎是这个过程可能会因为它在ASP.NET工作进程上运行而在进程中间终止,而不管调用异步方法的不同方式如何(I&#39;我花了最近两个小时左右阅读各种SO问题和Stephen Cleary的博客......哇)。

如果在这种情况下的根本问题是方法I&#39; d&#39;火灾和忘记&#39;绑定到http上下文并受ASP.NET工作进程提前终止,然后我的问题变成了......

有没有办法从ASP.NET上下文中删除此方法/任务/进程?一旦将该请求解析到模型中,我自己就没有更具体的需要在http上下文中操作。如果有一种简单的方法我可以将它移出那里(因此让事情继续禁止网站/ apppool重启),那就太棒了。

为了尽职尽责,让我们说我摆脱了控制器中的存储库上下文并将其委托给其他一些上下文:

public IHttpActionResult Submit() {
    try {
        var model = MyModel.Parse(Request.Content.ReadAsStringAsync().Result);

        SomeStaticClass.SaveSubmission(model); // <-- fire and forget, don't wait

        return Accepted();

    } catch (Exception)
        return InternalServerError();
    }
}

...那么唯一需要的是&#34;交叉线&#34;是模型本身 - 没有其他代码逻辑依赖。

当然,我可能会成为一个小山丘 - 无论如何,插入数据库不会花费一小部分时间......看起来应该很容易,而且我&# 39; m显然太固执而不能满足于#34;足够好&#34;今晚。

2 个答案:

答案 0 :(得分:3)

好的,发现了一些对我的方案实际上有帮助的东西。它的基本要点似乎是不要这样做。

为了正确地执行此操作,需要将其提交给分布式体系结构中的单独组件(例如,某种类型的消息或服务队列,其可以单独拾取以进行处理)。这似乎是完全打破ASP.NET工作进程的唯一方法。

一个S / O评论(转到另一个S / O帖子)引导我发布两篇我在发布前尚未见过的文章:一篇是Stephen Cleary,另一篇是Phil Haack。

感兴趣的帖子:How to queue background tasks in ASP.NET Web API

斯蒂芬的火与忘记ASP.NET博客文章(非常好,希望我先找到这个):http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html

菲尔的文章:http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/

Stephen的以下项目也可能会引起关注:https://github.com/StephenCleary/AspNetBackgroundTasks

我以为我会删除我的问题,但后来认为我花了这么长时间才找到我的答案,可能是另一个漂浮在SO周围的问题不会受到伤害......

(在这种特殊情况下,只要写入数据库,提交到另一个服务就会接近,所以我可能会放弃这种api方法的异步处理,但至少现在我知道我什么时候实际上确实需要这样做)

答案 1 :(得分:1)

数据库插入不应该花费太长时间,您必须将该处理卸载到后台任务。对于初学者来说,只是将任务写入队列(或者,正如您所建议的那样,将其移交给服务)将花费同样的时间,但任何一种方法都应该是亚秒级。

但是,如果时间对您来说至关重要,那么加快响应时间的一种方法是使用某种形式的内存缓存使数据库尽可能快地写入,这样对物理数据库存储的较慢写入就是排队的背景任务。大容量站点经常使用内存数据库来实现这种行为(我从来不需要一个,所以无法帮助您选择产品),但您也可以使用每个应用程序实例的对象列表自己编写代码和某种形式的背景循环。

这是您链接的那些文章适用的地方,并且它变得复杂,因此预构建的实现几乎总是最好的方法 - 如果您想要预先构建的“即发即忘”实现,请查看HangFire