Node.js 与 .Net 的性能对比

Posted

技术标签:

【中文标题】Node.js 与 .Net 的性能对比【英文标题】:Node.js vs .Net performance 【发布时间】:2012-03-06 14:30:24 【问题描述】:

我读过很多关于 Node.js 速度快并且能够适应大量负载的文章。有没有人有任何真实世界的证据证明这一点与其他框架相比,尤其是 .Net?我读过的大多数文章都是轶事,或者没有与 .Net 进行比较。

【问题讨论】:

您能说得更准确些吗? 我对 .Net 和 Node.js 在 IIS 中运行的可比 Web 应用程序的性能比较感兴趣。 我无法想象会有人建立一个高性能的网站。 .Net 的要求。您遇到的最基本的问题是,由于高性能,它在许可方面不会非常划算。网站通常需要横向扩展。不,我不是.Net 仇恨者。 .Net 支付账单。 我必须使用 Node/express/mongo 和新的 .net webapi/mongo 对一个小型 REST API 进行内部测试,并且根据客户的需求存在性能差异,但最后这一天,不足以有所作为。您需要根据自己的场景开发自己的测试。我们花了三天时间用两种语言编写不同的 API,然后又花了几天时间来正确设置测试。如果您打算做任何严重的事情,我建议您根据您的要求设置测试,并自己决定哪个更适合您的负载。 @ShaneCourtrille 你混淆了.Net(一个框架)和Windows(一个操作系统)。它们是非常不同的东西,.Net 没有许可要求(在 Linux 上作为 Mono 运行得非常好)。 【参考方案1】:

FAST 和处理大量 LOAD 是两件不同的事情。如果您每秒发送 500 个请求(在 LOAD 下),那么真正FAST 每秒处理一个请求的服务器可能会完全崩溃。

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

我猜您正在寻找 ASP.NET 和 node 之间的比较。在这场战斗中,在一切都被编译/解释之后,你可能会在性能上非常接近。也许 .NET 有点 FASTER 或者 node 有点 FASTER,但它可能足够接近以至于你不在乎。我敢打赌 .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 完成端口,但 Entity Framework 不使用它们。因此,您可以构建一个纯异步并处理大量负载的 ASP.NET 应用程序,但大多数人不这样做,因为它不像构建一个同步的那样容易,而且您可能无法使用您最喜欢的一些部分如果你这样做,框架(如 linq 到实体)。

问题在于 ASP.NET(和 .NET 框架)被创建为对异步 I/O 没有意见。 .NET 不关心您是编写同步代码还是异步代码,因此由开发人员来决定。部分原因是因为异步操作的线程和编程被认为是“困难的”,而 .NET 想让每个人都开心(菜鸟和专家)。它变得更加困难,因为 .NET 最终有 3-4 种不同的异步模式。 .NET 4.5 正试图回过头来对 .NET 框架进行改造,以建立一个围绕异步 IO 的自以为是的模型,但您关心的框架可能需要一段时间才能真正支持它。

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

再一次,我没有任何硬性数据来证明某种方式,但我认为 node 会赢得典型 Web 应用程序的 LOAD 竞争。一个高度优化(100% 异步)的 .NET 应用程序可能会让等效的 node.js 应用程序运行起来物有所值,但如果你取所有 .NET 和所有节点应用程序的平均值,平均而言,节点可能处理更多加载。

希望对您有所帮助。

【讨论】:

请记住,ASP.NET 长期以来一直支持异步请求处理程序,而在 MVC4 中,它们的使用变得非常简单。 “这些阻塞请求意味着线程池中有价值的线程什么都不做。阻塞越多,ASP.NET 应用程序能够提供服务的负载就越少。 " 为什么我们是在前面排队(传入请求)还是在后端(实际工作线程)排队?无论如何,客户端请求都在等待响应。我认为人们在这场辩论中忽略的关键是“吞吐量”。不是服务器拥有多少并发连接,而​​是它对每个请求的响应速度有多快? //不允许我编辑我的评论,所以这就是我的意思。// @sjdirect - 吞吐量与响应时间不同。你关心响应时间是对的,但它是在队列时间+响应时间之间的选择,或者只是响应时间。在这两种情况下,请求的处理都将花费同样长的时间(同步执行不会使您的数据库请求执行得更快),但是如果您的请求线程被阻塞,那么您也在为请求添加队列时间因为在之前的请求完成之前,您甚至无法开始处理请求。 这真的很丰富,谢谢!不过需要注意的一点是,Entity Framework 6(当前为 RC1)现在支持 .NET 4.5 的异步模式。 msdn.microsoft.com/en-us/data/jj819165 这是非常投机的!有数据就好了。这通常是我决定如何处理性能主题的方式。【参考方案2】:

我在 nodejs 和 IIS 之间进行了初步的性能测试。 IIS 在发出“hello, world!”时比 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 上)。这是一个非常初级的测试,绝不是决定性的。但我相信这是一个很好的起点。 Nodejs 在其他 web 服务器上可能更快,在其他平台上,但在 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 似乎是在 WINDOWS 上输出 STATIC HTML 速度最快的服务器。

更新(2012 年 5 月 18 日) 以前我有 100,000 个请求和 10,000 个并发请求。我将其增加到 1,000,000 个总请求和 100,000 个并发请求。 IIS 是大赢家,而 Nodejs 是最差的。我已将结果列表化如下:

.

【讨论】:

