多线程HttpListener,等待异步和任务

时间:2012-06-23 05:36:50

标签: c# task async-await

这是一个多线程的可扩展HttpListener的一个很好的例子吗?

这是一个真正的IIS会这样做吗?

public class Program
{
    private static readonly HttpListener Listener = new HttpListener();

    public static void Main()
    {
        Listener.Prefixes.Add("http://+:80/");
        Listener.Start();
        Listen();
        Console.WriteLine("Listening...");
        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }

    private static async void Listen()
    {
        while (true)
        {
            var context = await Listener.GetContextAsync();
            Console.WriteLine("Client connected");
            Task.Factory.StartNew(() => ProcessRequest(context));
        }

        Listener.Close();
    }

    private static void ProcessRequest(HttpListenerContext context)
    {
        System.Threading.Thread.Sleep(10*1000);
        Console.WriteLine("Response");
    }
}

我特意寻找一种不依赖于IIS的可扩展解决方案。而只是在http.sys(这是httplistener类) - 不依赖于iIS的原因是因为政府。我工作的区域需要极小的攻击面积。

4 个答案:

答案 0 :(得分:22)

我在https://github.com/JamesDunne/Aardwolf做了类似的事情,并对此做了一些广泛的测试。

请参阅https://github.com/JamesDunne/aardwolf/blob/master/Aardwolf/HttpAsyncHost.cs#L107处的代码,了解核心事件循环的实现。

我发现使用Semaphore来控制有多少并发GetContextAsync请求处于活动状态是最好的方法。本质上,主循环继续运行,直到信号量由于到达计数而阻塞线程。然后会有N个并发“连接接受”活动。每次接受连接时,都会释放信号量,并且新的请求可以取而代之。

信号量的初始值和最大值计数值需要进行一些微调,具体取决于您希望接收的负载。这是您期望的并发连接数与客户所需的平均响应时间之间的微妙平衡。值越高意味着可以在平均响应时间慢得多的情况下维持更多连接;将拒绝更少的连接。较低的值意味着可以在更快的平均响应时间内保持较少的连接;更多连接将被拒绝。

我通过实验(在我的硬件上)发现,128周围的值允许服务器在可接受的响应时间处理大量并发连接(最多1,024个)。使用您自己的硬件进行测试并相应地调整参数。

我还发现WCAT的单个实例不喜欢自己处理超过1,024个连接。因此,如果您认真对待负载测试,请使用多台带有WCAT的客户端计算机,并确保通过快速网络进行测试,例如: 10 GbE并且您的操作系统的限制不会减慢您的速度。请务必在Windows Server SKU上进行测试,因为默认情况下桌面SKU是受限制的。

<强>要点: 如何编写连接接受循环对于服务器的可伸缩性至关重要。

答案 1 :(得分:5)

从技术上讲,你是对的。为了使其可扩展,您可能希望同时运行多个GetContextAsync(性能测试需要确切知道多少,但“每个核心的一些”可能是正确的答案)。

当然,正如评论所指出的那样;不使用IIS意味着你需要非常认真地了解IIS为你提供“免费”的许多东西。

答案 2 :(得分:1)

我知道我参与聚会的时间非常晚,但我在NuGet上发布了一个库(来源https://github.com/jchristn/WatsonWebserver),它封装了一个异步网络服务器。

答案 3 :(得分:-1)

以下是一种使用取消令牌来彻底关闭监听器的模式:

try
{
    while (active)
    {
        Task<HttpListenerContext> listenTask = httpListener.GetContextAsync();
        listenTask.Wait(myCancelToken.Token);
        HttpListenerContext listenerContext = listenTask.Result;
        // Do something with listenerContext in a seperate thread or task..
    }
}
catch (System.OperationCanceledException)
{
    // This is expected!
}
httpListener.Close();

请注意,此操作应在其自己的线程或任务中执行,以防止阻塞其他代码。