31

这会是多线程的可扩展 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 的原因是因为 govt。我工作的区域需要极小的攻击表面积。

4

4 回答 4

25

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

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

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

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

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

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

简介: 如何编写连接接受循环对服务器的可伸缩性至关重要。

于 2012-09-24T00:34:01.020 回答
5

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

然后很自然地,正如评论所指出的那样;不使用 IIS 意味着您需要非常认真地对待 IIS 为您“免费”提供的很多东西的安全性。

于 2012-09-13T22:52:18.373 回答
1

我知道我在这方面迟到了,但我在 NuGet 上发布了一个库(来源在这里https://github.com/jchristn/WatsonWebserver ),它封装了一个异步网络服务器。

于 2017-03-08T20:21:08.897 回答
-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();

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

于 2020-05-20T05:19:17.040 回答