您将苹果与猫进行比较。比较 Node.js 和 ASP.NET MVC。最多 IIS 在提供静态文件方面更快,尽管我对此表示严重怀疑。 @alessioalex :我不明白为什么这种比较无效。我正在比较静态 html 的响应时间。 IIS 正在从 default.htm 中输出静态 html,而 nodejs 服务器正在输出相同的字符串,并且 IIS 领先。比较一个 ASP.NET MVC 应用程序需要更多的精力和时间,我打算稍后再做。 好吧,说 IIS 比 Node.js 更擅长在 Windows 上提供静态文件。 IIS 只提供静态文件等(如 Apache 或 nginx),Node 做的远不止这些。您应该将 ASP.NET MVC 与 Node 进行比较(查询数据库、从外部服务检索数据等)。您将看到 Node 在 ASP.NET MVC 上的巨大性能提升。 如果你要这样做,请至少了解节点的本质。一个 Node 进程只能使用一个核心。因此,您要比较的是在一个内核上运行的节点进程与使用多个内核的 IIS 和 tomcat 进程。为了正确比较,您需要运行节点集群。请参阅nodejs.org/api/cluster.html 了解简单易用的集群解决方案。但是,我可以根据经验告诉你,node 和 async c# 之间的差异是 10-15%,这取决于你在做什么。 另外,用 node 和 IIS 和 Tomcat 测试静态文件是没有意义的。首先,node 不适用于静态文件,但它并不是真正的意思(为正确的工作使用正确的工具)。如果有人担心他们的静态文件的速度,他们无论如何都应该使用 CDN。【参考方案3】:

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

第 9 轮测试目前是最新的(2014 年 5 月)。测试了许多 IIS 风格,但 aspnet-stripped 似乎是最快的 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 倍以上。

【讨论】:

除了在 Multiple Queries 测试中,ASPNET 有两个条目(aspnet-stripped-raw 和 aspnet-mysql-raw)都击败了 nodejs-mysql,这是***的 njs 条目。 嗯,多查询测试并不能完全测试服务器速度。主要是测试MySQL驱动速度。 NodeJS 主要使用 MongoDB、CouchDB 等 NO-SQL 数据库。 MySQL 驱动程序可能未优化。 Json 序列化和明文测试倾向于提供纯服务器速度 - 我会更信任它们。 如果我使用 IIS 节点会怎样?我的表现会下降还是一样。 感谢基准页面的链接。然而,答案可能需要更新,随着 .NET Core 2.1 的出现,情况可能发生了很大变化。例如,2018 JSON 序列化基准将 ASP.NET Core 列为 971,122 个请求/秒,将 Node.js 列为 561,593 个请求/秒,因此今天 ASP.NET Core 在这方面似乎几乎是 Node.js 的两倍。 作为第 20 轮(2021-02-08),asp.net core 比 node.js 快很多【参考方案4】:

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

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

归根结底,你要为这个决定负责,我不认为这是借口 “*** 上的某个人给我看了一篇文章,说没问题” 会和你的老板切磋的。

【讨论】:

我正在寻找一些东西来说服人们(包括我的老板),值得考虑作为 MVC.net 网站的替代品,而不是说服他们我们应该交换。到目前为止,我发现的只是轶事提及它可以支持更多负载并且性能更好。有人真的证明了这一点吗? 但是MVC网站有什么问题呢?你为什么要寻找替代方案?这是最重要的问题。如果问题是在高并发负载下速度很慢,那么您应该确保您使用的是 async.net。如果它仍然很慢,那么您需要分解代码并找出瓶颈所在。以我的经验,在 REAL WORLD 场景中节点和异步网络之间并没有太大的不同。您可以更改您的平台,但您可能只需将一组代码瓶颈/头痛更改为另一组代码瓶颈/头痛。 我才意识到这是在 2012 年写的。好吧,差不多 10 年后,Node.js 风靡全球。全栈开发不再需要掌握两种语言。相比之下,我曾经是一名 php 全栈工程师,现在我是一名全栈工程师,前后都使用 TypeScript。扩展 Node.js 很容易,我们的 React 前端的 s-s-r 非常简单。【参考方案5】:

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

【讨论】:

您认为 JS 中的“弱”输入会减慢速度的建议是错误且无关紧要的,无论如何,这是比较 Apples 和 Stones(即使是 Oranges 也会比您的建议更相似)。跨度> @rainabba 当你比较某种计算(例如 x 的斐波那契)时,他是完全正确的。 @steve 实际上,给定 Z,你仍然不能这么说,因为 JS 是一种语言,而 .Net 是一种框架。它们是完全不同的东西。 .Net 运行时是为特定的处理器架构编译的,因此您无法针对单个硬件显着更改特定代码块的性能。正如 V8 所展示的,JS 可以被解释和执行,并且速度变化很大,没有理由认为有一天你用 JS 编写的斐波那契代码的运行速度不会像通过 CLR 运行的代码一样快(很可能,它将是快点)。苹果和石头;正如我所说。 也许你是对的,但在我看来,我不知道其他国家,在中国,我采访的很多程序员都只知道 EF 或 Linq to Sql,这些框架显着降低了 .net 的性能 对于 JS 也可以这样说。当 JS 赶上斐波那契时,你真的认为 .NET 会继续等待吗?

以上是关于Node.js 与 .Net 的性能对比的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 与 .Net 的性能对比

Node.js 与 .Net 的性能对比

Node.js 与 .Net 的性能对比

StackExchange NetGain 与 Node.js WS websocket 服务器性能对比

.Net 2.0 与 .Net 3.5 的性能对比

protobuf-net 与 protobuf-csharp-port 的性能对比