Node.js против производительности .Net

183

Я много читал о том, что Node.js быстр и способен справляться с большими нагрузками. У кого-нибудь есть реальные доказательства этого против других фреймворков, в частности .Net? Большинство статей, которые я прочитал, являются анекдотическими или не имеют сравнений с .Net.

Спасибо

Дэвид Меррилис
источник
1
Не могли бы вы уточнить, о каком сценарии идет речь?
Маркус Гранстрем
1
Меня интересует сравнение производительности .Net и Node.js для сопоставимых веб-приложений, работающих в IIS.
Дэвид Меррилис
1
Я не могу представить, чтобы кто-то создавал веб-сайт, который имел бы высокую производительность. требования вне .Net. Самая основная проблема, с которой вы столкнетесь, заключается в том, что она не будет очень рентабельной с точки зрения лицензирования, так как имеет высокую производительность. сайты обычно требуют масштабирования. И нет, я не ненавистник .Net. .Net оплачивает счета.
Шейн Кортрилл
4
Я должен был провести внутренние тесты небольшого REST API с использованием Node / express / mongo и нового .net webapi / mongo, и были отличия, основанные на том, что хотел клиент, но в конце дня этого было недостаточно, чтобы сделать разница. Вам необходимо разработать свои собственные тесты, основанные на ваших собственных сценариях. Нам потребовалось три дня, чтобы написать разные API на обоих языках, а затем еще пару дней, чтобы правильно настроить тестирование. Если вы планируете делать что-то серьезное, я бы предложил настроить тесты на основе ваших требований и решить для себя, что лучше для вашей нагрузки.
AlexGad
5
@ShaneCourtrille Вы путаете .Net (фреймворк) и Windows (операционная система). Это очень разные вещи, и НЕТ лицензионных требований для .Net (который прекрасно работает в Linux как Mono).
rainabba

Ответы:

367

Быть БЫСТРЫМ и обрабатывать много нагрузки - две разные вещи. Сервер, который действительно БЫСТРО обслуживает один запрос в секунду, может полностью работать, если вы отправите ему 500 запросов в секунду (в режиме LOAD ).

Вы также должны учитывать статические (и кэшированные) и динамические страницы. Если вы беспокоитесь о статических страницах, то IIS, вероятно, превзойдет узел, потому что IIS использует кэширование в режиме ядра, что означает, что запросы, запрашивающие статическую страницу, даже не собираются выходить из ядра.

Я предполагаю, что вы ищете сравнение между ASP.NET и узлом. В этой битве после того, как все будет скомпилировано / интерпретировано, вы, вероятно, будете довольно близки по производительности. Может быть, .NET немного БЫСТРЕЕ, или, может быть, узел немного БЫСТРЕЕ , но, вероятно, он достаточно близко, чтобы вам было все равно. Я бы поставил на .NET, но я точно не знаю.

Место, где этот узел действительно интересен, предназначено для обработки LOAD . Вот где технологии действительно отличаются. ASP.NET выделяет поток для каждого запроса из своего пула потоков, и как только ASP.NET исчерпал все доступные запросы потоков, начинают ставиться в очередь. Если вы обслуживаете приложения «Hello World», такие как пример @shankar, то это может не иметь большого значения, потому что потоки не будут блокироваться, и вы сможете обрабатывать множество запросов перед вами. закончились темы. Проблема с моделью ASP.NET возникает, когда вы начинаете делать запросы ввода-вывода, которые блокируют поток (обращение к БД, выполнение http-запроса к службе, чтение файла с диска). Эти запросы на блокировку означают, что ваш ценный поток из пула потоков ничего не делает. Чем больше у вас блокировок,ЗАГРУЗИТЕ ваше приложение ASP.NET будет в состоянии служить.

Чтобы предотвратить эту блокировку, вы используете порты завершения ввода / вывода, которые не требуют удержания потока, пока вы ожидаете ответа. ASP.NET поддерживает это, но, к сожалению, многие из распространенных фреймворков / библиотек в .NET НЕ. Например, ADO.NET поддерживает порты завершения ввода / вывода, но Entity Framework их не использует. Таким образом, вы можете создать приложение ASP.NET, которое является чисто асинхронным и обрабатывает большую нагрузку, но большинство людей этого не делают, потому что это не так просто, как создать синхронное приложение, и вы не сможете использовать некоторые из ваших любимых частей структуры (как linq для сущностей), если вы делаете.

