Node.js или Java
Производительность, ресурсы, управление потоками, популярность и личный опыт
Недавно мы с коллегами обсуждали вопрос популярности некоторых технологий — в частности Java и node.js. После недолгого интернет-серфинга выяснилось, что именно эти технологии используют многие информационные гиганты для разработки и поддержания своих площадок в сети. Ниже, я приведу лишь малую часть.
He менее интересным является и то, что по данным поиска на indeed.com (28.06.2019) по запросам Java Developer (30272 вакансии) и node.js developer (7401 вакансии), специалисты по этим технологиям довольно востребованы.
Но все это лишь общая информация, касающаяся популярности. Информация, которая натолкнула меня на мысль углубиться в тему и порассуждать на предмет технических особенностей, что привело к написанию этой статьи.
Почему их стоит сравнивать
Java — это язык, node.js — можно назвать экосистемой построенной на базе JS, и, прежде всего, на базе V8 — движка от Google.
Однако, когда мы говорим про Java, мы говорим не только про язык, а про виртуальную машину Java, а также всю экосистему и инфраструктуру построенную вокруг этой машины. Как минимум, их можно сравнивать по этому признаку — как результат, в обоих случаях, мы имеем среду исполнения. В случае Java — это виртуальная машина. В случае node.js — это движок V8 который представлен на большинстве ОС, таких как Windows, Linux, MacOS и менее известных.
Разработчики могут писать код пользуясь одним и тем же языком, и это будет работать более-менее одинаковым образом на разных ОС за счет того, что существует среда исполнения. Среда исполнения влияет на то, как происходит взаимодействие с ОС. Кроме того, их можно сравнивать т.к. они используются для решения похожего круга задач.
V8 и JVM
Когда в v8 попадает код JS, осуществляется just in time компиляция в байт код, который используется в виртуальной машине, код на JS выполняется все быстрее и быстрее.
Байт код — это промежуточный язык высокого уровня, поэтому в виртуальной машине Java пишут не только на Java, но также на Scala и Kotlin.
Есть предпосылки тому, что в ближайшем будущем для V8 можно будет использовать не только JS но и TypeScript или другие. На данный момент идет транспайлинг этих языков в JS. В будущем, они вероятно будут поддерживаться из коробки, и все будет работать намного быстрее.
Сейчас идет непрерывное развитие V8, и по большому счету, появление новых версий node.js связано с появлением новой версии движка V8. Они прямо взаимосвязаны.
Node.js: преимущества и недостатки
Node.js был создан Райаном Далом (Ryan Dahl) в 2009 году.
Сам node.js включает в себя несколько основных составляющих:
- движок V8;
- библиотеку libuv, которая отвечает за центральную часть node — цикл событий (event loop), который осуществляет взаимодействие с ОС, а так же за асинхронный ввод/вывод (I/O);
- из набора различных JS библиотек и непосредственно самого языка JS.
Перейдем к его плюсам и минусам.
Плюсы:
- легкость и скорость написания
- легковесность
- относительная простота (в сравнении с java)
- npm (node package manager (огромное количество библиотек которые могут быть установлены в одну строку)
- каждая библиотека попадает в дерево зависимостей и это все делается легко
- постоянное развитие (сейчас активно развивается TypeScript (который привносит в JS типизацию, декораторы и используется например для Angular)
Минусы:
- гибкость и быстрое развитие порождает также и минусы т.к. надо постоянно следить за обновлениями, некоторые вещи выходят недостаточно протестированными;
- был случай, когда разработчик удалил свою библиотеку из NPM и множество приложений использующих ее перестали работать;
Преимущества и недостатки Java
В противовес сразу рассмотрим основные характеристики Java.
Плюсы:
- скорость работы,
- распространенность (в ВУЗАХ многих стран изучают java, также на java удобно изучать ООП),
- огромный набор библиотек.
Минусы:
- тяжеловесность,
- некоторые парадигмы Java создавались давно и уже устарели,
- JDK проприетарен, поэтому Java развивается медленно.
В последнее время JS начинает обгонять Java (и чем дальше, тем больше).
Также Java уходит из мира Android, ей на смену приходит Kotlin который хоть и использует JVM, но все же является другим языком.
Конфликт Oracle и Google
Java была создана компанией Sun, которая позже была выкуплена компанией Oracle и по сей день принадлежит ей. По этой причине, для многих компаний использование Java создает некоторые проблемы.
У Google возникли проблемы, когда Oracle начали с ними судебное разбирательство за использование Java в Android. Из-за этого Google очень активно принял Kotlin, который появился независимо. Java является проприетарной. Но есть виртуальная машина Oracle, а также открытая виртуальная машина Java (open JVM), которая используется в Linux и написана в open source. Иногда существуют некоторые несовместимости, но в последнее время их все меньше и меньше.
Кстати, Google так и не смог полностью отказаться от Java. В Dalvik, который используется как ядро в Android, вшит JVM. Возможно от этого уйдут, но сделать это будет очень сложно т.к. практически вся экосистема Android построена на Java — прежде всего — на использовании модернизированного JVM. И это, в какой-то момент, тоже было причиной конфликта между Oracle и Google, потому что Oracle запрещает просто так модернизировать JVM. Это самая важная часть Java. А сам язык можно использовать практически без ограничений.
Java vs node.js: производительность и ресурсоемкость
Прежде всего стоит отметить, что производительность Java намного выше чем у JS, и, соответственно, node.js.
Если запустить какую-то простую задачу, вроде возведения в квадрат, то в тестах показатели могут различаться до 10 раз. Если запустить циклы в миллионы задач калькуляции, Java практически всегда будет превосходить node.js. Плюс, огромное различие между Java и node.js в том, что node является однопоточным, это является как его преимуществом, так и недостатком с другой стороны.
Java умеет работать с потоками, которые поддерживаются на уровне ОС, и получается, что программа написанная на Java наиболее полно использует возможности ОС. И если нужно написать высоконагруженное приложение, которое будет использовать большое количество вычислений, то Java для этого однозначно подойдет лучше. Проблема в том, что даже маленький сервер написанный на Java будет занимать много памяти — как на диске, так и оперативной.
Node.js является легковесным за счет архитектуры построенной на обработке событий. Он построен для работы в качестве веб-сервера и очень хорошо справляется с обслуживанием легковесных задач. Например, простой запрос вроде расчета чего-либо, или записи в базу данных происходит очень быстро. А если запросов становится очень много и мы хотим масштабировать систему в node, можно использовать веб-сервер Nginx или Apache. Можно завести много одинаковых инстансов node. Тогда все будет распределяться через балансировку нагрузки по round-robin. Если мы запустим 8 инстансов node на 16 ядер соответственно, ОС сама распределит инстансы между ядрами. Node этим не управляет, у него будет один поток.
Управление потоками в Java и node.js
В Java мы можем создать приложение и запустить в нем 8 потоков. За счет того, что происходит более тесное взаимодействие с ОС, можно распределить нагрузку.
Как известно, один из веб-серверов написанных на Java — это tomcat. Там можно четко проследить, что когда пользователь делает запрос, запускаются дополнительные потоки. А когда приходит запрос на node, цикл событий (event loop) будет обработан и отправлен обратно, затем придет следующий запрос. И за счет того, что мы не ждем результатов первого, он тоже будет подхвачен. Пока запросы легковесные, все хорошо. Однако, когда производится тяжелое вычисление, при наличии одного инстанса, node останавливается и наступает тайм-аут.
На node можно прописать буквально несколько строк кода и получить простейший веб-сервер. Естественно, для более широкого функционала, где будут нотификации, авторизации, логирование и т.д. это сложнее реализовать, но существуют фреймворки которые позволяют решать такие вопросы.
На Java есть развитое API — concurrency api, которое позволяет работать с конкурентными потоками. Но в то же время, это является и одной из проблем т.к. конкурентность это очень сложная штука и далеко не каждый разработчик хорошо в этом разбирается.
Веб, REST API — это стихия node, и иногда именно его и используют. Но если мы имеем дело со сложными калькуляциями, все же лучше использовать Java.
Мой проект на Java
На Java у меня был интересный проект — распределенное приложение, основной задачей которого была обработка больших объемов графической информации для дальнейшего использования в каталогах. При создании каталога необходимо подготовить наборы большого количества изображений различных разрешений, которые будут использованы при создании каталога. Проще говоря — это приложение для автоматизации предпечатной подготовки каталога.
Раньше фотографам приходилось делать все вручную. Для начала нужно было использовать какое-то небольшое приложение для того, чтобы загрузить свои изображения. Далее, специалист создававший каталог должен был разработать структуру каталога через другое приложение. Потом, в другом приложении, создавался воркфлоу, который раскидывал картинки в ту структуру, которая была создана. В общем, процесс был довольно тяжелым. Использовался ImageMagick который есть на Linux, Windows, MacOS. Мы имели дело с Linux.
Например, в приложение была загружена .tiff картинка размером 200–300 мб, а из нее нужно было сделать картинки различных разрешений, что-то вырезать, или сделать подложку.
Первая версия приложения не справлялась с большой нагрузкой, не хватало даже сервера с 16 ядерным процессором. Мы улучшили архитектуру приложения для использования нескольких инстансов одновременно, чтобы кардинально не менять работу приложения. Запускалось много инстансов, которые взаимодействовали между собой и каждый из них обрабатывал кусок задачи. Было сложно, но нам удалось успешно реализовать все буквально за пару месяцев. И система до сих пор работает. В процессе пришлось разбираться с конкурентностью и различными аспектами взаимодействия.
Что-то из этого проекта все же можно было перенести на node, но некоторые вещи все равно пришлось бы делать на Java, т.к. там было много различных вычислений. В принципе, мы могли сделать части на node, которые бы вызывали определенные части на Java и использовать микросервисную архитектуру. Можно было использовать смешанный вариант. Но этот подход не всегда работает, т.к. разработчик специализирующийся на node, может не являться специалистом в Java и наоборот. А найти универсальных разработчиков гораздо сложнее.
Из опыта на node.js
Был проект по организации большого объема данных. Чем-то похоже на проект описанный выше. Только здесь у нас загружается файл, который содержит большой набор информации и подлежит валидации через сторонний сервис (написанный на java), несколько раз и по разным правилам. Нужно было обрабатывать сотни гигабайт информации, а node для этого не предназначен.
Проектировать архитектуру системы было особенно интересно, т.к. приложение состояло из нескольких микросервисов, в том числе сторонних. При работе со сторонним сервисом, который осуществлял валидацию, мы использовали RabbitMQ message broker. Мы отдавали стороннему серверу необходимую информацию и получали сообщение от RabbitMQ после окончания валидации, затем данные обрабатывались по частям, чтобы избежать out of memory.
И если изначально приложение обрабатывало файл, содержащий 10000 записей, то теперь может обрабатывать до миллиона. Нам все же удалось решить эту задачу с помощью node.js, хотя на Java ее можно было решить проще, однако заказчик хотел использовать именно node, т.к. нужна была единая инфраструктура и архитектура с микросервисами написанными на JS. Используя node решить задачу было намного сложнее, и потребовало больше времени, но node.js выигрывает за счет скалируемости. Именно поэтому сейчас мы можем наращивать количество воркеров и обрабатывать все больше и больше данных. На Java это происходило бы сложнее.
На самом деле, любую задачу можно решить и так и так, но тут стоит повториться: если есть много вычислений то лучше использовать Java, если вычислений не много, можно смело использовать node.
Итоги и перспективы: сможет ли node.js обогнать Java?
Сейчас идет к тому, что node.js часто будет использоваться как обертка, а начинку будут писать на других языках. Его недостатки давно известны. Например, такой условный недостаток как однопоточность уже исправлен. В последней версии node представлена возможность использования нескольких потоков.
Java изначально создавалась как легковесное решение заменяющее C++, а теперь стала тяжеловесной. Это как эволюция. Возможно, когда-нибудь появится что-то, что заменит и node.
Сейчас, по количеству заказов, и по моим ощущениям, node.js уже обогнал Java.
JS активно развивается и он будет меняться — возможно что-то придет ему на смену.
Сейчас не видно потенциального конкурента, который смог бы заменить Java и node.js.
Проблема в том, что развитие Java в последнее время идет довольно медленно, а node.js развивается с такой скоростью, что заменить его в ближайшее время не представляется возможным.