Node.js与.Net的性能

Dav*_*ees 175 .net performance node.js

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

谢谢

Mat*_*son 362

作为FAST和处理大量的负载是两回事.这是一个真正的服务器FAST,如果你把它每秒500个请求(下在服务每秒请求可能完全嘶哑LOAD).

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

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

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

要防止此阻塞,请使用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%异步的应用程序更容易(因为节点会强制您编写异步的应用程序).

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

希望有所帮助.

  • 请记住,ASP.NET已经支持异步请求处理程序很长一段时间了,使用MVC4它们变得非常简单易用. (36认同)
  • //不会让我编辑我的评论,所以这就是我的意思.// @ sjdirect - 吞吐量与响应时间不同.你关心响应时间是正确的,但它是队列时间+响应时间或响应时间之间的选择.在两种情况下,请求的处理都会花费同样的时间(同步执行不会使您的数据库请求执行得更快),但如果您的请求线程被阻止,那么您也会为请求添加队列时间因为在完成之前的请求之前,您甚至无法开始处理请求. (19认同)
  • **"这些阻塞请求意味着来自线程池的宝贵线程无效.阻塞越多,ASP.NET应用程序能够提供的负载就越少."**为什么我们无关紧要排队前(传入请求)还是后端(实际工作线程)?无论如何,客户端请求都在等待响应.我认为人们在这场辩论中忽视的关键是"吞吐量".它不是关于服务器拥有多少并发连接,它对响应每个请求的速度有多快? (12认同)
  • 这真的很有用,谢谢!需要注意的一点是,Entity Framework 6(目前是RC1)现在支持.NET 4.5的异步模式.http://msdn.microsoft.com/en-us/data/jj819165 (6认同)
  • 这是非常推测的!拥有数据会很棒.这通常是我决定如何处理性能主题的方式. (4认同)
  • @nixkuroi不完全是.使用async/await的优点是,当请求执行I/O操作(从磁盘,数据库读取,发出http请求)时,您不会在等待I/O时保持线程**空闲.你为什么不关心不必要地持有线程?因为如果您的服务器收到大量请求,那么它不太可能出现线程.如果线程用完,请求甚至无法开始处理,因此它们会排队.这就是Matt所说的关于*排队时间+响应时间*与*响应时间*的选择. (3认同)
  • ASP.NET 5 现已开源,基准测试也是如此:https://github.com/aspnet/benchmarks#plain-text-with-http-pipelined (2认同)
  • @Alisson - 是的,但我认为我的意思(已经有一段时间了)是 async/await 平衡了 Node.js 的队列/响应时间问题(通过不持有线程),所以在这一点上,它归结为哪种编译语言更快。假设 c# 的编译器优化使代码运行速度比 node.js 快,并且排队时间不是一个因素,那么 c# 的 async/await 总体上更快? (2认同)
  • @nixkuroi 你是对的,正确使用 async/await 与 Node.js 均衡。我不知道 c# 的编译器优化是否使代码比 node.js 运行得更快,但如果这是真的,那么我相信您对 c# 整体速度更快的看法是正确的。顺便说一句,我已经看到一些基准测试指出 .net core 比 node.js 更快,因此我假设您的假设是正确的。 (2认同)

Sha*_*kar 49

我在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);
Run Code Online (Sandbox Code Playgroud)

的default.htm

hosted by iis at http://localhost/test/
<p>hello, world!</p>
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }

}
}
Run Code Online (Sandbox Code Playgroud)

和结果:

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
Run Code Online (Sandbox Code Playgroud)

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

更新(2012年5月17日)Tomcat(在Windows上)似乎击败IIS手,比IIS发送静态html快约3倍.

tomcat的

index.html at http://localhost:8080/test/
<p>hello, world!</p>
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

