我一直在研究 Asp.Net Security,我发现了一些令人惊讶的代码:

奇怪的代码?

context.Response.ContentType = "text/html";
await context.Response.WriteAsync("<html><body>");
await context.Response.WriteAsync("An remote error has occured: " + context.Request.Query["ErrorMessage"] + "<br>");
await context.Response.WriteAsync("<a href=\"/\">Home</a>");
await context.Response.WriteAsync("</body></html>");

令我惊讶的是多次使用短字符串调用 WriteAsync。

我会做什么

我会使用带有 String.Format 或 StringBuilder 的模板来连接字符串,然后在一次调用中写入:
var template = @"
<html><body>
An remote error has occured:{0}<br>
<a href=\"/\">Home</a>
</body></html>
";

var html = string.format(template, context.Request.Query["ErrorMessage"]);
await context.Response.WriteAsync(html);

我观察到的差异
  • 我的代码更容易修改。
  • 我有一些额外的空白。
  • 我的代码使用了更大的硬编码字符串,而不是一堆小的硬编码字符串。
  • 我使用 String.Format ,与串联相比,它可能会影响性能。
  • 如果应该避免字符串连接,这部分应该分解:
    "An remote error has occured: " + context.Request.Query["ErrorMessage"] + "<br>"
    

  • 问题

    出于讨论的目的。让我们假设这是在一个平均有大约 10,000 个同时活跃用户的 Web 服务器的上下文中:所以性能很重要。
  • 为什么要这样做?
  • 它如何影响性能?
  • 什么时候应该调用 await Response.WriteAsync 而不是 Response.Write?
  • 应多久调用一次 Response.WriteAsync?
  • 尽可能多地使用少量数据
  • 仅当大量文本准备好时
  • 最佳答案

    我创建了一个 Azure 网站(在 Basic 1 - 1 Small Instance 上运行)来对此进行基准测试。然后我使用 https://loader.io 的免费服务在 1 分钟内以 100 个用户/秒的速度运行每个测试。

    我以不同的顺序运行每个测试 3 次。每次测试运行的时间彼此相差在 200 毫秒以内。

    结果:

    结果很明显: StringBuilder 明显获胜。 每个异步调用的成本远远超过任何形式的字符串连接的成本(甚至 String.Format 的性能也比多个异步调用更好)。

  • 1992ms - StringBuilder.Append
  • 3071ms - StringBuilder.AppendFormat
  • 4257ms - 带有 String.Format 的 WriteAsync
  • 9265ms - WriteAsync

  • 下面是每个测试的代码:
        // Do not write this code - It is ugly and performs terribly
        private async Task TestWriteAsync(HttpContext context)
        {
            var r = context.Response;
    
            var id = "id";
            var size = "12";
            var text = "text";
    
            await r.WriteAsync("<div style='display:none'>");
    
            for (int i = 0; i < 10000; i++)
            {
                await r.WriteAsync("<li id='");
                await r.WriteAsync(id);
                await r.WriteAsync("' style='font-size:");
                await r.WriteAsync(size);
                await r.WriteAsync("'>");
                await r.WriteAsync(text);
                await r.WriteAsync("</li>");
            }
    
            await r.WriteAsync("</div>");
        }
    
        // This is much better, but still not great
        private async Task TestWriteAsyncFormat(HttpContext context)
        {
            var r = context.Response;
    
            var id = "id";
            var size = "12";
            var text = "text";
            var template = "<li id='{0}' style='font-size:{1}'>{2}</li>";
    
            await r.WriteAsync("<div style='display:none'>");
    
            for (int i = 0; i < 10000; i++)
            {
                await r.WriteAsync(string.Format(template, id, size, text));
            }
    
            await r.WriteAsync("</div>");
        }
    
        // The Performance Winner, but ugly code
        private async Task TestStringBuilder(HttpContext context)
        {
            var sb = new StringBuilder();
    
            var id = "id";
            var size = "12";
            var text = "text";
    
            sb.Append("<div style='display:none'>");
    
            for (int i = 0; i < 10000; i++)
            {
                sb.Append("<li id='");
                sb.Append(id);
                sb.Append("' style='font-size:");
                sb.Append(size);
                sb.Append("'>");
                sb.Append(text);
                sb.Append("</li>");
            }
    
            sb.Append("</div>");
    
            await context.Response.WriteAsync(sb.ToString());
        }
    
        // Decent performance and Clean Code
        private async Task TestStringBuilderFormat(HttpContext context)
        {
            var sb = new StringBuilder();
    
            var id = "id";
            var size = "12";
            var text = "text";
            var template = "<li id='{0}' style='font-size:{1}'>{2}</li>";
    
            sb.Append("<div style='display:none'>");
    
            for (int i = 0; i < 10000; i++)
            {
                sb.AppendFormat(template, id, size, text);
            }
    
            sb.Append("</div>");
    
            await context.Response.WriteAsync(sb.ToString());
        }
    

    因此,虽然旧的“Response.Write”比同步请求的 StringBuilder 快,但“await Response.WriteAsync”要慢得多(因为异步开销)。

    测试截图:

    c# - 多个 Response.writeAsync 调用-LMLPHP

    c# - 多个 Response.writeAsync 调用-LMLPHP

    c# - 多个 Response.writeAsync 调用-LMLPHP

    c# - 多个 Response.writeAsync 调用-LMLPHP

    10-08 13:53