Статьи

Сравнение библиотек фоновой обработки Ruby: отложенное задание

Люди и дверь синие

Как только вы начнете работать с приложениями на Rails, выходящими за рамки типичного блога «учебного стиля», вам, вероятно, понадобится какая-то обработка, а не просто ответы на запросы пользователей.

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

Другим примером того, где вам может понадобиться такая «вещь» (назовем ее фоновой обработкой или фоновыми заданиями), может быть операция, которая занимает значительное время (скажем, как правило, все, что занимает более 400 мс) и вы не хотите заставлять своего пользователя ждать. Представьте, что у вас есть видео сайт. После того как пользователь загрузит видео, ваше приложение должно его кодировать, что может занять более десяти минут. Очевидно, что вы не можете просто держать пользователя зависшим на HTTP-запросе (который, скорее всего, истечет время ожидания), пока вы кодируете видео. Вместо этого вам нужно как-то сказать Rails: «Вот, возьмите этот код и запускайте в фоновом режиме, пока я отвечаю пользователю с некоторым HTML».

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

Давайте рассмотрим, как работает фоновая обработка, а также какие альтернативы предлагает сообщество Ruby для ее реализации в вашем приложении.

теория

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

Современные операционные системы позволяют запускать сразу несколько процессов на компьютере. Обратите внимание, что «сразу» было в кавычках; обычно не все процессы выполняются в один и тот же момент (упрощенное объяснение: если у вас четыре ядра, обычно одновременно могут выполняться четыре процесса). Часть операционной системы, называемая планировщиком, дает каждому процессу немного времени для запуска, а затем переключается на другой процесс, делая это для каждого процесса в системе. Таким образом, планировщик создает иллюзию всех процессов, запущенных одновременно, поскольку он так быстро переключается между процессами (подобно тому, как очень быстро мигает светодиод, как будто он постоянно включен).

Возможно, мы можем создать новый процесс (помимо тех, которые отвечают на HTTP-запросы) для каждой работы, которую мы хотим обрабатывать в фоновом режиме. Но, как оказалось, создание процессов не совсем легкая операция с точки зрения используемых вычислительных ресурсов. Если бы мы выполнили эту стратегию и имели 100 000 рабочих мест, нам нужно было бы создать 100 000 процессов! Вместо этого фоновая обработка может использовать очереди .

Очередь — это четко определенная структура данных, которая позволяет нам добавлять вещи в нее и выводить их в соответствии с определенными правилами. Это структура FIFO («первым пришел-первым вышел») , что означает, что если вы введете «a», то добавите «b», вы получите «a» перед «b» (немного похоже на добавление в начало). списка, а затем чтение из его конца). Дело в том, что мы можем добавить «задания» в очереди, и тогда работники (работающие в своем собственном процессе) будут обрабатывать их позже.

Оказывается, что управлять всем этим может быть сложно, если не сказать больше. Например, что будет делать система, если работник или работа занимают слишком много времени и задерживают оставшуюся очередь? Как бы вы убедились, что ваша очередь работает хорошо (помните, что сама очередь должна храниться либо в памяти, либо на диске)? К счастью для нас, люди из сообщества Ruby уже разобрались со многими этими вещами и собрали их в несколько разных библиотек, таких как delayed_job , Resque и Sidekiq, которые используют разные подходы к почти одной и той же проблеме.

Задержка :: Работа

Shopify избавился от собственного зуда с помощью Delayed :: Job, который был достаточно хорош для того, чтобы открыть исходные тексты и написать несколько документов, чтобы остальная часть сообщества Ruby могла воспользоваться этим.

Рассматривая только стабильные бэкэнды для Delayed :: Job (далее именуемого DJ), он использует ActiveRecord, Mongoid и т. Д. В качестве системы, на которой основывается очередь. Это означает, что вам не нужно запускать что-либо еще (например, Redis), чтобы служить вашей системой очередей; ActiveRecord, который есть в вашем приложении Rails, будет работать.

