c# - 具有异步/等待和处理高负载的简单任务返回异步 HtppListener

标签 c# .net http httplistener system.net

我创建了以下简单的 HttpListener 来同时处理多个请求(在 .NET 4.5 上):

class Program {

    static void Main(string[] args) {

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://+:8088/");
        listener.Start();
        ProcessAsync(listener).ContinueWith(task => { });
        Console.ReadLine();
    }

    static async Task ProcessAsync(HttpListener listener) {

        HttpListenerContext ctx = await listener.GetContextAsync();

        // spin up another listener
        Task.Factory.StartNew(() => ProcessAsync(listener));

        // Simulate long running operation
        Thread.Sleep(1000);

        // Perform
        Perform(ctx);

        await ProcessAsync(listener);
    }

    static void Perform(HttpListenerContext ctx) {

        HttpListenerResponse response = ctx.Response;
        string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);

        // Get a response stream and write the response to it.
        response.ContentLength64 = buffer.Length;
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);

        // You must close the output stream.
        output.Close();
    }
}

我使用 Apache Benchmark Tool 对其进行负载测试。当我发出 1 个请求时,我得到一个请求的最长等待时间为 1 秒。例如,如果我发出 10 个请求,响应的最长等待时间会增加到 2 秒。

您将如何更改我的上述代码以使其尽可能高效?

编辑

在@JonSkeet 的回答后,我将代码更改如下。最初,我试图模拟一个阻塞调用,但我想这是核心问题。所以,我采纳了@JonSkeet 的建议并将其更改为 Task.Delay(1000)。现在,下面的代码给出了最大值。等待时间大约10 个并发请求需要 1 秒:

class Program {

    static bool KeepGoing = true;
    static List<Task> OngoingTasks = new List<Task>();

    static void Main(string[] args) {

        HttpListener listener = new HttpListener();
        listener.Prefixes.Add("http://+:8088/");
        listener.Start();
        ProcessAsync(listener).ContinueWith(async task => {

            await Task.WhenAll(OngoingTasks.ToArray());
        });

        var cmd = Console.ReadLine();

        if (cmd.Equals("q", StringComparison.OrdinalIgnoreCase)) {
            KeepGoing = false;
        }

        Console.ReadLine();
    }

    static async Task ProcessAsync(HttpListener listener) {

        while (KeepGoing) {
            HttpListenerContext context = await listener.GetContextAsync();
            HandleRequestAsync(context);

            // TODO: figure out the best way add ongoing tasks to OngoingTasks.
        }
    }

    static async Task HandleRequestAsync(HttpListenerContext context) {

        // Do processing here, possibly affecting KeepGoing to make the 
        // server shut down.

        await Task.Delay(1000);
        Perform(context);
    }

    static void Perform(HttpListenerContext ctx) {

        HttpListenerResponse response = ctx.Response;
        string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
        byte[] buffer = Encoding.UTF8.GetBytes(responseString);

        // Get a response stream and write the response to it.
        response.ContentLength64 = buffer.Length;
        Stream output = response.OutputStream;
        output.Write(buffer, 0, buffer.Length);

        // You must close the output stream.
        output.Close();
    }
}

最佳答案

在我看来,您最终会遇到听众的分歧。在 ProcessAsync 中,您启动一​​个新任务来监听(通过 Task.Factory.StartNew),然后在结束时调用 ProcessAsync再次方法。这怎么可能完成?目前尚不清楚这是否是您的性能问题的原因,但它看起来绝对是一个普遍的问题。

我建议将您的代码更改为只是一个简单的循环:

static async Task ProcessAsync(HttpListener listener) {
    while (KeepGoing) {
        var context = await listener.GetContextAsync();
        HandleRequestAsync(context);         
    }
}

static async Task HandleRequestAsync(HttpListenerContext context) {
    // Do processing here, possibly affecting KeepGoing to make the 
    // server shut down.
}

现在上面的代码忽略了 HandleRequestAsync 的返回值。您可能想要保留“当前正在运行”任务的列表,当您被要求关闭时,使用await Task.WhenAll(inFlightTasks) 来避免过快地关闭服务器。

另请注意,Thread.Sleep 是一个阻塞 延迟。异步延迟将是 await Task.Delay(1000)

关于c# - 具有异步/等待和处理高负载的简单任务返回异步 HtppListener,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13709946/

相关文章:

c# - 是否可以在不丢失原始引用的情况下删除列表列表中的项目?

javascript - Meteor 应用程序中没有访问控制允许来源错误

http - 如何配置nginx重定向到https ...除了一个目录

java - c# RSA 使用私钥加密

c# - 如何从 Windows Phone 8 应用程序发送电子邮件?

c# - 返回 'IList' 与 'ICollection' 与 'Collection'

rest - 在 Rest 中使用 POST 进行删除/更新?

c# - Selenium:从文件系统拖放到 WebDriver?

c# - 如果窗体上没有其他控件,图表就会消失

c# - 如何修剪字节数组的几个字节?