2014-09-24 103 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

相关问题