Проблема состоит в том, что ASP.NET (и .NET Framework) были созданы для того, чтобы не думать об асинхронном вводе-выводе. .NET не волнует, пишете ли вы синхронный или асинхронный код, поэтому решение за этим должен принять разработчик. Частично это связано с тем, что многопоточность и программирование с асинхронными операциями считались «сложными», и .NET хотел порадовать всех (новичков и экспертов). Это стало еще сложнее, потому что .NET закончила с 3-4 различными шаблонами для выполнения асинхронного. .NET 4.5 пытается вернуться и модернизировать платформу .NET, чтобы создать самоуверенную модель асинхронного ввода-вывода, но может пройти некоторое время, пока платформы, о которых вы заботитесь, действительно поддержат ее.

Разработчики узла, с другой стороны, сделали выбор, что ВСЕ ввод / вывод должен быть асинхронным. Благодаря этому решению разработчики узлов также смогли принять решение, что каждый экземпляр узла будет однопоточным, чтобы минимизировать переключение потоков, и что один поток будет просто выполнять код, который был поставлен в очередь. Это может быть новый запрос, это может быть обратный вызов из запроса БД, это может быть обратный вызов из запроса http rest, который вы сделали. Узел пытается максимизировать эффективность процессора, устраняя переключение контекста потока. Поскольку узел сделал этот выборный выбор, что ALL I / O является асинхронным, это также означает, что все его фреймворки / дополнения поддерживают этот выбор. Проще писать приложения, которые на 100% асинхронны в узле (потому что узел заставляет вас писать приложения, которые являются асинхронными).

Опять же, у меня нет точных цифр, чтобы доказать, так или иначе, но я думаю, что узел выиграет конкурс LOAD для типичного веб-приложения. Сильно оптимизированное (100% асинхронное) приложение .NET может дать эквивалентное приложение node.js за свои деньги, но если вы взяли среднее значение всех .NET и всех приложений для узлов, то в среднем узел, вероятно, обрабатывает больше НАГРУЗКИ.

Надеюсь, это поможет.

Мэтт Дотсон
источник
39
Помните, что ASP.NET уже давно поддерживает асинхронные обработчики запросов, а с MVC4 они стали чрезвычайно просты в использовании.
Fabspro
12
«Эти запросы на блокировку означают, что ваш ценный поток из пула потоков ничего не делает. Чем больше у вас блокировок, тем меньше НАГРУЗКА ваше приложение ASP.NET сможет обслуживать». Почему имеет значение, стоим ли мы в очереди (входящий запрос) или в бэкэнде (фактический рабочий поток)? Независимо от того, что клиентский запрос ожидает ответа. Я думаю, что ключ, который люди упускают из виду в этой дискуссии, - это «пропускная способность». Дело не в том, сколько одновременных соединений держит сервер, а в том, насколько быстро он может ответить на каждый запрос, верно?
sjdirect
19
// Не позволю мне редактировать свой комментарий, поэтому вот что я хотел сказать .// @sjdirect - Пропускная способность не совпадает со временем ответа. Вы правы, заботясь о времени ответа, но это выбор между временем очереди + временем ответа или просто временем ответа. Обработка запроса будет занимать одинаково много времени в обоих сценариях (синхронное выполнение НЕ приведет к ускорению выполнения вашего запроса БД), но если потоки вашего запроса заблокированы, то вы также добавляете время очереди к запросам потому что вы даже не можете начать обработку запроса, пока не будут выполнены предыдущие запросы.
Мэтт Дотсон
6
Это было действительно информативно, спасибо! Однако следует отметить, что Entity Framework 6 (в настоящее время RC1) теперь поддерживает асинхронный шаблон из .NET 4.5. msdn.microsoft.com/en-us/data/jj819165
парламент,
4
Это очень умозрительно! Было бы здорово иметь данные. Обычно я так решаю, как поступить с темами выступления.
kingPuppy
50

Я сделал элементарный тест производительности между nodejs и IIS. IIS примерно в 2,5 раза быстрее, чем nodejs, когда выдаете «привет, мир!». код ниже.

