【问题标题】:Finish current requests when stopping self-hosted owin server停止自托管 owin 服务器时完成当前请求
【发布时间】:2014-11-18 16:06:06
【问题描述】:

我将 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 中间件,该中间件将跟踪当前正在处理的请求并将停止操作推迟到一切完成。中间件还会在停止阶段短路所有请求。但这个解决方案对我来说并不好。

【问题讨论】:

  • 对我来说听起来是个好主意。我认为您需要 something 来缩短新请求,并且在管道中放置一些东西似乎是正确的地方。对于跟踪请求,您可以在请求开始和结束时使用基本的 Interlocked.Increment / Decrement 计数器,以便您知道最后一个请求何时完成。

标签: c# .net owin self-hosting katana


【解决方案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 他们正在谈论类似的方法。

【讨论】:

  • 您的代码不是完全线程安全的。考虑这种情况:requestCount 为 0,调用了 ShutDown(),但同时有一个新请求到达,但仍将 shutDownStateOn 评估为 false。 ShutDown() 继续,返回并且主线程将 GetRequestCount() 评估为 0,因此应用程序停止。但是,上述请求仍在运行。
  • 我发现了另一个问题:当await next.Invoke(environment);你只知道这个中间件以及可能所有后续的中间件都完成了,但是之后通过网络传输生成的响应需要一些时间。因此,如果您在 GetRequestCount() 为 0 后立即终止应用程序,则传输响应可能会中断。
猜你喜欢
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-25
  • 1970-01-01
相关资源
最近更新 更多