异步使用的 HttpListener 同步运行

HttpListener Used Asynchronously Behaves Synchronously

我正在使用 System.Net.HttpListener BeginGetContext/EndGetContext 来同时处理多个 http 请求。这在我的 16 核 Windows 7 SP1 桌面上运行良好,它同时处理 16 个请求。在 Windows Server 2012 R2 16 Processor VM 上,20 个请求中的前 2 个是并发处理的,然后是顺序处理的请求,例如第三个请求的响应必须在查看第四个请求的请求之前发送。

我希望服务器以与桌面类似的方式处理请求 m/c 即。在 8 秒内处理 20 个请求,而不是当前的 95 秒。

以下日志显示了 windows 7 机器上的行为(良好)。客户端和服务器进程都是 运行 on the Windows 7 m/c.

这是客户端日志。每行包含客户端的原始请求,服务器回显并加上服务器处理请求的时间。原始请求包含序列号和客户端发出请求的时间。

请注意,所有请求都是在整点后 12:46 分钟发出的,12:51 响应了 16 个请求,12:54 响应了最后一个请求。

http://localhost:8894/dostuff?val=1-client-12:46-server-12:51
http://localhost:8894/dostuff?val=17-client-12:46-server-12:51
http://localhost:8894/dostuff?val=15-client-12:46-server-12:51
http://localhost:8894/dostuff?val=2-client-12:46-server-12:51
http://localhost:8894/dostuff?val=7-client-12:46-server-12:51
http://localhost:8894/dostuff?val=3-client-12:46-server-12:51
http://localhost:8894/dostuff?val=13-client-12:46-server-12:51
http://localhost:8894/dostuff?val=18-client-12:46-server-12:51
http://localhost:8894/dostuff?val=9-client-12:46-server-12:51
http://localhost:8894/dostuff?val=14-client-12:46-server-12:51
http://localhost:8894/dostuff?val=0-client-12:46-server-12:51
http://localhost:8894/dostuff?val=6-client-12:46-server-12:51
http://localhost:8894/dostuff?val=10-client-12:46-server-12:51
http://localhost:8894/dostuff?val=5-client-12:46-server-12:51
http://localhost:8894/dostuff?val=19-client-12:46-server-12:51
http://localhost:8894/dostuff?val=11-client-12:46-server-12:51
http://localhost:8894/dostuff?val=12-client-12:46-server-12:52
http://localhost:8894/dostuff?val=16-client-12:46-server-12:53
http://localhost:8894/dostuff?val=8-client-12:46-server-12:53
http://localhost:8894/dostuff?val=4-client-12:46-server-12:54

以下日志显示了 Windows Server 2012 计算机上的行为(错误)。 Windows Server 2012 m/c.

上的客户端和服务器进程都是 运行

请注意,前 2 个请求是并发处理的,但每个后续请求需要连续 5 秒。

请注意,前 2 个请求是并发处理的,但每个后续请求需要连续 5 秒。所有请求均在整点后 46 分 39 秒发送。前 2 个请求在每小时 46 分 44 秒得到响应,但最后一个响应在每小时 48 分 14 秒收到。

http://localhost:8895/dostuff?val=5-client-46:39-server-46:44
http://localhost:8895/dostuff?val=1-client-46:39-server-46:44
http://localhost:8895/dostuff?val=2-client-46:39-server-46:49
http://localhost:8895/dostuff?val=6-client-46:39-server-46:54
http://localhost:8895/dostuff?val=3-client-46:39-server-46:59
http://localhost:8895/dostuff?val=4-client-46:39-server-47:4
http://localhost:8895/dostuff?val=7-client-46:39-server-47:9
http://localhost:8895/dostuff?val=9-client-46:39-server-47:14
http://localhost:8895/dostuff?val=8-client-46:39-server-47:19
http://localhost:8895/dostuff?val=10-client-46:39-server-47:24
http://localhost:8895/dostuff?val=11-client-46:39-server-47:29
http://localhost:8895/dostuff?val=12-client-46:39-server-47:34
http://localhost:8895/dostuff?val=13-client-46:39-server-47:39
http://localhost:8895/dostuff?val=14-client-46:39-server-47:44
http://localhost:8895/dostuff?val=15-client-46:39-server-47:49
http://localhost:8895/dostuff?val=16-client-46:39-server-47:54
http://localhost:8895/dostuff?val=18-client-46:39-server-47:59
http://localhost:8895/dostuff?val=17-client-46:39-server-48:4
http://localhost:8895/dostuff?val=19-client-46:39-server-48:9
http://localhost:8895/dostuff?val=0-client-46:39-server-48:14

下面的代码可能会提供一些线索,但我怀疑它更有可能是服务器上的一些配额或节流问题。

// SERVER build with "csc program.cs" run as program.exe
using System;
using System.Net;
using System.Text;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        HttpListener listenerLocal = new HttpListener();
        listenerLocal.Prefixes.Add("http://*:8895/");
        listenerLocal.Start();
        while (true)
        {
            //var result = listener.BeginGetContext(RequestCallback, listener);
            var resultLocal = listenerLocal.BeginGetContext((result) =>
            {
                HttpListener listener = (HttpListener)result.AsyncState;
                HttpListenerContext context = listener.EndGetContext(result);
                Thread.Sleep(5000);
                byte[] buffer = Encoding.UTF8.GetBytes(
                    context.Request.Url.OriginalString + string.Format(
                    "-server-{0}:{1}", DateTime.Now.Minute, DateTime.Now.Second));
                context.Response.ContentLength64 = buffer.Length;
                System.IO.Stream output = context.Response.OutputStream;
                output.Write(buffer, 0, buffer.Length);
                output.Close();
            }
            , listenerLocal);
            resultLocal.AsyncWaitHandle.WaitOne();
        }
    }
}

// CLIENT build with "csc program.cs" run as program.exe
using System;
class Program
{
    static void Main(string[] args)
    {
        for (int ii = 0; ii < 20; ii++)
        {
            var thr = new System.Threading.Thread((ctr) =>
            {
                var data = new System.Net.WebClient().OpenRead(
                  string.Format("http://localhost:8895/dostuff?val={0}-client-{1}:{2}"
                  ,ctr, DateTime.Now.Minute, DateTime.Now.Second));
                var reader = new System.IO.StreamReader(data);
                Console.WriteLine(reader.ReadToEnd());
                data.Close();
                reader.Close();
            });
            thr.Start(ii);
        }
        Console.ReadLine();
    }

}

ThreadPool 不是突发任务的最佳选择,相反你可以使用普通的 Thread

将此用于您的服务器

using System;
using System.Text;
using System.Net;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        HttpListener listenerLocal = new HttpListener();
        listenerLocal.Prefixes.Add("http://*:8895/");
        listenerLocal.Start();    

        int count = 0;
        while (true)
        {
            if (count == 20)
                continue;

            Interlocked.Increment(ref count);

            var thr = new Thread(ctr =>
            {
                var l = ctr as HttpListener;
                HttpListenerContext context = l.GetContext();

                Thread.Sleep(5000);

                byte[] buffer = Encoding.UTF8.GetBytes(context.Request.Url.OriginalString + string.Format(
                    "-server-{0}:{1}", DateTime.Now.Minute, DateTime.Now.Second));
                context.Response.ContentLength64 = buffer.Length;
                System.IO.Stream output = context.Response.OutputStream;
                output.Write(buffer, 0, buffer.Length);
                output.Close();

                Interlocked.Decrement(ref count);
            });
            thr.Start(listenerLocal);
        }
    }
}