мое оборудование: Dell Latitude E6510, Core i5 (двухъядерный), 8 ГБ ОЗУ, 64-битная ОС Windows 7 Enterprise

сервер узла

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, должны сделать паузу и дважды подумать, прежде чем продолжить.

Обновлено (17.05.2012). Похоже, что Tomcat (в Windows) опережает IIS, примерно в 3 раза быстрее IIS при создании статического html.

Кот

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

результаты кота

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 НА WINDOWS.

Обновлено (18.05.2012). Ранее у меня было 100 000 запросов и 10 000 одновременных запросов. Я увеличил его до 1 000 000 и 100 000 одновременных запросов. IIS выходит как кричащий победитель, с Nodejs, обличающим худшее. Я привел в таблицу результаты ниже:

NodeJS против IIS против Tomcat, обслуживающий STATIC HTML в WINDOWS,

Шанкар
источник
56
Вы сравниваете яблоки с кошками. Сравните Node.js с ASP.NET MVC. В большинстве случаев IIS быстрее обрабатывает статические файлы, хотя я серьезно сомневаюсь даже в этом.
Alessioalex
12
@alessioalex: я не понимаю, почему это сравнение недействительно. Я сравниваю время отклика для статического HTML. IIS выводит статический html из default.htm, в то время как сервер nodejs выводит ту же строку, и IIS выходит вперед. Сравнение приложения ASP.NET MVC потребует больше усилий и времени, и я планирую сделать это позже.
Шанкар
28
Хорошо, скажем, что IIS лучше обслуживает статические файлы в Windows, чем Node. IIS обслуживает только статические файлы и тому подобное (например, Apache или NGINX), Node делает гораздо больше. Вы должны сравнивать ASP.NET MVC с Node (запрашивать базу данных, получать данные из внешнего сервиса и т. Д. И т. Д.). Вы увидите огромный прирост производительности с Node по сравнению с ASP.NET MVC.
Alessioalex
27
Если вы собираетесь сделать это, пожалуйста, по крайней мере, понять природу узла. Один процесс Node может использовать только одно ядро. Итак, вы сравниваете процесс узла, работающий на одном ядре, с процессом IIS и tomcat, использующим несколько ядер. Для правильного сравнения необходимо запустить кластеризованный узел. См. Nodejs.org/api/cluster.html для простого в использовании кластерного решения. Тем не менее, по опыту могу сказать, что разница между node и async c # составляет 10-15% в любом случае, в зависимости от того, что вы делаете.
AlexGad
14
Кроме того, тестирование статических файлов с помощью узла, IIS и Tomcat не имеет смысла. Прежде всего, узел не подходит для статических файлов, но на самом деле он не предназначен (используйте правильный инструмент для правильной работы). Если кого-то беспокоит скорость их статических файлов, он все равно должен использовать CDN.
AlexGad
26

Серверы NIO (Node.js и т. Д.), Как правило, работают быстрее, чем серверы BIO. (IIS и т. Д.). Чтобы поддержать мое утверждение, TechEmpower - компания, специализирующаяся на тестах веб-фреймворков . Они очень открыты и имеют стандартный способ тестирования всех фреймворков.

Тесты 9-го тура в настоящее время самые последние (май 2014 г.). Было протестировано много разновидностей IIS, но Aspnet-Stripped, кажется, самый быстрый вариант IIS.

Вот результаты в ответах в секунду (чем выше, тем лучше):

  • Сериализация JSON
    • nodejs: 228,887
    • САШ раздели: 105,272
  • Одиночный запрос
    • nodejs-MySQL: 88,597
    • САШ раздели-сырец: 47,066
  • Несколько запросов
    • nodejs-MySQL: 8,878
    • САШ раздели-сырец: 3,915
  • Простой текст
    • nodejs: 289,578
    • САШ раздели: 109,136

Во всех случаях Node.js имеет тенденцию быть в 2 раза быстрее, чем IIS.

ttekin
источник
1
За исключением теста с несколькими запросами, где в ASPNET есть две записи (aspnet-stripped-raw и aspnet-mysql-raw), которые обе бьют nodejs-mysql, которая является верхней записью njs.
GalacticCowboy
4
Ну, тест Multiple Queries не совсем проверяет скорость сервера. В основном это тестирование скорости драйвера MySQL. NodeJS в основном использует базы данных NO-SQL, такие как MongoDB, CouchDB. Драйвер MySQL может быть не оптимизирован. Тесты Json-сериализации и открытого текста, как правило, дают чистую скорость сервера - я бы им больше доверял.
ttekin
Что делать, если я использую узел IIS? мое выступление будет ухудшаться или будет таким же.
Умашанкар
3
Спасибо за ссылку на страницу тестов. Однако для ответа может потребоваться обновление, возможно, с появлением .NET Core 2.1 все могло немного измениться. Например, в тесте JSON-сериализации 2018 года список ASP.NET Core составляет 971 122 запросов в секунду, а Node.js - 561 593 запросов в секунду, поэтому сегодня ASP.NET Core будет в два раза быстрее, чем Node.js в этом отношении.
stakx - больше не вносит вклад
13

Я должен согласиться с Маркусом Гранстромом, что сценарий здесь очень важен.

Честно говоря, звучит так, будто вы принимаете архитектурное решение. Мой совет - изолировать проблемные области и сделать «пирог» между любыми стеками, которые вы рассматриваете.

В конце концов, вы несете ответственность за решение, и я не думаю, что это оправдание: «Какой-то парень в Stackoverflow показал мне статью, в которой говорилось, что все будет в порядке».

Номер 9
источник
1
Я ищу что-то, чтобы убедить людей (включая моего босса), что стоит рассмотреть в качестве альтернативы веб-сайту MVC.net, а не убеждать их, что мы должны поменяться местами. Все, что я нашел до сих пор, - это анекдотичные упоминания о том, что он может поддерживать большую нагрузку и работает лучше. Кто-нибудь на самом деле доказал это?
Дэвид Меррилис
17
Но что не так с веб-сайтом MVC? ПОЧЕМУ вы пытаетесь найти альтернативу? Это наиболее важный вопрос. Если проблема в том, что при большой одновременной нагрузке скорость работы снижается, то вам следует убедиться, что вы используете async.net. Если это все еще очень медленно, то вам нужно разбить ваш код и выяснить, где ваши узкие места. По моему опыту, между сценариями REAL WORLD и между узлом и асинхронной сетью нет существенных различий. Вы можете изменить свою платформу, но вы, скорее всего, просто замените один набор узких мест / головных болей кода на другой набор узких мест / головных болей кода.
AlexGad
1

Основное отличие, которое я вижу, состоит в том, что узел .js является динамическим языком программирования (проверка типов), поэтому типы должны быть получены во время выполнения. Языки со строгой типизацией, такие как C # .NET, теоретически имеют гораздо больший потенциал в борьбе с Node .js (и PHP и т. Д.), Особенно там, где требуются дорогостоящие вычисления. Между прочим, .NET должна иметь лучшее внутреннее взаимодействие с C / C ++, чем узел .js.

Ондрей Розинек
источник
4
Ваше предположение о том, что «слабая» типизация в JS замедляет его, неверно и не имеет значения, и независимо от того, что это сравнивает яблоки и камни (даже апельсины будут более похожи, чем вы предлагаете).
rainabba
7
@rainabba Когда вы сравниваете вычисления какого-либо вида (например, Фибоначчи от x), он совершенно прав.
Стэн
5
@steve На самом деле, учитывая Z, вы все еще не можете сказать это, потому что JS - это язык, а .Net - это фреймворк. Это совершенно разные вещи. Среда выполнения .Net скомпилирована для конкретной архитектуры процессора, поэтому вы не можете существенно изменить производительность определенного фрагмента кода для одного компонента оборудования. Как показывает V8, JS можно интерпретировать и выполнять с очень разными скоростями, и нет оснований думать, что однажды ваш код Фибоначчи, написанный на JS, не будет работать JUST так же быстро, как с кодом, выполняемым через CLR (вероятно, это будет Быстрее). Яблоки и камни; как я сказал.
Райнабба
1
Возможно, вы правы, но, на мой взгляд, я не знаю других стран, в Китае, у многих программистов, у которых я брал интервью у только что известных EF или Linq to Sql, эти платформы значительно снижают производительность .net
dexiang
1
То же самое можно сказать и JS. в то время как JS догоняет Фибоначчи, вы действительно думаете, что .NET останется там, где его ждет?
Quanben