Разработка → Когда Феникс успел убить Рейлс?

перевод
jarosluv 6 октября в 09:59 6,1k
Оригинал: Stefan Wintermeyer, …


Случается, что некоторые задачи не удаётся решить на привычном для вас языке программирования. Предположим, перед нами встала задача реализовать обмен сообщениями в реальном времени при больших объёмах трафика. Как сделать это оптимально? По понятным причинам, Руби – не лучший для этого вариант, поэтому придётся начинать поиски альтернатив. Перед вами появится множество путей, и как для рубиста, самым оптимальным выбором будет использование Эликсира.


Эликсир – функциональный язык программирования с синтаксисом Руби, работающий на виртуальной машине Эрланга. Следовательно, для человека из мира Руби знакомство с языком пройдёт очень легко. Хотя для того, чтобы понять его полностью, придётся постараться, не раз перечитывая документацию.


Ответ на вопрос из заголовка и масса интересного об Эликсире сразу же под катом.


Никогда! Феникс не убийца Рейлс, а напарник


Структура веб-приложения на обеих платформах довольно похожа. И это не удивительно: один из наиболее значимых участников сообщества Феникса – бывший член основной команды Рейлс, а большинство людей, занимавшихся разработкой фреймворка – рубисты. Кое-какие различия всё-таки присутствуют.


  • Внедрение подхода а-ля «Clean Architecture» для построения приложения. Самые значимые изменения пришли вместе с последней на данный момент версией Феникса 1.3. Подробнее об этом можно узнать из этой статьи. Отделение частей Феникса от частей приложения, ограничение бизнес-логики контекстами, использование репозиториев для работы с базой данных – всё это не типично для мира Рейлс.
  • Зонтичные приложения aka Umbrella app – идея отказа от преждевременного использования микросервисов и разделение объёмной бизнес-логики на части внутри одного большого приложения. Подробнее об этой возможности Эликсира читайте в этой статье.
  • Представления в Фениксе называются шаблонами и больше похожи на декораторы, но теоретически это не должно вызвать особых трудностей, хотя и меняет идею взаимодействия, делая её более походящей на популярный сейчас Ханами.
  • Ещё одна из особенностей Феникса – каналы. При каждом подключении к каналу создаётся новый процесс, который в последствии при возникновении ошибки никак не повлияет на другие подключения. Более подробно мы рассмотрим эту тему далее. Кроме того, Феникс предлагает фронтенд-библиотеку с отличными абстракциями для соединений с неструктурированным веб-сокетом.
  • Все прелести языка Эликсир, о которых будет рассказано далее.

Также присутствует огромное количество мелких различий, начиная от встроенных инструментов для работы с фронтендом, заканчивая отличными от Рейлс идеями и философией.


Виртуальная машина Эрланга и с чем её едят


Руби – объектно-ориентированный язык программирования. Это интерпретируемый язык, созданный Юкихиро Мацумото с расчётом на продуктивность и простоту. Эликсир же – функциональный язык, созданный Жозе Валимом и работающий на виртуальной машине Эрланга, называемой BEAM. Программы компилируются в байт-код для BEAM, которая выглядит как один процесс операционной системы, но на самом деле таит в себе целую вселенную.


BEAM работает на сервере непрерывно. Процесс Руби (и Рейлс) запускается только тогда, когда сервер начинает обрабатывать запрос. Поэтому, если, к примеру, необходимо выполнять какое-то действие каждый час, в среде Рейлс придётся использовать CronJob, в то время как в Фениксе об этом позаботится маленькая Эликсир-программка внутри BEAM.


defmodule Stack do
  use GenServer

  # Callbacks

  def handle_call(:pop, _from, [h | t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item | state]}
  end
end

# Start the server
{:ok, pid} = GenServer.start_link(Stack, [:hello])

# This is the client
GenServer.call(pid, :pop)
#=> :hello

GenServer.cast(pid, {:push, :world})
#=> :ok

GenServer.call(pid, :pop)
#=> :world

В Эликсире для выполнения задачи существует возможность создать новый процесс (который в магическом мире Эрланга ведёт себя скорее как тред, хотя по сути не является ни тем, ни другим), тогда как в Руби в большинстве своём приходится помещать фоновые задачи в отдельную очередь (например, через Sidekiq), чтобы можно было осуществлять масштабирование независимо от веб-приложения.


Поскольку процессы являются изолированными, а при обмене сообщениями создаётся новая копия данных, больше не придётся беспокоиться о повреждении или изменении данных по ту сторону системы. Операции внутри процесса синхронные, поэтому порядок его выполнения намного адекватнее, нежели в той же Ноде. Ещё одно преимущество – эффективный сборщик мусора. Как только процесс «отработал своё», он освобождает память для ОС.


Потоки Руби (JRuby, Rubinius) имеют свойство взаимодействовать с данными других потоков, что впоследствии достаточно проблематично отладить. В таких случаях довольно быстро развивается состояние гонки.


Работа с веб-сокетами


