Продуктивність Node.js проти .Net


183

Я багато читав про швидкість Node.js і вміст вмістити велику кількість навантаження. Хто-небудь має реальні свідоцтва цього чи інших рамок, зокрема. Net? Більшість прочитаних нами статей є анекдотичними або не мають порівнянь з .Net.

Дякую


1
Не могли б ви бути більш точними, про який сценарій ми говоримо?
Маркус Гранстрем

1
Мене цікавить будь-яке порівняння продуктивності .Net і Node.js для порівняних веб-додатків, що працюють в IIS.
Девід Мерріліс

1
Я не можу уявити, щоб хтось будував веб-сайт, який мав високу перф. вимоги поза .Net. Найбільш основна проблема, з якою ви зіткнулися, полягає в тому, що це не буде дуже економічно вигідним щодо ліцензування, оскільки високий рівень перф. Зазвичай сайти вимагають масштабування. І ні, я не ненависник. .Не оплачує рахунки.
Шейн Кортрілль

4
Мені довелося робити внутрішні тести невеликого API REST, використовуючи Node / express / mongo та новий .net webapi / mongo, і існували відмінності в перф-ті, залежно від того, що хотів клієнт, але в кінці дня недостатньо, щоб зробити різниця. Вам потрібно розробити власні тести на основі власних сценаріїв. Нам знадобилося три дні, щоб написати різні API на обох мовах, а потім ще пару днів, щоб правильно встановити тестування. Якщо ви плануєте робити щось віддалено серйозне, я б запропонував налаштувати тести виходячи з ваших вимог і вирішити для себе, що краще для вашого навантаження.
AlexGad

5
@ShaneCourtrille Ви плутаєте .Net (рамки) та Windows (операційна система). Вони є дуже різними речами, а для літньої ліцензії на .Net (яка працює на Linux як Mono), немає.
rainabba

Відповіді:


366

Будучи ШВИДКО і обробкою багато НАВАНТАЖЕННЯ дві різними речей. Сервер, який дійсно Швидкий при обслуговуванні одного запиту в секунду, може цілком зламатись, якщо ви надішлете йому 500 запитів в секунду (під LOAD ).

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

Я здогадуюсь, що ви шукаєте порівняння між ASP.NET та вузлом. У цьому бою, після того, як все буде складено / інтерпретовано, ви, ймовірно, будете досить близькими у виконанні. Можливо .NET трохи швидше, а може, вузол трохи швидше , але, мабуть, досить близько, що вам все одно. Я ставлю на .NET, але точно не знаю.

Місце, де вузол дійсно є переконливим, - це обробка НАВАНТАЖЕННЯ, що ваша програма ASP.NET зможе обслуговувати. НАВАНТАЖЕННЯ . Тут технології дійсно відрізняються. ASP.NET виділяє потік для кожного запиту з його пулу потоків, і як тільки ASP.NET вичерпає всі доступні запити потоків, починає чергуватися. Якщо ви подаєте додатки "Hello World", як-от приклад @shankar, це може не мати великого значення, тому що потоки не будуть заблоковані, і ви зможете обробляти безліч запитів перед вами закінчуються нитки. Проблема з моделлю ASP.NET виникає, коли ви починаєте робити запити вводу / виводу, які блокують потік (дзвоніть до БД, робите http-запит до служби, читаєте файл з диска). Ці запити блокування означають, що ваша цінна нитка з пулу потоків нічого не робить. Чим більше ви блокуєте,

Щоб запобігти цьому блокуванню, ви використовуєте порти завершення вводу / виводу, які не вимагають утримувати нитку, поки ви чекаєте відповіді. ASP.NET підтримує це, але, на жаль, багато загальних фреймворків / бібліотек в .NET DON'T. Наприклад, ADO.NET підтримує порти завершення вводу-виводу, але Entity Framework їх не використовує. Таким чином, ви можете створити додаток ASP.NET, який має суто асинхронний режим і справляється з великим навантаженням, але більшість людей цього не робить, оскільки це не так просто, як створити синхронну та ви не зможете використовувати деякі улюблені частини рамки (як, наприклад, linkq до сутностей), якщо це зробити.

Проблема полягає в тому, що ASP.NET (і .NET Framework) були створені з метою невпевненості в асинхронному введення-виводу. .NET не хвилює, чи пишете ви синхронний чи асинхронний код, тому розробник повинен прийняти це рішення. Частина цього полягає в тому, що вважалося, що нарізка і програмування за допомогою асинхронних операцій "важкі", і .NET хотів зробити всіх щасливими (ноуби та експерти). Це стало ще складніше, тому що .NET отримав 3-4 різних схеми для асинхронізації. .NET 4.5 намагається повернутись та доопрацювати рамку .NET, щоб мати втілену модель навколо асинхронного вводу-виводу, але це може пройти деякий час, поки рамки, про які ви дбаєте, насправді її підтримують.

Дизайнери вузлів, з іншого боку, зробили впевнений вибір, що ВСІ В / О повинні бути асинхронні. Через це рішення дизайнерам вузлів також вдалося прийняти рішення про те, що кожен екземпляр вузла буде однопоточним, щоб мінімізувати комутацію потоку, і що один потік буде просто виконувати код, який був у черзі. Це може бути новий запит, це може бути зворотний виклик із запиту БД, це може бути зворотний виклик із http-запиту відпочинку, який ви зробили. Вузол намагається досягти максимальної ефективності процесора, усуваючи контекстні комутатори потоку. Оскільки вузол зробив цей впевнений вибір, що ВСІ введення / виведення є асинхронним, це також означає, що всі його рамки / доповнення підтримують цей вибір. Простіше писати додатки, які на 100% асинхронізуються у вузлі (адже вузол змушує писати програми, які є асинхронніми).

