2016-07-10 132 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()); 
    } 
}