Приложения реального времени (например, чаты) используют канал обмена данными (например, веб-сокет). Создавать такие с помощью Феникса – проще простого. С Рейлс это тоже возможно, но компонент ActionCable, обслуживающий соединения с веб-сокетами, едва ли справится с первой тысячей одновременных подключений. С точки зрения Феникса, алгоритм ActionCable для отправки сообщений от сервера к клиенту до смешного прост, ненадёжен и плохо масштабируем. Чтобы вы понимали, насколько выигрышнее в данном случае использовать Эликсир и Феникс: приложение сможет спокойно обрабатывать пару сотен тысяч одновременных подключений от одного сервера.


Справедливости ради, если вы столкнулись с проблемами кабеля Рейлс на работающем приложении, советую взглянуть на AnyCable, одна из реализаций которого сделана как раз на Эрланге. Тем самым вы получите преимущества BEAM на Рельсовом приложении.


Запуск объёмных веб-приложений с BEAM особо приятен: масштабирование на многочисленных серверах проходит очень легко. Создавая проект на Фениксе, можно вообще забыть о проблемах с масштабированием. Кстати об этом… Вы же знаете, что сервер WhatsApp тоже работает на BEAM?


~~~


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


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


Может, и не самый дешёвый вариант по части железа, но точно масштабируемый!


~~~


Функциональное программирование vs ООП


Иногда Эликсир слегка напоминает Руби. Это ощутимо сбивает с толку, ведь руби принадлежит миру объектно-ориентированного программирования, а Эликсир построен на принципах функционального. Это же две параллельные вселенные! Если вы с Руби уже давно перешли на «ты», то для понимания Эликсира всё равно потребуется время.


Феникс – это не просто шустрый Рейлс, написанный на Эликсире


Давайте рассмотрим, что представляет собой процесс отправки сообщений в Эликсире, с помощью следующего модуля:


defmodule Example do
 def start_link do
   spawn(fn() -> loop() end)
 end

 def loop do
   receive do
     {:hello, pid} -> IO.inspect("Got hello from #{inspect pid}")
   end

   loop()
 end
end

А теперь откроем IEx:



iex(1)> pid = Example.start_link()
#PID<0.120.0>