Знову ж таки, у мене немає жодних важких цифр, щоб довести той чи інший спосіб, але я думаю, що вузол виграв би конкуренцію ЗА НАВЧАННЯ для типового веб-додатка. Високооптимізований (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 ГБ оперативної пам’яті, 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, ймовірно, швидший на інших веб-серверах, на інших платформах, але на Windows IIS є переможцем. Розробники, які хочуть перетворити свій ASP.NET MVC в nodejs, повинні зробити паузу та подумати двічі, перш ніж продовжувати.

Оновлено (17.5.2012) Tomcat (на windows), здається, бив IIS руками вниз, приблизно в 3 рази швидше, ніж IIS у вимкненні статичного html.

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, як видається, є найшвидшим сервером у видаленні СТАТИЧНОГО HTML, НА ВІКНАХ.

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

NodeJS vs 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.
Алесіолекс

27
Якщо ви збираєтесь це робити, будь ласка, принаймні зрозумійте природу вузла. Один процес у вузлі може використовувати лише одне ядро. Отже, те, що ви порівнюєте, це процес вузла, який працює на одному ядрі до IIS і tomcat процесу, використовуючи кілька ядер. Для правильного порівняння потрібно запустити кластерний вузол. Див. Nodejs.org/api/cluster.html для простого використання кластерного рішення. Однак я можу вам сказати з досвіду, різниця між вузлом та async c # становить 10-15% в будь-якому випадку залежно від того, що ви робите.
AlexGad

14
Також тестувати статичні файли з вузлом та IIS та Tomcat безглуздо. Перш за все, вузол не чудовий для статичних файлів, але його насправді не призначено (використовувати правильний інструмент для правильної роботи). Якщо хтось переживає за швидкість своїх статичних файлів, у будь-якому разі він повинен використовувати CDN.
AlexGad

26

Сервери NIO (Node.js тощо), як правило, швидше, ніж BIO-сервери. (IIS тощо). Щоб підтримати мою заяву, TechEmpower - компанія, що спеціалізується на показниках веб-фреймворку . Вони дуже відкриті і мають стандартний спосіб тестування всіх кадрів.

Тести 9 раунду наразі є останніми (травень 2014 року). Існує багато перевірених ароматів IIS, але найшвидший варіант IIS, здається, позбавлений Aspnet.

Ось результати відповідей за секунду (чим вище, тим краще):

  • JSON серіалізація
    • nodejs: 228,887
    • позбавлений Aspnet: 105,272
  • Один запит
    • nodejs-mysql: 88,597
    • Аспнет-позбавлений-сирий: 47,066
  • Кілька запитів
    • nodejs-mysql: 8,878
    • Аспнет-позбавлений-сирий: 3,915
  • Простий текст
    • nodejs: 289,578
    • позбавлений Aspnet: 109,136

У всіх випадках Node.js, як правило, в 2 рази + швидше, ніж IIS.


1
За винятком тесту кількох запитів, де ASPNET має дві записи (aspnet-stripped-raw та aspnet-mysql-raw), які обидва перемагають nodejs-mysql, що є верхньою записом njs.
GalacticCowboy

4
Що ж, тест кількох запитів не є точно тестуванням швидкості сервера. Це в основному тестування швидкості драйвера MySQL. NodeJS в основному використовує бази даних NO-SQL, такі як MongoDB, CouchDB. Можливо, драйвер MySQL не оптимізований. Тести серіалізації Json та Plaintext, як правило, дають чисту швидкість сервера - я б більше їм довіряв.
ttekin

що робити, якщо я використовую вузол IIS? моя продуктивність погіршиться чи буде однаковою.
Умашанкар

3
Дякуємо за посилання на сторінку еталону. Однак відповідь може потребувати оновлення, але, можливо, щось змінилося з появою .NET Core 2.1. Наприклад, орієнтир серіалізації JSON 2018 року містить список ASP.NET Core зі швидкістю 971122 запити / сек, а Node.js - 561,593 запитами / сек, тому сьогодні ASP.NET Core виявиться майже вдвічі швидшим, ніж Node.js у цьому відношенні.
stakx - більше не вносить внесок

13

Я маю згоду з Маркусом Гранстромом. Сценарій тут дуже важливий.

Якщо чесно, це здається, що ви приймаєте архітектурне рішення з високим впливом. Моя порада полягатиме в тому, щоб виділити проблеми, які викликають занепокоєння, і зробити «випічку» між усіма групами, які ви розглядаєте.

Зрештою, ви несете відповідальність за рішення, і я не думаю, що виправдання "Якийсь хлопець на Stackoverflow показав мені статтю, в якій сказано, що це буде добре" Вирішите це з вашим начальником.


1
Я шукаю щось, щоб переконати людей (включаючи мого начальника), що варто розглядати їх як альтернативу веб-сайту MVC.net, а не переконувати їх у тому, що ми повинні обмінятися. Все, що я знайшов до цього часу, - це анекдотичні згадки про те, що він може підтримувати більше навантаження та краще працювати. Хтось насправді це довів?
Девід Мерріліс

17
Але що не так з веб-сайтом MVC? ЧОМУ ви намагаєтесь знайти альтернативу? Це найважливіший Q. Якщо проблема полягає в тому, що собака повільна під великим одночасним навантаженням, то вам слід переконатися, що ви використовуєте async.net. Якщо це все-таки дуже повільно, вам потрібно розбити код і з'ясувати, де ваші вузькі місця. На мій досвід, в сценаріях РЕАЛЬНОГО СВІТУ не існує великої різниці між вузлом та асинхронною мережею. Ви можете змінити свою платформу, але ви, ймовірно, просто поміняєте один набір вузьких місць / головних болів на інший набір вузьких місць / головних болів.
AlexGad

1

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


4
Ваша думка про те, що "слабке" введення в JS уповільнює його, є неправильним і не має значення, і незалежно, це порівнювати яблука та камені (навіть апельсини були б більш подібними, ніж те, що ви пропонуєте).
rainabba

7
@rainabba Якщо ви порівнюєте обчислення якогось типу (наприклад, значення х), він абсолютно правильний.
Стен

5
@steve Насправді, задавши Z, ви все ще не можете сказати, що оскільки JS - це мова, а .Net - це фреймворк. Вони абсолютно різні речі. .Net час виконання компілюється для певної архітектури процесора, тому ви не можете суттєво змінити продуктивність певного фрагмента коду для одного обладнання. Як показує V8, JS можна інтерпретувати та виконувати з надзвичайно різною швидкістю, і немає підстав думати, що одного дня ваш код напруженого тексту, написаний на JS, не працюватиме так швидко, як з кодом, що проходить через CLR (швидше за все, це буде швидше). Яблука та кісточки; як я сказав.
rainabba

1
можливо, ви маєте рацію, але, на мій погляд, я не знаю інших країн, в Китаї багато багатьох програмістів, з якими я брав інтерв'ю у відомого EF або Linq на Sql, ці рамки значно знижують продуктивність .net
dexiang

1
Те саме можна сказати і JS. поки JS наздоганяється на фінал, ти справді думаєш, що .NET залишиться там, де він чекає?
quanben
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.