NodeJS vs IIS与Tomcat在WINDOWS上提供STATIC HTML.

  • 你正在比较苹果和猫.将Node.js与ASP.NET MVC进行比较.最多IIS在提供静态文件方面更快,但我甚至对此表示怀疑. (55认同)
  • 好吧,假设IIS在Windows上提供静态文件比在Node上更好.IIS只提供静态文件等(如Apache或NGINX),Node做的远不止这些.您应该将ASP.NET MVC与Node进行比较(查询数据库,从外部服务检索数据等).使用Node over ASP.NET MVC,您将看到巨大的性能提升. (27认同)
  • 如果您打算这样做,请至少了解节点的性质.一个节点进程只能使用单个核心.因此,您要比较的是在一个核心上运行到IIS的节点进程和使用多个核心的tomcat进程.为了正确比较,您需要运行节点集群.有关简单易用的群集解决方案,请参见http://nodejs.org/api/cluster.html.但是,我可以从经验告诉你,节点和异步c#之间的区别是10-15%,这取决于你正在做什么. (26认同)
  • 此外,使用节点和IIS以及Tomcat测试静态文件也毫无意义.首先,节点不适合静态文件,但它并不是真正意义上的(使用正确的工具来完成正确的工作).如果有人担心他们的静态文件的速度,他们应该使用CDN. (14认同)
  • @alessioalex:我不明白为什么这个比较无效.我正在比较静态html的响应时间.IIS正在从default.htm发出静态html,而nodejs服务器正在抛出相同的字符串,并且IIS正在向前发出.比较ASP.NET MVC应用程序需要更多的精力和时间,我打算稍后再做. (12认同)
  • 这个示例节点中的@alessioalex甚至没有解析请求,这意味着IIS的执行速度更快,速度仍然提高了2.5倍...... (6认同)
  • node.js是一个服务器.ASP MVC是一个框架.比较Express.js或其后代之一和ASP MVC将是准确的.比较IIS和节点是准确的. (4认同)
  • @alessioalex:我关注了你的 twitter 链接:https://gist.github.com/2652991 并修改了节点服务器以从磁盘读取 default.htm 文件,缓存它然后提供它(因为这就是 IIS 所做的)。节点的性能变得更糟:线程时间的总和现在是 264 秒。 (2认同)
  • 更快!=生产力,维护,松散耦合,单元测试能力等 (2认同)

tte*_*kin 19

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剥-RAW: 47,066
  • 多个查询
    • 的NodeJS MySQL的: 8,878
    • ASPNET剥-RAW: 3,915
  • 纯文本
    • 的NodeJS: 289,578
    • ASPNET剥: 109,136

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

  • 截至第 20 轮(2021-02-08),asp.net core 比 node.js 快得多 (5认同)
  • 好吧,多个查询测试并不完全测试服务器速度.它主要测试MySQL驱动程序的速度.NodeJS主要使用像MongoDB,CouchDB这样的NO-SQL数据库.MySQL驱动程序可能未经过优化.Json序列化和纯文本测试倾向于提供纯服务器速度 - 我更信任它们. (3认同)
  • 感谢您提供指向基准页面的链接。但是,答案可能需要更新,随着.NET Core 2.1的出现,情况可能已经发生了很大变化。例如,在2018年JSON序列化基准测试中,ASP.NET Core的速度为971,122请求/秒,Node.js的速度为561,593请求/秒,因此今天ASP.NET Core在这方面的速度几乎是Node.js的两倍。 (3认同)
  • 除了多重查询测试之外,ASPNET 有两个条目(aspnet-stripped-raw 和 aspnet-mysql-raw)都击败了排名第一的 njs 条目 Nodejs-mysql。 (2认同)

小智 11

我不得不同意Marcus Granstrom这里的情景非常重要.

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

在一天结束时,你负责做出决定,我不认为借口"Stackoverflow上的某个人向我展示了一篇文章说它会没问题"将与你的老板一起切断.

  • 但MVC网站有什么问题?你为什么想找个替代品?这是最重要的问题.如果问题是在并发负载很重的情况下狗是慢的,那么你应该确保你使用的是async.net.如果它仍然很慢,那么你需要分解你的代码并弄清楚你的瓶颈在哪里.根据我的经验,在REAL WORLD场景中节点和异步网之间没有太大差异.您可以更改您的平台,但您可能只是为另一组代码瓶颈/头痛更改了一组代码瓶颈/头痛. (17认同)