2016-07-10 85 views
2

我有一个API端点需要尽快返回给调用者。它目前返回1ms以下。但是,如果我在数据库中记录了一些内容,现在需要接近10毫秒。请求完成后的处理

你会如何处理完成请求,然后在请求完成后进行某种处理?我尝试了Response.Body.Flush(),但是没有完成请求,它仍然需要全部10ms。它看起来像是发送/清除有效负载,但请求仍然没有完成,直到操作方法完成。

它会在中间件中执行日志记录吗?

编辑:

一个解决办法,我发现是使用FluentScheduler这样的:

JobManager.Initialize(new Registry()); 
JobManager.Start(); 
Program.cs中

前host.Run(),然后从这样的活动时间表的紧迫任务:在中间件

JobManager.AddJob(() => 
{ 
    // do something... 
}, (s) => s.ToRunOnceIn(1).Seconds()); 
+1

这有点广泛,不知道您是否使用任何消息代理/总线或查看特定的代码。也许发布一些你正在使用的代码。最直接的方法是使用消息总线,然后启动一个事件并稍后记录它 – Tseng

+0

我没有使用任何类型的排队系统。我希望在框架中有一些支持这一点的东西。 – pbz

+0

Response.OnCompleted接近,但连接在运行时仍处于打开状态。 – pbz

回答

2

你需要以某种方式启动一个不同的线程。放在管道前面的中间件会让您有机会在请求完成之前完成工作。如果你从那里旋转线程,那么它可能会工作。

你可能想使用某种producer-consumer pattern,所以你不会杀死你的服务器。否则,如果每个请求都启动一个立即生效并且同时有很多请求的线程,那么最终可能会耗尽资源。生产者 - 消费者会帮助你节制这项工作。

如果您不急,您可以等待一周左右,我将为ASP.NET提供文件记录器,然后您会看到类似的实现。

+0

你们不是从微软总是告诉我们不要在ASP.NET中启动我们自己的线程(猜想它也应该适用于ASP.NET Core),因为它会以ASP.NET管理它处理线程池的方式搞砸异步请求? – Tseng

+0

如果您使用生产者消费者,则不必为每个请求启用新线程。 –

+0

像Quarts.NET,FluentScheduler和HangFire这样的框架似乎可以通过启动线程来工作。但是,他们并没有从我所知道的方面支持Core。 – pbz

1

记录不会帮助你,因为你仍然会请求管道内,你可能会或可能不会有必要的信息以记录你需要做的事情。

你可以异步日志,它可能给你最大的改进没有重大的架构变化:Async Programming : Introduction to Async/Await on ASP.NET

如果不工作,你可以做一些事情,是多线程的。这变得更加复杂,但仍然可行:Multithreading in C# .NET 4.5 (Part 1)

+0

我希望会有一个“管道完成后”的响应... Response.OnCompleted获得关闭,但连接仍然打开,主叫方仍然在等待我在OnCompleted中执行处理。 – pbz

+0

我认为你使用这种方法会遇到的问题是它仍然是管道的一部分,这意味着它会减慢你的整体响应速度。除非你介绍了一些异步或多线程方法,否则我认为它不会真的给你买东西。 – MichaelDotKnox

+0

如果我可以在请求结束之后挂接到管道中,即在API调用者获得完整响应之后,那么从外部世界的角度来看,它看起来像请求花费在1ms以下。是的,如果服务器承受很大压力,整体速率将从1ms增加,但这些情况很少。大量的CPU内核大部分时间都无所事事。 – pbz

0

异步是解决此问题的正确方法。在您的请求期间(只要您想要)开始异步连接/查询到数据库。只是不要等它完成。

+0

ASP.NET Core使用依赖注入,并且某些服务是有作用域的,这意味着它们只有一个与请求的生命周期相等的生命周期,并在末尾处理。我不知道如何用'ILogger '实现。你是否确定**它没有作用域?否则,这种解决方案将无法正常工作,因为它有可能在它完成之前处理掉(如果请求比记录操作快得多) – Tseng

+0

糟糕,这应该是对另一个答案的评论。虽然在范围界定方面很好,但我没有考虑与这些资源的互动。 –

0

这只是一个想法,所以如果我走了,不要倒票。你能用HttpResponse.RegisterForDispose()吗?例如:

public class JobToRunAfterRequestIsComplete : IDisposable { 
    public void Dispose() 
    { 
     // Do your post-processing here. 
    } 
} 

public class YourController { 
    public void YourAction() { 
     // Do your normal processing here 
     ... 
     // Register your post processing here 
     this.Response.RegisterForDispose(new JobToRunAfterRequestIsComplete()); 
    } 
}