iex(2)> send pid, {:hello, self()}
"Got hello from "#PID<0.118.0>"
"#PID<0.118.0>"
{:hello, #PID<0.118.0>}

Смахивает на ООП, не правда ли? В сообществе Эрланга/Эликсира даже с юмором отмечают, что эти два языка больше соответствуют концепциям ООП, чем другие популярные языки программирования. Так значит, Эликсир – объектно-ориентированный язык? Ничего подобного. Но об этом чуть позже.


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


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


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


Конкурентность


Про Руби можно сказать, что конкурентность в нём хромает. Да, в некоторых версиях Руби поддерживается потоковая конкурентность, но встроенный в MRI (оригинальный Руби, написанный на языке Си) способ синхронизации потоков GIL препятствует конкурентному выполнению задач. Нужно отметить, что Руби всё-таки позволяет осуществлять конкурентные операции ввода-вывода.


Эликсир же просто создан для этого. В прямом смысле. Конкурентность в нём реализована на основе так называемой «модели акторов» – концепции, реализованной виртуальной машиной Эрланга ещё до того, как этот термин вообще увидел свет.


Идея заключается в том, что можно создавать изолированные процессы, взаимодействующие между собой посредством обмена сообщениями и при этом не влияющие на состояния друг друга. Пример такой модели вы видели в предыдущем подразделе.


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


Работа с базами данных


Давайте сравним самые популярные инструменты для работы с базами данных в Руби и Эликсире соответственно – ActiveRecord и Ecto. И тот, и другой позволяют делать запросы и использовать валидации, но всё же функционируют совершенно по-разному. Для рубистов ActiveRecord прост и понятен в использовании, кроме того, совсем не требует знания SQL. История с Ecto совершенно противоположная: разобраться в нём сложнее, плюс необходимо базовое знание SQL.


defmodule Sample.App do
  import Ecto.Query
  alias Sample.Weather
  alias Sample.Repo

  def keyword_query do
    query = from w in Weather,
         where: w.prcp > 0 or is_nil(w.prcp),
         select: w
    Repo.all(query)
  end

  def pipe_query do
    Weather
    |> where(city: "Kraków")
    |> order_by(:temp_lo)
    |> limit(10)
    |> Repo.all
  end
end

Можно сказать, что ActiveRecord – секретное оружие Рейлс (от которого правда все стали воротить нос). Про Ecto такого не скажешь. Ecto хорош, но Феникс и без него на высоте, однако Рейлс без ActiveRecord не был бы самим собой.


Гемы vs пакеты


Гем – это созданный другим разработчиком код, который с легкостью можно интегрировать в своё приложение. По сути обычная библиотека. Большинство рубистов в своих проектах постоянно пользуются одним и тем же набором гемов. Можно сказать, что код без гемов – это как еда без соли. Чтобы оценить всю мощь экосистемы гемов, загляните сюда.


В мире Эликсира такие кусочки программ от сторонних разработчиков называются пакетами. Экосистема пакетов пока не доросла до масштабов экосистемы гемов (подробнее по ссылке). Судя по тому, как работают над Фениксом и Ecto, пакеты не должны создавать проблем с обновлениями в будущем. Но сказать точно можно будет только спустя пару лет.


Скорость


Можно с уверенностью сказать: Феникс быстрее. Намного быстрее, чем Рейлс.


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


А скорость разработки в Руби и Эликсире (а также на Рейлс и Фениксе) занимает приблизительно одинаковое время. Разумеется, переходя с одного фреймворка на другой впервые, нужно понимать, что потери времени неизбежны.


Быстрое развертывание без простоя


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


Придётся попотеть, чтобы решить эту задачу в среде Руби, а Эликсир справится с ней на ура: в BEAM специально для этого есть встроенный функционал – горячая замена кода. Для многих это становится чудом. Вот только скорее всего придётся отказаться от Докера. Короче говоря, одни плюсы.


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


Юкихиро vs Жозе Дэвид vs Крис


Юкихиро Мацумото и Жозе Валим по-настоящему значимые люди, каждый из которых сделал очень много для своего языка и сообщества в целом. Но давайте сейчас подробнее поговорим о создателях движущих сил каждого из языков. Над такими проектами с открытым кодом, как Рейлс и Феникс, как правило, работают большие команды разработчиков. В подобных командах ключевую роль обычно играют создатели продуктов. Они определяют направление развития и ведут публичную деятельность.


Создатель Рейлс Дэвид Хайнемайер Хенссон aka DHH выступает с докладами с самого первого дня своего детища. Он отличный оратор. Ранее его доклады на конференциях РейлсКонф всегда открывали глаза на многие вещи. Сейчас он немного сдал позиции, но его вклад всё равно колоссален.


Дэвид умеет придать наглядность сухим примерам. Помимо программирования он занимается фотографией и автоспортом. Но всё же его способностям «оживить» программный продукт или книгу (да, он ещё и писатель) сложно составить конкуренцию.


Дэвид может показаться высокомерным, но его можно понять. Сообщество Рейлс намного обширнее сообщества Феникса, и Дэвид просто физически не смог бы пообщаться на конференциях с каждым желающим, как это делает Крис.


Не то чтобы деньги были показателем успеха, но Дэвид стал мультимиллионером благодаря своей компании Basecamp, работающей на стеке Руби и Рейлс.


Создатель Феникса Крис МакКорд тоже с самого начала выступает публично. Крис был членом сообщества Рейлс, но как-то раз не нашёл решения нескольких вопросов и ради этого перешёл на Эликсир. Чувствуется, что он увлечён своим творением не меньше, чем когда-то Дэвид своим. На конференциях Крис более лоялен к новичкам; его доклады более адаптированы для неподготовленного слушателя.


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


Основная проблема обоих фреймворков


Самая большая трудность – поиск хороших разработчиков, что актуально как для Феникса, так и для Рейлс. Хотя для Феникса эта задача стоит гораздо острее. И это огромная проблема. Зачем нужен безупречный фреймворк, если некому будет в нем работать? Возможно, с Фениксом стоит подружиться первоклассным PHP-разработчикам?


Сегодня об Эликсире говорят все и везде. Самое время начать его изучать – прямо сейчас


Движущая сила технологий – это мы с вами, так что присоединяйтесь к разработке на Эликсире как можно скорее!


А какой фреймворк выбрали бы вы?


Подводя итоги, можно сказать, что Феникс предлагает немного более мощные технологии, но это ещё не показатель успеха проекта. Что действительно важно, так это наличие разработчиков, прекрасно разбирающихся в фреймворке, с которым они работают.


Если вы уже продолжительное время ведёте проект на Рейлс, будьте крайне осторожны при переходе на Феникс. На чужом дворе трава всегда зеленее. На своей практике встречаются удачные переносы, но почти всегда ими занимались мастера своего дела.


Если у вас уже есть команда талантливых Рейлс-разработчиков, желающих попробовать что-то новенькое, то стоит организовать какой-нибудь сайд-проект на Фениксе, понаблюдать, что из этого выйдет, а потом уже решать, стоит ли игра свеч.


Эликсир и Феникс можно использовать и для создания обычных CRUD-приложений, если ваша компания этого хочет по каким-то причинам, будь то функциональное программирование, наличие только одного сервера, новизна языка и т. п.


Если же скорость и развёртывание без простоя жизненно необходимы вашему проекту, то с Фениксом вам точно по пути.


И что бы вы ни выбрали, знайте, здесь нет неправильного варианта.


Заключение


Данная статья – это адаптация компиляции трёх материалов:



Над переводом работала Надежда / tresstensel.
Адаптацию материала выполнил Ярослав / jarosluv.


P.S. Всех заинтересовавшихся приглашаем на митап по Эликсиру в Рамблере 19 октября в 19:00. Для жителей других городов и стран будет организована онлайн-трансляция.


P.P.S. Ответы на вопросы по Эликсиру в Телеграме.

Проголосовать:
+18
Сохранить: