Используем Resque в Rails

в 13:10, , рубрики: resque, ruby, ruby on rails, метки: , ,

Resque — ruby-библиотека для создания фоновых задач, составления очередей таких задач и их последующего выполнения. Задачи могут быть любым ruby-классом или модулем, содержащим метод perform. В ruby-сообществе Resque пришел на смену Delayed Job (не знаю, кстати, почему проект перестал развиваться, весьма удобная была вещь на мой взгляд) и обладает большим количеством различных преимуществ, таких как разделение задач по разным машинам, приоритеты задач, устойчивость к разным утечкам памяти и еще, и еще, и еще. На этом вступление для тех, кто не может самостоятельно перевести первый абзац из README прошу считать законченным.

В данной статье будет показано как использовать resque и resque-scheduler в rails-приложении.

Подключаем resque в rails-проект

Для использования resque необходимо установить Redis — хранилище данных типа ключа-значение. С установкой проблем возникнуть не должно — пользователи Linux и Mac без труда найдут и исходники, и готовые пакеты, пользователи Windows сами выбрали путь мазохистов также должны справиться. Кроме того установить Redis можно с помощью самого Resque, подробнее об этом на гитхаб-странице проекта. После установки запускается все это простой командой redis-server.

Далее добавляем в Gemfile:

gem ‘resque’
gem ‘resue-scheduler’

Затем bundle install и поехали!
В config/initializers добавляем файл resque.rb примерно с таким содержанием:

uri = URI.parse("redis://localhost:6379/")  
Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)

где 6379 — порт, на котором запускается redis по умолчанию.

Для того, чтобы иметь доступ к нашим фоновым задачам через web-интерфейс, добавляем следующие строки в config.ru:

require 'resque/server'  
run Rack::URLMap.new "/" => AppName::Application,  "/resque" => Resque::Server.new  

и по адресу localhost:3000/resque вы можете увидеть всю необходимую информацию об очередях, задачах, смысле жизни и т.д.

Добавляем rake-таск(уж простите за такой транслит, но “задача” занята job’ом) — создаем файл lib/tasks/resque.rake со следующим содержимым:

require 'resque/tasks'  
task "resque:setup" => :environment

и запускаем ее:

rake resque:work QUEUE=*

Теперь в окне терминала вы можете видеть вывод выполняющихся задач.

Базовые функции

Для начала создадим некий класс, который и будет представлять из себя задачу, которую нам нужно выполнить. Перед этим создадим директорию для хранения классов задач, например app/jobs и добавим эту папку в config/initializers/resque.rb:

Dir["#{Rails.root}/app/jobs/*.rb"].each { |file| require file }

Содержимое же класса должно быть примерно таким:

class SimpleJob
 @queue = :simple

 def self.perform
    # здесь делаем важные и полезные вещи
    …
    …
    ...
    puts "Job is done"
 end
end

Обратите внимание на имя очереди — simple, это обязательно, как и метод perform. Добавить эту задачу в очередь можно так: Resque.enqueue(SimpleJob). Результат выполнения, строка “Job is done” будет выведена в консоль resque, кроме того по адресу localhost:3000/resque можно увидеть новую добавленную очередь и результат работы воркера.

Передать аргументы в perform также не составляет труда. Например изменяем наш класс следующим образом:

def self.perform(str)
    # здесь делаем важные и полезные вещи
    …
    …
    ...
    puts "Job is done! #{str}"
 end

И добавляем задачу в очередь следующим образом: Resque.enqueue(SimpleJob, “Yahoo!”). После перезпуска rake resque:work QUEUE=* в консоли вы увидите ожидаемое: “Job is done! Yahoo!”. Следует отметить, что все аргументы передаваемые через enqueue сериализуются в JSON, из-за чего объекты класса Symbol становятся обычными строками, а сложные объекты, например сущности ActiveRecord — хэшами. Соответственно, если вы хотите использовать методы и ассоциации ваших моделей, есть смысл передавать просто id объектов и выбирать их уже внутри perform.

Планировщик задач

Resque-scheduler — расширение Resque для задач, которые должны быть выполнены в будущем. Он поддерживает два типа задач — задержанные и задачи по расписанию. Добавим scheduler в наш проект. Добавляем конфигурацию scheduler в resque.rake, теперь он выглядит так:

require 'resque/tasks'  
require 'resque_scheduler/tasks'

task "resque:setup" => :environment  

namespace :resque do
 task :setup do
    require 'resque'
    require 'resque_scheduler'
    require 'resque/scheduler'      

    Resque.redis = 'localhost:6379'

    Resque.schedule = YAML.load_file("#{Rails.root}/config/rescue_schedule.yml")
 end
end

Как видно из этого участка кода, мы грузим расписание из файла config/rescue_schedule.yml:

every_two_minute_job:
 cron: "*/2 * * * *"
 class: SimpleTask
 args: some arg
 description: “every two minute job”

Для использования scheduler в rails-приложении нужно также добавить в config/initializers/resque.rb строку require 'resque_scheduler'

Теперь запускаем в консоли rake resque:scheduler и смотрим, как наша задача выполняется каждые 2 минуты. Добавлять задержанные задачи еще проще:

Resque.enqueue_at(2.minutes.from_now, SimpleTask, ‘some arg’)

чтобы выполнить задачу в определенное время,

Resque.enqueue_in(2.minutes, SimpleTask, ‘some arg’)

чтобы выполнить задачу через определенное время.
Также можно удалять добавленные задержанные задачи:

Resque.remove_delayed(SimpleTask, ‘some arg’)

Конечно же работу Resque можно и нужно тестировать, для этого есть соответствующие gem’ы:
github.com/leshill/resque_spec
github.com/justinweiss/resque_unit

Дополнительные ссылки

Код Resque на гитхабе
github.com/defunkt/resque

Resque-scheduler там же
github.com/bvandenbos/resque-scheduler

Документация онлайн
defunkt.io/resque/

Скринкаст от Райана Бэйтса
railscasts.com/episodes/271-resque

Гостевая статья Дэйва Хувера для RubyLearning Blog
rubylearning.com/blog/2010/11/08/do-you-know-resque/

Jon Homan — Background Jobs in Rails with Resque
blog.jonhoman.com/background-jobs-in-rails-with-resque/

Allen Fair — Understanding how Resque works
girders.org/resque-internals-for-heterogenous-systems

Автор: rsludge

Поделиться

* - обязательные к заполнению поля