Хабрахабр

[Перевод] Всегда ли Node.js будет медленнее, чем Golang?

Возникает такое ощущение, что буквально каждую неделю появляется новый «веб-фреймворк» для Node.js, который называют чем-то таким, что работает быстрее, чем всё, что было до него. Всем известно, что Express — это медленно, но способен ли очередной фреймворк по-настоящему улучшить производительность подсистемы ввода-вывода Node.js? Единственное, что он может — это устранить чрезмерную нагрузку на систему, создаваемую Express. Об улучшении чего-то фундаментального речи не идёт. Собственно говоря, для того, чтобы кардинальным образом улучшить ситуацию, нужно работать на более глубоком уровне, а не добавлять новые абстракции поверх Node.js.

image

Что нужно для того, чтобы на платформе Node.js можно было бы создавать серверные приложения, работающие гораздо быстрее чем всё то, что есть сегодня?

Анализ ситуации

Express — один из старейших веб-фреймворков для Node.js. Он основан на стандартных возможностях этой платформы, давая разработчикам удобный интерфейс, построенный вокруг концепции приложения, и позволяя управлять URL-маршрутами, параметрами, методами и прочим подобным.

Единственное, чего ему не хватает — это производительность. Express прост, он помогает программистам быстро разрабатывать приложения. Но они сами являются тем, что создаёт дополнительную нагрузку на систему и плохо влияют на производительность. Постоянно появляющиеся проекты, вроде Fastify, стремятся к тому, чтобы дать разработчикам те же возможности, что и Express, но с меньшими потерями производительности. А дать она, в сравнении с конкурентами, может не так уж и много. Они жёстко ограничены тем, что может дать платформа Node.js.

Количество HTTP-запросов, обрабатываемых разными серверами в секунду

Это — максимум платформы Node.js. Обратите внимание на красную линию. На самом деле это — очень низкий предел производительности, если сравнивать платформу Node.js с её популярными альтернативами наподобие Golang. Фреймворки для неё, независимо от того, есть ли в их названиях слово «fast» или нет, эту линию пересечь не в состоянии.

К счастью Node.js поддерживает C++-аддоны, биндинги Google V8, которые позволяют связывать JavaScript и C++, и позволяют вызывать из JavaScript любые механизмы, даже в том случае, если эти механизмы предоставляются чем-то, отличающимся от платформы Node.js.

Это позволяет JavaScript-программам выжать всё возможное из движка Google V8, не ограничиваясь тем, что разработчики Node.js сочли вполне достаточным. Это делает возможным расширение и улучшение возможностей JavaScript-приложений, позволяет выйти на новые уровни производительности.

О выходе µWebSockets.js

В начале этого месяца я выпустил новый проект — µWebSockets.js. В качестве хостинга для его кода используется GitHub, а не npm, но установить его для Node.js средствами npm можно так:

npm install uNetworking/uWebSockets.js#v15.0.0

Для работы с µWebSockets.js не нужен компилятор. Поддерживаются Linux, macOS и Windows. Исходной версией системы является 15.0.0, нумерация версий осуществляется по правилам семантического версионирования.

Он состоит из примерно 6 тысяч строк C и C++-кода и значительно обходит по производительности лучшие решения, написанные на Golang. µWebSockets.js — это альтернативный веб-сервер для бэкенд-приложений, написанных на JS. Паоло Ардоино из Bitfinex отмечает, что это — замечательный проект. Так, биржа bitfinex.com уже портировала оба своих торговых API (REST и WebSocket) на µWebSockets.js и постепенно вводит их в продакшн. Мне же хотелось бы сказать, что тому, что у меня появилась возможность выпустить µWebSockets.js, я всецело обязан поддержке, оказанной мне BitMEX, Bitfinex и Coinbase.

Особенности µWebSockets.js

µWebSockets.js — это новый проект, выпущенный под лицензией Apache 2.0, который является продолжением того, что известно как «uws». Данный проект представляет собой полный стек для Google V8, начинающийся на уровне ядра операционной системы, полностью заменяющий стандартные возможности Node.js и представляющий собой стабильную, безопасную, соответствующую стандартам, быструю и легковесную подсистему ввода-вывода для Node.js. Вот как выглядит взаимодействие JS-приложения с операционной системой с применением µWebSockets.js.

Взаимодействие JS-приложения с ОС c применением µWebSockets.js

Каждый слой зависит лишь от предыдущего слоя. Как видите, проект состоит из нескольких слоёв. Такая архитектура упрощает выявление и исправление ошибок, а также выполнение работ по расширению решения за счёт реализации в нём новых возможностей.

Это позволяет, при необходимости, заменять части решения, добавлять в систему альтернативные реализации тех или иных возможностей, не сталкиваясь при этом с необходимостью изменения кода, находящегося на более высоком уровне. Надо отметить, что слой µSockets сам состоит из трёх подслоёв, представляющих собой механизмы для работы с событиями и с сетью, а также инструменты для защиты данных.

