Node.js与.Net的性能


181

我已经阅读了很多有关Node.js的快速信息,并能够承受大量负载。有人在现实世界中有证据证明与其他框架相比,特别是.Net吗?我读过的大多数文章都是轶事,或者没有与.Net的比较。

谢谢


1
您能在我们正在讨论的哪种情况下更精确吗?
MarcusGranström2012年

1
我对.Net和Node.js在IIS中运行的可比较Web应用程序的任何性能比较感兴趣。
David Merrilees 2012年

1
我无法想象有人会建立一个具有较高性能的网站。.Net之外的需求。您遇到的最基本的问题是,由于高性能,它在许可方面不是很划算。网站通常需要扩展。不,我不是.Net讨厌者。.Net支付账单。
Shane Courtrille'2

4
我必须使用Node / express / mongo和新的.net webapi / mongo对一个小的REST API进行内部测试,并且基于客户端的需求存在性能差异,但最终还是不足以使区别。您需要根据自己的场景开发自己的测试。我们用了三天的时间来用两种语言编写不同的API,然后又花了两天的时间来正确设置测试。如果您打算进行任何需要认真处理的事情,我建议您根据自己的要求进行测试,并自己决定哪种方法更适合您的负载。
AlexGad 2012年

5
@ShaneCourtrille您使.Net(框架)和Windows(操作系统)感到困惑。它们是完全不同的东西,.Net(在Linux上像Mono一样运行得很好)没有许可要求。
2013年

Answers:


365

作为FAST和处理大量的负载是两回事。如果您每秒发送500个请求(在LOAD下),那么实际上每秒处理一个请求的速度非常快的服务器可能完全崩溃。

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

我猜您正在寻找ASP.NET与节点之间的比较。在这场战斗中,在对所有内容进行编译/解释之后,您的性能可能会非常接近。也许.NET有点更快或许节点是一个小更快,但它可能接近,以至于你不在乎。我打赌.NET,但是我不确定。

节点真正引人注目的地方是处理LOAD。这是技术真正不同之处。ASP.NET为来自其线程池的每个请求专用一个线程,一旦ASP.NET用尽了所有可用线程的请求,便开始排队。如果您像@shankar的示例一样在服务“ Hello World”应用程序,那么这可能没什么大不了的,因为不会阻塞线程,并且您将能够处理很多请求,然后再处理用完线程。当您开始发出阻止线程的I / O请求(调用DB,向服务发出http请求,从磁盘读取文件)时,ASP.NET模型就会出现问题。这些阻塞请求意味着您来自线程池的宝贵线程无所作为。您拥有的障碍越多,加载您的ASP.NET应用即可。

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

问题在于,ASP.NET(和.NET Framework)是针对异步I / O创建的。.NET不在乎您是否编写同步或异步代码,因此,由开发人员来决定。部分原因是因为使用异步操作进行线程和编程被认为是“艰苦的”,.NET希望让每个人(菜鸟和专家)感到高兴。变得更加困难,因为.NET最终以3-4种不同的模式进行异步处理。.NET 4.5正在尝试回溯并改版.NET框架,使其具有围绕异步IO的自以为是的模型,但是直到您关心的框架实际支持它时,可能还需要一段时间。

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

同样,我没有任何硬性数字可以证明一种方法,但是我认为节点将赢得典型Web应用程序的LOAD竞争。高度优化的(100%异步).NET应用程序可能会花钱让等效的node.js应用程序运行,但是,如果您平均使用所有.NET和其中的所有节点应用程序,则平均而言,节点可能会处理更多加载。

希望有帮助。


39
请记住,ASP.NET长期以来一直支持异步请求处理程序,而使用MVC4,它们已经变得非常易于使用。
fabspro

12
“这些阻止请求意味着您从线程池中获得的有价值的线程无济于事。您拥有的阻止越多,ASP.NET应用程序能够提供的负载就越少。” 为什么要在前面(传入的请求)还是在后端(实际的工作线程)排队呢?无论如何,客户端请求正在等待响应。我认为人们在这场辩论中忽略的关键是“吞吐量”。它与服务器拥有多少并发连接无关,它对每个请求的响应速度有多快?
sjdirect

19
///不允许我编辑评论,所以这就是我要说的。//@sjdirect-吞吐量与响应时间不同。您很在乎响应时间,但是可以在队列时间+响应时间之间选择,也可以只选择响应时间。在两种情况下,请求的处理都将花费相同的时间(同步执行不会使您的数据库请求执行得更快),但是,如果您的请求线程被阻塞,那么您还将在请求中添加队列时间因为您甚至无法完成之前的请求,就无法开始处理请求。
马特·多森

6
这真是内容丰富,谢谢!不过要注意的一件事是,实体框架6(当前为RC1)现在支持.NET 4.5中的异步模式。msdn.microsoft.com/en-us/data/jj819165
议会

4
这是非常投机的!拥有数据会很棒。通常,这就是我决定如何继续进行表演主题的方式。
kingPuppy

50

我在nodejs和IIS之间进行了基本的性能测试。抛出“ hello,world!”时,IIS的速度大约是nodejs的2.5倍。下面的代码。

我的硬件:Dell Latitude E6510,Core i5(双核),8 GB RAM,Windows 7 Enterprise 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上)。这是一个非常基本的测试,绝不是结论性的。但是我相信这是一个很好的起点。在其他Web服务器和其他平台上,Nodejs可能更快,但在Windows IIS上胜出。希望将ASP.NET MVC转换为nodejs的开发人员应该暂停并三思而后行。

已更新(5/17/2012)Tomcat(在Windows上)似乎击败了IIS,在抛出静态html方面比IIS快约3倍。

雄猫

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似乎是在WINDOWS上分发STATIC HTML的最快服务器。

更新日期(5/18/2012)以前,我总共有100,000个请求,同时有10,000个并发请求。我将其增加到1,000,000个总请求和100,000个并发请求。IIS成为尖叫的赢家,Nodejs表现最差。我将以下结果制成表格:

NodeJS vs IIS vs Tomcat在Windows上提供静态HTML


55
您正在将苹果与猫进行比较。将Node.js与ASP.NET MVC进行比较。IIS最多在提供静态文件方面速度更快,尽管我对此深表怀疑。
alessioalex 2012年

12
@alessioalex:我不明白为什么这个比较无效。我正在比较静态html的响应时间。IIS从default.htm发出静态html,而nodejs服务器发出相同的字符串,而IIS领先。比较ASP.NET MVC应用程序将需要更多的精力和时间,我打算稍后再做。
香卡2012年

28
好的,可以说IIS比Windows更好地在Windows上提供静态文件。IIS只提供静态文件,而Node(例如Apache或NGINX)则不仅仅提供静态文件。您应该将ASP.NET MVC与Node进行比较(查询数据库,从外部服务检索数据等)。通过ASP.NET MVC,您将看到Node带来的巨大性能提升。
alessioalex 2012年

27
如果要执行此操作,请至少了解节点的性质。一个节点进程只能使用一个核心。因此,您要比较的是一个节点进程在一个核心上运行,而IIS和tomcat进程使用多个核心。为了正确比较,您需要运行群集节点。有关易于使用的群集解决方案,请参见nodejs.org/api/cluster.html。但是,我可以根据经验告诉您,根据您的工作情况,node和async c#之间的差异为10-15%。
AlexGad 2013年

14
同样,使用节点,IIS和Tomcat测试静态文件也是没有意义的。首先,节点对于静态文件不是很好,但是它并不是真正意义上的(使用正确的工具完成正确的工作)。如果有人担心其静态文件的速度,则无论如何应该使用CDN。
AlexGad 2013年

25

NIO服务器(Node.js等)往往比BIO服务器要快。(IIS等)。为了证明我的观点,TechEmpower是一家专门从事Web框架基准测试的公司。它们非常开放,并且具有测试所有framewrks的标准方法。

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

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

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

在所有情况下,Node.js的速度往往都比IIS快2倍以上。


1
除了在“多重查询”测试中,ASPNET有两个条目(aspnet-stripped-raw和aspnet-mysql-raw)都击败了nodejs-mysql,后者是最重要的njs条目。
GalacticCowboy 2014年

3
好吧,多重查询测试不是完全测试服务器速度。它主要测试MySQL驱动程序的速度。NodeJS主要使用NO-SQL数据库,例如MongoDB,CouchDB。MySQL驱动程序可能未优化。Json序列化和Plaintext测试倾向于提供纯服务器速度-我会更加信任它们。
ttekin

如果我使用IIS节点怎么办?是我的表现会下降还是会保持不变。
Umashankar '18

3
感谢您提供指向基准页面的链接。但是,答案可能需要更新,.NET Core 2.1的出现可能使情况发生了很大变化。例如,2018年JSON序列化基准测试列出ASP.NET Core为971122请求/秒,Node.js为561593请求/秒,因此今天ASP.NET Core在这方面的速度几乎是Node.js的两倍。
stakx-不再贡献

13

我必须同意Marcus Granstrom的观点,在这里非常重要。

老实说,这听起来像是您在做出具有重大影响的建筑决策。我的建议是隔离关注的区域,并在您考虑的所有堆栈之间进行“烘焙”。

归根结底,您要对决定负责,我不认为借口“ Stackoverflow上的某人向我展示了一篇文章,说那会很好的”会与您的老板削减。


1
我正在寻找可以说服人们(包括我的老板)的东西,值得考虑作为MVC.net网站的替代品,而不是说服他们我们应该互换。到目前为止,我发现的所有轶事都提到它可以支持更多的负载并且性能更好。有人真的证明过这一点吗?
David Merrilees

17
但是MVC网站怎么了?您为什么要寻找替代品?那是最重要的问题。如果问题是在高并发负载下运行缓慢,那么您应该确保使用async.net。如果仍然很慢,则需要分解代码并找出瓶颈所在。以我的经验,在真实世界中,节点和异步网络之间并没有太大的区别。您可以更改平台,但是您可能会简单地将一组代码瓶颈/头痛更改为另一组代码瓶颈/头痛。
AlexGad

1

我看到的主要区别是node .js是动态编程语言(类型检查),因此类型必须在运行时派生。从理论上讲,像C#.NET这样的强类型语言在与Node .js(和PHP等)的对抗中具有更大的潜力,尤其是在计算成本很高的地方。顺便说一句,.NET与C / C ++的本地互操作性应优于节点.js。


4
您关于在JS中“弱”键入速度使其变慢的建议是错误且无关紧要的,无论如何,这都是在比较Apple和Stones(甚至Oranges也比您所建议的更相似)。
2013年

7
@rainabba当您比较某种计算(例如x的斐波那契)时,他是完全正确的。
Stan 2013年

5
@steve实际上,对于Z,您仍然不能说出来,因为JS是一种语言,而.Net是一个框架。他们是完全不同的东西。.Net运行时是为特定的处理器体系结构编译的,因此您不能为单个硬件显着更改特定代码块的性能。如V8所示,JS可以被解释和执行,并且速度变化很大,因此没有理由认为有一天您用JS编写的斐波那契代码的运行速度不会像通过CLR运行的代码一样快(可能快点)。苹果和石头;就像我说的。
2013年

1
也许您是正确的,但在我看来,我不知道其他国家/地区,在中国,我采访过许多
程序员

1
JS可以说同样的话。当JS赶上fibonacci时,您是否真的认为.NET将保留在等待的地方?
Quanben
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.