Node.js与.Net的性能

时间:2012-02-15 08:40:09

标签: .net performance node.js

我已经阅读了很多关于Node.js快速且能够容纳大量负载的信息。有没有人有这个与其他框架的真实世界证据,特别是.Net?我读过的大多数文章都是轶事,或者没有与.Net进行比较。

谢谢

5 个答案:

答案 0 :(得分:361)

FAST 并处理大量 LOAD 是两回事。如果您每秒发送500个请求(在 LOAD 下),那么每秒提供一个请求时真正​​ FAST 的服务器可能会完全崩溃。

您还必须考虑静态(和缓存)与动态页面。如果您担心静态页面,那么IIS可能会击败节点,因为IIS使用内核模式缓存,这意味着请求静态页面的请求甚至不会离开内核。

我猜你正在寻找ASP.NET和节点之间的比较。在这场战斗中,在编译/解释所有内容后,你的表现可能会非常接近。也许.NET有点更快或者节点有点更快,但它可能足够接近你不在乎。我敢打赌.NET,但我不确定。

节点真正引人注目的地方是处理 LOAD 。这是技术真正不同的地方。 ASP.NET为其来自其线程池的每个请求专用一个线程,一旦ASP.NET耗尽所有可用线程,请求就开始排队。如果您正在使用@shankar的“Hello World”应用程序,那么这可能并不重要,因为线程不会被阻止,您将能够在处理之前处理大量请求用完线程。当您开始发出阻止线程的I / O请求(调用数据库,向服务发出http请求,从磁盘读取文件)时,就会出现ASP.NET模型的问题。这些阻塞请求意味着来自线程池的宝贵线程无效。您拥有的阻止越多,您的ASP.NET应用程序能够提供的 LOAD 就越少。

要防止此阻塞,请使用I / O完成端口,在等待响应时不需要保留线程。 ASP.NET支持这一点,但不幸的是,.NET中的许多常见框架/库都没有。例如,ADO.NET支持I / O完成端口,但实体框架不使用它们。因此,您可以构建一个纯粹异步并处理大量负载的ASP.NET应用程序,但大多数人不这样做,因为它不像构建同步的那样容易,并且您可能无法使用一些您喜欢的部分如果你这样做的框架(如linq到实体)。

问题在于ASP.NET(和.NET Framework)的创建是对异步I / O没有意见。 .NET不关心您是否编写同步或异步代码,因此由开发人员做出此决定。部分原因是因为使用异步操作进行线程化和编程被认为是“很难”,.NET希望让每个人都高兴(新手和专家)。它变得更加困难,因为.NET最终有3-4种不同的模式来进行异步。 .NET 4.5试图回过头来改进.NET框架以获得围绕异步IO的固定模型,但是在您关心的框架实际支持它之前可能需要一段时间。

另一方面,节点的设计者做出了一个自以为是的选择,即所有I / O应该是异步的。由于这个决定,节点设计者还能够决定每个节点实例是单线程的,以最小化线程切换,并且一个线程只执行已经排队的代码。这可能是一个新请求,它可能是来自DB请求的回调,它可能是来自您所做的http rest请求的回调。 Node尝试通过消除线程上下文切换来最大化CPU效率。因为节点做出了这种自以为是的选择,即所有I / O都是异步的,这也意味着它的所有框架/附加组件都支持这种选择。在节点中编写100%异步的应用程序更容易(因为节点会强制您编写异步的应用程序)。

同样,我没有任何硬数据证明这种或那种方式,但我认为节点将赢得典型网络应用程序的LOAD竞争。高度优化的(100%异步).NET应用程序可能会为等价的node.js应用程序提供运行,但是如果您将所有.NET和所有节点应用程序的平均值放在那里,平均节点可能会处理更多LOAD。

希望有所帮助。

答案 1 :(得分:48)

我在nodejs和IIS之间进行了基本的性能测试。 在发出“hello,world!”时,IIS比nodejs快2.5倍。 代码如下。

我的硬件: 戴尔Latitude E6510,酷睿i5(双核),8 GB内存,Windows 7企业版64位操作系统

节点服务器

runs at http://localhost:9090/
/// <reference path="node-vsdoc.js" />
var http = require("http");
http.createServer(function (request, response) {
response.writeHead(200, { "Content-Type": "text/html" });
response.write("<p>hello, world!</p>");
response.end();
}).listen(9090);

的default.htm

hosted by iis at http://localhost/test/
<p>hello, world!</p>