При этом другие слои системы даже не знают о том, что такое SSL. Например, если нужно заменить на что-нибудь OpenSSL, для этого достаточно поменять на то, что нужно, файл ssl.c с его шестью сотнями строк кода. Такой подход, помимо удобства замены одних частей системы на другие, ведёт и к упрощению процесса обнаружения ошибок.

Внутренние подслои µSockets

В Node.js всё это смешано, никто не задавался целью изолировать отдельные части этой платформы. Представленная здесь архитектура серьёзно отличается от той, монолитной, которая используется в Node.js, где в одном и том же файле исходного кода можно встретить и вызовы libuv, и команды для работы с системой, и обращения к OpenSSL и V8. Это значительно усложняет процесс внесения серьёзных изменений в Node.js.

О разработке для µWebSockets.js

Вот чрезвычайно упрощённый и сокращённый пример работы с µWebSockets.js, главная задача которого — продемонстрировать базовые возможности системы.

/* SSL-приложение с маршрутами */
uWS.SSLApp({ key_file_name: 'misc/key.pem', cert_file_name: 'misc/cert.pem', passphrase: '1234'
}).get('/hello', (res, req) => { /* Очень сильно упрощено */ res.end('Hello World!');
}).ws('/*', , message: (ws, message, isBinary) => { /* OK имеет значение false при переполнении * рекомендовано дождаться сброса буфера */ let ok = ws.send(message, isBinary); }, drain: (ws) => { console.log('WebSocket backpressure: ' + ws.getBufferedAmount()); }, close: (ws, code, message) => { console.log('WebSocket closed'); }
}).listen(port, (token) => { if (token) { console.log('Listening to port ' + port); }
});

В определённом смысле, можно говорить о том, что µWebSockets.js с применением SSL может обойти Gorilla WebSocket, реализацию протокола WebSocket на Go, без SSL. То есть оказывается, что JS-код может обмениваться сообщениями с использованием SSL даже быстрее, чем, при определённых условиях, код, написанный на Go без SSL. Полагаю, что это — отличный результат.

Быстрая реализация протокола WebSocket

Socket.IO, во многих отношениях, можно считать эквивалентом Express, работающим в режиме реального времени. Оба эти проекта появились достаточно давно, работать с ними просто, они пользуются популярностью. Но они, кроме прочего, ещё и медленны.

Различные реализации WebSocket

IO, сводятся к реализации функционала публикации сообщений и подписки на них, к возможностям отправки и получения сообщений. Задачи, которые помогает решить разработчику Socket.

SSL-трафик не может интерпретироваться корпоративными прокси-серверами, он проходит через них точно так же, как проходит любой HTTP-трафик, в результате использование протокола WebSocket поверх SSL не приводит к блокировке соответствующего трафика. При этом надо отметить бесполезность использования неких запасных механизмов для работы с протоколом WebSocket, так как браузеры уже давно поддерживают эту технологию. Они лишь неоправданно увеличивают сложность решений. Запасные механизмы для поддержки WebSocket предусмотреть можно, но в их использовании нет никакого смысла.

IO, для того, чтобы µWebSockets.js мог бы полностью заменить Socket. Одна из целей µWebSockets.js заключается в том, чтобы дать разработчикам возможности, похожие на те, которые есть в Socket. Это возможно в том случае, если не используется какой-нибудь особенный нестандартный протокол. IO без необходимости использования каких-либо обёрток более высокого уровня.

Надо отметить, что в описываемом релизе µWebSockets.js этим возможностям не было уделено особенного внимания, но сейчас над ними ведётся серьёзная работа. Многие компании сталкиваются с проблемами публикации сообщений и подписки на них при работе с WebSocket. Поэтому следите за новостями. То, что получится в результате, будет очень быстрым (тесты показывают, что µWebSockets.js уже оказывается быстрее Redis).

Итоги

В настоящее время µWebSockets.js развивается, в проект добавляются новые возможности, исправляются ошибки. Некоторое время уйдёт на то, чтобы избавиться от тех мелких недостатков, которых характерны для первых релизов новых программ. Учитывайте то, что речь идёт о большом проекте, состоящем из многих тысяч строк кода, написанного на C и C++, который хранится в трёх репозиториях. Здесь лежит JavaScript-обёртка — uWebSockets.js. Вот веб-сервер, написанный на C++ — uWebSockets. А вот — базовая библиотека, написанная на C — uSockets.

В этих компаниях стабильность и безопасность, что совершенно естественно и очевидно, являются важнейшими характеристиками используемого ими ПО. Проект, о котором идёт речь, используется компаниями, программы, применяемые которыми, создают огромную нагрузку на подсистемы ввода-вывода.

Уважаемые читатели! Планируете ли вы использовать µWebSockets.js в своих проектах?

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»