Используем Resque в 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
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 15

      –1
      Про скедулер не знал, спасибо :)
        +5
        Маленькое дополнение из собственного опыта. Если ставить задачу в resque в методах типа after_create, то можно столкнуться с тем что задача начнет выполняться раньше чем произойдет коммит в БД (вот такой он офигительно быстрый этот resque) и тогда объект еще не будет доступен для других соединений к БД, в том числе и для самого resque. В этом случае лучше использовать методы серии after_commit…
          0
          Я правильно понял что это аналог Celery в python?
            +1
            Статья как раз вовремя, только что собрался прикручивать Delayed Job из-за поддержки расписаний по-умолчанию. Но раз существует resque_scheduler, тем более появился такой хороший мануал, то естественно выбираем Rescue :)
            Автор, спасибо :)
              +2
              А вот есть интересная штуковина от большого специалиста по «асинхронным рельсам»:
              github.com/mperham/sidekiq
              Это то же самое, но жрет памяти меньше т.к. не process per job, а thread per job.
                +1
                О, celluloid в деле.
                  +1
                  Впихиваем невпихуемое или actor на рубях.
                +1
                DelayedJob перестал развиваться? оО Последний коммит 3 дня назад. github.com/collectiveidea/delayed_job Вкуснях в нем, конечно, поменьше, чем в Resque, но для большинства задач он вполне годится. И Redis не нужен, опять же.
                  0
                  «Вкусностей» там в три раза больше, чем в rescue. Ну разве что веб-морды нет изкоробки.
                    0
                    *resque
                      0
                      А можете пару наиболее примечательных вкусностей, по опыту? сейчас как раз думаю что именно выбрать для очередей. Ваше мнение было бы кстати
                        +1
                        Ну, если по-простому, то в Resque умеет сохранять только простые типы. Скажем, можно сделать так:

                        Resque.enqueue MyCoolJob, post.id
                        


                        или так:

                        Resque.enqueue MyCoolJob, 'string'
                        


                        но нельзя так

                        Resque.enqueue MyCoolJob, post
                        


                        В DJ есть очень сложный и навороченный сериалайзер. С его помощью можно сохранять практически любые объекты со стейтами. А так же делать такие штуки:

                        Post.create(params[:post]).delay.publish!
                        


                        Этот код запомнит объект со всеми его состояниями, а потом запустит у этого объекта метод publish!.
                          0
                          А если сложное приложение с несколькими БД, которые переключаются в зависимости от имени поддомена, то что лучше использовать? Не сталкивались с таким? Нужно будет в каждую задачу передавать параметры подключения к бд, или можно как-то проще?
                    0
                    А я юзаю DJ и крутой гем clockwork. Один раз поднял энвайрмент, и он висит в памяти, по таймеру добавляет задачу в DJ.

                    Выглядит как-то так:

                    # config/clock.rb
                    require_relative './environment'
                    require 'clockwork'
                    include Clockwork
                    
                    
                    every(1.day, 'user.pages.update', at: '12:00'){ User.unblocked.all.each(&:update_pages_info!) }
                    every(6.hours, 'user.apps.persistence.check'){ Delayed::Job.enqueue UpdateApplicationPersistenceJob.new }
                    every(1.day, 'pages.refresh_likes_count', at: '23:00'){ Facebook::Page.update_likes_count! }
                    
                      0
                      Defunkt кстати сам писал в описании Resque, что они до этого сидели на DJ и он их полностью устраивал и всем нравился. А Resque они начали писать, когда очереди разрослись слишком неприлично. Суть в том, что пока у тебя нет огромных нагрузок и тысяч очередей, DJ проще и удобнее. Кроме того, в нем офигенный сериалайзер. А Resque работает только с простыми типами.

                      Only users with full accounts can post comments. Log in, please.