我自己的使用任务并行库的基准程序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace HttpBench
{
class Program
{
    private int TotalCount = 100000;
    private int ConcurrentThreads = 1000;
    private int failedCount;
    private int totalBytes;
    private int totalTime;
    private int completedCount;
    private static object lockObj = new object();

    /// <summary>
    /// main entry point
    /// </summary>
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run(args);
    }

    /// <summary>
    /// actual execution
    /// </summary>
    private void Run(string[] args)
    {
        // check command line
        if (args.Length == 0)
        {
            this.PrintUsage();
            return;
        }
        if (args[0] == "/?" || args[0] == "/h")
        {
            this.PrintUsage();
            return;
        }

        // use parallel library, download data
        ParallelOptions options = new ParallelOptions();
        options.MaxDegreeOfParallelism = this.ConcurrentThreads;
        int start = Environment.TickCount;
        Parallel.For(0, this.TotalCount, options, i =>
            {
                this.DownloadUrl(i, args[0]);
            }
        );
        int end = Environment.TickCount;

        // print results
        this.Print("Total requests sent: {0}", true, this.TotalCount);
        this.Print("Concurrent threads: {0}", true, this.ConcurrentThreads);
        this.Print("Total completed requests: {0}", true, this.completedCount);
        this.Print("Failed requests: {0}", true, this.failedCount);
        this.Print("Sum total of thread times (seconds): {0}", true, this.totalTime / 1000);
        this.Print("Total time taken by this program (seconds): {0}", true, (end - start) / 1000);
        this.Print("Total bytes: {0}", true, this.totalBytes);
    }

    /// <summary>
    /// download data from the given url
    /// </summary>
    private void DownloadUrl(int index, string url)
    {
        using (WebClient client = new WebClient())
        {
            try
            {
                int start = Environment.TickCount;
                byte[] data = client.DownloadData(url);
                int end = Environment.TickCount;
                lock (lockObj)
                {
                    this.totalTime = this.totalTime + (end - start);
                    if (data != null)
                    {
                        this.totalBytes = this.totalBytes + data.Length;
                    }
                }
            }
            catch
            {
                lock (lockObj) { this.failedCount++; }
            }
            lock (lockObj)
            {
                this.completedCount++;
                if (this.completedCount % 10000 == 0)
                {
                    this.Print("Completed {0} requests.", true, this.completedCount);
                }
            }
        }
    }

    /// <summary>
    /// print usage of this program
    /// </summary>
    private void PrintUsage()
    {
        this.Print("usage: httpbench [options] <url>");
    }

    /// <summary>
    /// print exception message to console
    /// </summary>
    private void PrintError(string msg, Exception ex = null, params object[] args)
    {
        StringBuilder sb = new System.Text.StringBuilder();
        sb.Append("Error: ");
        sb.AppendFormat(msg, args);
        if (ex != null)
        {
            sb.Append("Exception: ");
            sb.Append(ex.Message);
        }
        this.Print(sb.ToString());
    }

    /// <summary>
    /// print to console
    /// </summary>
    private void Print(string msg, bool isLine = true, params object[] args)
    {
        if (isLine)
        {
            Console.WriteLine(msg, args);
        }
        else
        {
            Console.Write(msg, args);
        }
    }

}
}

和结果:

IIS: httpbench.exe http://localhost/test

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 97
Total time taken by this program (seconds): 16
Total bytes: 2000000

nodejs: httpbench.exe http://localhost:9090/

Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 234
Total time taken by this program (seconds): 27
Total bytes: 2000000

结论: IIS比nodejs快2.5倍(在Windows上)。这是一个非常基本的测试,绝不是决定性的。但我相信这是一个很好的起点。在其他网络服务器上,在其他平台上,Nodejs可能更快,但在Windows上,IIS是胜利者。希望将其ASP.NET MVC转换为nodejs的开发人员应该在继续之前暂停并三思而后行。

更新(2012年5月17日) Tomcat(在Windows上)似乎击败了IIS,在发布静态HTML时比IIS慢大约3倍。

Tomcat的

index.html at http://localhost:8080/test/
<p>hello, world!</p>

tomcat结果

httpbench.exe http://localhost:8080/test/
Completed 10000 requests.
Completed 20000 requests.
Completed 30000 requests.
Completed 40000 requests.
Completed 50000 requests.
Completed 60000 requests.
Completed 70000 requests.
Completed 80000 requests.
Completed 90000 requests.
Completed 100000 requests.
Total requests sent: 100000
Concurrent threads: 1000
Total completed requests: 100000
Failed requests: 0
Sum total of thread times (seconds): 31
Total time taken by this program (seconds): 5
Total bytes: 2000000

更新结论:我多次运行基准程序。 Tomcat似乎是推出STATIC HTML,ON WINDOWS的最快服务器。

更新(2012年5月18日) 以前我总共有100,000个请求,有10,000个并发请求。我将其增加到1,000,000总请求和100,000个并发请求。 IIS成为尖叫的赢家,Nodejs最糟糕。我已将以下结果列为表格:

NodeJS vs IIS vs Tomcat serving STATIC HTML on WINDOWS

答案 2 :(得分:17)

NIO服务器(Node.js等)往往比BIO服务器更快。 (IIS等)。为了支持我的主张,TechEmpower是一家专注于web framework benchmarks的公司。它们非常开放,并且有标准的方法来测试所有的framewrks。

第9轮测试目前是最新的(2014年5月)。有许多IIS风格测试,但aspnet剥离似乎是最快的IIS变种。

以下是每秒响应次数的结果(越高越好):

  • JSON序列化
    • nodejs:228,887
    • aspnet-stripped:105,272
  • 单一查询
    • nodejs-mysql:88,597
    • aspnet-stripped-raw:47,066
  • 多个查询
    • nodejs-mysql:8,878
    • aspnet-stripped-raw:3,915
  • 纯文本
    • nodejs:289,578
    • aspnet-stripped:109,136

在所有情况下,Node.js往往比IIS快2倍+。

答案 3 :(得分:11)

我必须同意Marcus Granstrom的情况,这里的情况非常重要。

说实话,这听起来像是在做出高影响力的架构决策。 我的建议是隔离关注的区域,并在你考虑的任何堆栈之间进行“烘烤”。

在一天结束时,你负责做出决定,我不认为是借口 “Stackoverflow上的一些人向我展示了一篇文章说它会很好” 将与你的老板一起削减它。

答案 4 :(得分:1)

我看到的主要区别是节点.js是动态编程语言(类型检查),因此类型必须在运行时派生。像C#.NET这样的强类型语言在理论上有更多的潜力赢得与Node .js(和PHP等)的斗争,特别是在昂贵的计算方面。顺便说一句,.NET应该比C / C ++具有更好的本地互操作性而不是节点.js。