Начало работы с диджеем является самым простым из всех трех, поэтому многие люди предпочитают его. Добавьте следующее в ваш Gemfile:

gem 'delayed_job_active_record' 

Установите гем и настройте необходимые структуры базы данных, введя это в вашу оболочку:

 bundle install rails generate delayed_job:active_record rake db:migrate 

Код DJ действительно легко написать и понять. Все, что вам нужно сделать, это добавить .delay.method(parameters) к любому объекту для асинхронного вызова method (т. .delay.method(parameters) фоновом режиме; ваш код будет двигаться прямо и не будет ждать вывода) с заданными параметрами.

Например, если у вас есть следующая модель пользователя:

 class User < ActiveRecord::Base ... def send_newsletter(email) #send the newsletter here, which will take some time and you puts 'From send newsletter: ' + email end ... end 

Вы можете сделать следующее в контроллере:

 class IndexController < ApplicationController def index u = User.new #do some processing on the user object u.save #send the user a newsletter...delayed! u.delay.send_newsletter(params[:email]) end end 

Или вы можете изменить модель, чтобы гарантировать, что send_newsletter обрабатывается асинхронно независимо от того, что:

 class User < ActiveRecord::Base ... handle_asynchronously :send_newsletterend 

Затем вам нужно только это в контроллере (т.е. без .delay ), чтобы вызвать метод асинхронно (т.е. поместить его в очередь как задание):

 u.send_newsletter(params[:email]) 

Чтобы увидеть, как это работает, вы можете настроить контроллеры и модели, как отмечено, и запустить сервер Rails. Нам нужно сделать небольшую дополнительную настройку через нашу оболочку, чтобы наша очередь работала:

 rails generate delayed_job:active_record rake db:migrate 

Теперь, посетив «/ index / index», вы отправите задание в очередь (так как мы используем .delay ). Чтобы обработать работу, все, что нам нужно сделать, это ввести это в оболочку:

 rake jobs:work 

Вы должны увидеть «информационный бюллетень: готово!» В выходных данных. Большой!

Как вы можете видеть, DJ делает невероятно легким делать довольно сложные вещи (мы создали очередь, поместили в нее задание с помощью параметра email и обработали задание!). У них также есть хорошие документы и большое сообщество пользователей. В частности, DelayedJob позволяет действительно легко получить доступ к параметрам модели, поскольку методы прямо в вашей модели. К сожалению, у него есть и недостатки.

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

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

Вот где появляется нечто вроде Redis — это хранилище данных NoSQL, которое позволяет хранить данные в памяти (немного похоже на memcached). Redis был описан как «удаленный словарь», то есть это что-то вроде хэша Ruby, но на сервере. Может показаться, что Redis немного бессмыслен, если он хранится только в памяти (то есть, когда вы перезагружаете свой сервер, все исчезает), однако Redis может сохраниться и на диске!

DJ движется в сторону поддержки Redis. Однако поддержка сейчас нестабильна, поэтому я бы не стал использовать ее в производстве. Resque, с другой стороны, построен снизу вверх на Redis.

Пример приложения

Я создал пример приложения, чтобы показать простой пример отложенной работы в действии, которую вы можете запустить локально (в качестве отправной точки используется демонстрация выбора файла). По сути, вы загружаете вложение, и оно асинхронно устанавливает количество страниц. Однако, если вы выполняете действие «show» для одного из этих загруженных вложений (маршрут ресурса), он определяет количество страниц синхронно. Суть в том, чтобы показать, как легко вы можете настроить отложенный код задания, а также как можно смешивать синхронный и асинхронный код, используя только один метод (что, на мой взгляд, является недостатком delayed_job, поскольку затрудняет рассуждение об определенных методы).

Проверьте это здесь

Будьте на связи!

Вторая часть этой статьи расскажет о Resque и сравнит его с DelayedJob!