В основном ответ на то, что Clojure — это JVM. Мол, эта хрень такая тяжёлая.
Это появилось на канале ZA Tech в группе Slack несколько недель назад. Во время некоторых выступлений по Clojure спикеры делали такое замечание снова и снова.
По этому поводу я выступил в Slack. Теперь запишу для более широкого чтения и обсуждения.
Предисловие
Я тоже раньше думал, что JVM тяжёлая. Это было в начале 2000-х, в сравнении с PHP. Там были и другие тяжеловесы, вроде .NET и ColdFusion. Были и более лёгкие альтернативы вроде Perl и Python, но я тогда сидел на Windows, так что ActivePerl и ActivePython тоже были несколько тяжеловаты.
Впервые я преодолел свой «страх» перед JVM, когда развернул небольшое производственное приложение JRuby на Heroku. Этот маленький монстрик должен был выполнять только одну задачу в день. Он генерировал ряд PDF'ов, потом загружал их на iSign (сейчас не функционирует) для хранения и распространения. Сам iSign был классическим приложением Rails, которое хостилось на трёх AMI. Этот маленький динозавр на стоковом JVM (за исключением -server -Xmx=512M
) производил PDF'ки так быстро, что он буквально убивал трёхнодовый кластер при каждом запуске.
Я по-прежнему думал, что он немного тяжеловат в работе, но влюбился в этого гадкого утёнка.
Я более-менее следил за событиями в разработке JRuby и историями успеха и замечательно провёл время на Rubyfuza 2015 с Чарльзом Наттером. Это меня настолько вдохновило, что я стал открывать пулл-реквесты в проектах Ruby, которые просто запускают их тесты на JRuby. На том одном и закончил, к моему стыду.
И к 2016 году
В ноябре 2016 года я попытался с нуля написать приложение Rails. Впервые за несколько месяцев программировал на Ruby на своей машине. Обновление brew upgrade
врезалось в rbenv и поэтому выбросило все установленные версии Ruby, а я даже не заметил.
Мне предстояла презентация по WebSockets на Jozi.rb.
В начале выступления хотелось поиграться с репозиторием React on Rails, просто для общего ощущения совместного использования React и Rails. Я уже пару месяцев использовал re-frame и был уверен, что смогу вытянуть его голым React'ом.
Поезд сошёл с рельсов, и очень зрелищно.
Для клонирования и запуска одного шаблонного приложения требовалось обновить XCode, обновить инструменты командной строки для XCode (в сумме более 6 ГБ), установить новую версию Ruby и Bundler, а затем связать инсталляцию в шаблонном приложении… Всё просто, верно? Как и у большинства приложений Rails, у этого шаблонного приложения где-то в графе зависимостей была зависимость от libv8, и только это прибавляет более 1 ГБ в размере.
Всё упражнение заняло несколько часов.
Поиграв с такой впечатляющей демонстрацией, я осознал, что работа превращается в бросание монетки. Поэтому решил лучше сделать фронтенд на Ember, ибо я знаю его, а времени не хватало.
Опять то же самое, нужно обновить nvm, установить приемлемую версию node, установить ember-cli, сгенерировать приложение и установить зависимости через npm и bower.
Я немного поигрался и признал своё поражение, а вместо этого поделился своим опытом с коллегами. Это было унизительно, по-настоящему унизительно. Я чувствовал себя чужаком в мире, частью которого был так долго.
Вернёмся к заявлению, что JVM тяжёлая.
Как будем взвешивать?
- Размер JDK при скачивании слишком большой?
- Он использует много ресурсов в работе?
- Библиотеки занимают много места на диске?
- Развёртывание слишком тяжёлое?
- Она затормаживает вас день ко дню?
Вот некоторые вопросы, которые помогают преодолеть наши эмоциональные барьеры насчёт JVM.
Так что давайте займёмся ими и посмотрим, что выйдет на самом деле.
Первоначальный объём действительно настолько велик?
Эта «тяжесть» JVM — чистый FUD, начиная с якобы большого объёма первоначальной установки. Вы сравниваете примерно 200 МБ скачивания JDK с примерно 15 МБ скачивания Node или Ruby. Но это только начало. И для Node, и для Ruby в системе нужен нужен компилятор C, только он один — сотни мегабайт. Хуже того, вероятно вам понадобится компилятор в продакшне!
Куча раздувающей нагрузки и для Node, и для Ruby прячется от вас такими маленькими последовательными шагами. Если вы остановитесь и посчитаете объём, не говоря уже о потраченном времени, то 200 МБ окажутся гораздо более оптимальным вариантом.
Даже не хочу начинать составлять инструкцию по сборке вашего собственного веб-комплекта, просто чтобы не переписывать его, как только выйдет новый инструмент.
JVM так тяжела в работе?
JVM быстра, это вероятно одна из самых быстрых сред из существующих. Со временем она просто становится всё быстрее и легче. Тысячи самых умных инженеров работают над её улучшением, а ещё больше внесли свой вклад за последний 21 год.
Здесь есть реальные потоки выполнения, поддержка многоядерности, её можно адски конфигурировать или просто оставить как есть. Единственной полезной вещью, которую вам вероятно стоит знать, это установки ограничений памяти для JVM, и тогда она покажет чудеса при работе с ограничениями этого окружения.
Развёртываете на Heroku? java -server -Xmx512m beast.jar
. Если этого недостаточно, то у вас вероятно есть деньги, чтобы заплатить кому-то за совет… Ой, ну или просто посмотреть на StackOverflow.
Вот ключевая мысль, которую Чарльз и другие парни из сообщества JRuby продолжают продвигать. Без всяких телодвижений, ваши приложения наверняка будут работать быстрее и быстрее с каждым релизом JVM (независимо от прогресса JRuby).
Использование диска настолько большое?
Мне было интересно, и я посмотрел в папку ~/.m2
, где за 9 месяцев разработки c Clojure накопилось 1010 МБ зависимостей. Даже гигабайта ещё нет.
$ du -sh /usr/local/opt/rbenv/versions/2.3.3 ~/.nvm/versions/node/v6.9.1 ~/.m2
690M /usr/local/opt/rbenv/versions/2.3.3
232M /Users/kenneth/.nvm/versions/node/v6.9.1
1010M /Users/kenneth/.m2
Установка Ruby снова свежая и соответствует базовым требованиям для ведения этого блога и Middleman (я там работаю над исправлением). Да, чтобы вести этот статичный блог и участвовать в разработке инструментов, на которых он работает, требуется почти 700 МБ места.
У Node установлены только ember, docpad и bower, и он более 200 МБ.
Развёртывание настолько грузное?
Вероятно, вы можете предсказать, о чём я буду говорить…
При сборке вы делаете единственный файл JAR. В нём есть всё для работы вашего приложения на любой платформе. Просто помещаете JAR где нужно и предоставляете JVM разбираться с ним.
Нет такого требования, чтобы приложение работало на каком-то массивном сервере приложений, можете очень легко связать с производительным HTTP-сервером прямо в JAR-файле. Парни из Node так делают, и из Ruby тоже, но как-то файлы JAR не могут оставаться сами по себе? Я тоже так думал…
Я, например, освобождён от необходимости запускать apt-get install build-essentials
в продакшне.
День ото дня с JVM
У меня работает минимум пять процессов JVM на моём 2012 MacBook Pro с 8 ГБ памяти. Работают постоянно и каждый день. Я никогда не пробовал запустить пять приложений Rails одновременно.
Почему пять? Два для Datomic (сбор данных и консоль), один для наших API бэкенда и один для того фронтенда, с которым я работаю. Иногда в фоне также идут автоматические тесты. Уверен, сжатие памяти macOS наверняка тут помогает, потому что у значительной части этих процессов JVM должна быть загружены в память одни и те же байты.
Если бы вы сказали мне об этом 10 месяцев назад, я бы только посмеялся. Кто в здравом уме запустит 5 или более процессов JVM? Я мог бы отдать руку на отсечение, что я никогда так не сделаю.
О, а что же насчёт путей классов и всех остальных сумасшедших вещей? Они вообще вас не коснутся благодаря отличному инструментарию Clojure. Это та же причина, по которой вы используете npm или bundler и не мучаетесь с включением путей. Вы могли бы делать это, но тогда у вас вероятно другая проблема, которую вы не замечаете.
Радости REPL
Если бы мне пришлось непрерывно останавливать и запускать экземпляры JVM, я бы точно сошёл с ума. Это беспокоило меня насчёт JRuby в старые времена. К счастью, с Clojure и великолепным REPL приходится перезапускать экземпляр JVM только в редком случае, когда я действительно где-то крупно облажался. Здесь довольно хорошая защита от дурака. Figwheel днями работает без проблем.
Заключение
Будьте осторожны, оценивая JVM как объект. Оценивайте Java как язык во всех смыслах, но не смешивайте его с виртуальной машиной.
Я тоже раньше думал как вы. Я думал о JVM как о том бегемоте. К счастью, теперь я смог избавиться от этих ограничений и получил результат работы тысяч гигантов, поддерживающих её.
Ни в коем случае не считайте этот пост как знак «конца Node» или «конца Ruby». Воспринимайте как свежую перспективу. Если не можете перейти на JVM, хотя бы подумайте, как уменьшить раздутость в своём собственном мире.
Спасибо, что уделили мне своё время. Изучайте Clojure и пробуйте Simple Made Easy.
Автор: m1rko