2014-09-24 102 views
4

我將OWIN服務器作爲控制檯應用程序的一部分。你可以在這裏看到的主要方法:在停止自託管的owin服務器時完成當前請求

class Program 
{ 
    public static ManualResetEventSlim StopSwitch = new ManualResetEventSlim(); 

    static void Main(string[] args) 
    { 
     Console.CancelKeyPress += (s, a) => 
     { 
      a.Cancel = true; 
      StopSwitch.Set(); 
     }; 

     using (WebApp.Start<Startup>("http://+:8080/")) 
     { 
      Console.WriteLine("Server is running..."); 
      Console.WriteLine("Press CTRL+C to stop it."); 
      StopSwitch.Wait(); 
      Console.WriteLine("Server is stopping..."); 
     } 

     Console.ReadKey(); 
     Console.WriteLine("Server stopped. Press any key to close app..."); 
    } 
} 

當請求的處理是長一點,並在同一時間用戶按下CTRL + C停止請求處理立即停止,並且不發送響應應用程序。有沒有可能改變這種行爲?我想拒絕所有新請求,但要等到當前正在處理的請求完成並在此之後停止服務器。

我最初的想法是創建OWIN中間件,它將跟蹤當前正在處理的請求並推遲停止操作,直到完成所有操作。中間件也會在停止階段將所有請求短路。但是這個解決方案對我來說聽起來不太好。

+0

聽起來對我來說是個不錯的主意。我認爲你需要_something_來短路新的請求,並且將一些東西放在一邊似乎是正確的做法。對於跟蹤請求,您可以使用基本['Interlocked.Increment'](http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment(v = vs.110).aspx) /請求開始和結束時的'遞減'計數器,以便知道上次請求何時完成。 – Rhumborl 2014-09-29 08:28:37

回答

1

我與中間件的建議的方法結束:

public class ShutDownMiddleware 
{ 
    private readonly Func<IDictionary<string, object>, Task> next; 
    private static int requestCount = 0; 
    private static bool shutDownStateOn = false; 

    public static void ShutDown() 
    { 
     shutDownStateOn = true; 
    } 

    public static int GetRequestCount() 
    { 
     return requestCount; 
    } 

    public ShutDownMiddleware(Func<IDictionary<string, object>, Task> next) 
    { 
     this.next = next; 
    } 

    public async Task Invoke(IDictionary<string, object> environment) 
    { 
     if (shutDownStateOn) 
     { 
      environment["owin.ResponseStatusCode"] = HttpStatusCode.ServiceUnavailable; 
      return; 
     } 

     Interlocked.Increment(ref requestCount); 
     try 
     { 
      await next.Invoke(environment); 
     } 
     finally 
     { 
      Interlocked.Decrement(ref requestCount); 
     } 
    } 
} 

這是登記在管道中的第一中間件和程序的主要方法,我可以這樣使用它:

public class Program 
{ 
    public static ManualResetEventSlim StopSwitch = new ManualResetEventSlim(); 

    static void Main(string[] args) 
    { 
     Console.CancelKeyPress += (s, a) => 
     { 
      a.Cancel = true; 
      StopSwitch.Set(); 
     }; 

     using (WebApp.Start<Startup>("http://+:8080/")) 
     { 
      Console.WriteLine("Server is running..."); 
      Console.WriteLine("Press CTRL+C to stop it."); 
      StopSwitch.Wait(); 
      Console.WriteLine("Server is stopping..."); 
      ShutDownMiddleware.ShutDown(); 
      while (ShutDownMiddleware.GetRequestCount() != 0) 
      { 
       Thread.Sleep(TimeSpan.FromSeconds(1)); 
      } 
     } 
    } 
} 

我也發現這個:https://katanaproject.codeplex.com/workitem/281 他們正在談論類似的方法。

+0

您的代碼不完全是線程安全的。考慮這種情況:requestCount爲0,ShutDown()被調用,但同時一個新的請求到達並仍然評估爲false的shutDownStateOn。 ShutDown()繼續,返回並且主線程將GetRequestCount()評估爲0,以便應用程序停止。但是,上述請求仍在運行。 – 2016-10-26 06:15:09

+0

我發現了另一個問題:當等待next.Invoke(環境)時,你只知道這個中間件以及可能所有後續的中間件都已經完成,但是之後需要一些時間來傳輸生成的響應通過網絡。因此,如果您在GetRequestCount()獲得0後立即終止應用程序,則傳輸響應可能會中斷。 – 2016-10-26 10:48:19

相關問題