Записки Вредного программиста

enjoy, motherfuckers ;)

Оповещение после выполнения "тяжелой" фоновой задачи с помощью Faye и PrivatePub

Допустим нам необоходимо после выполнения большой задачи в фоновом режиме сообщить об этом пользователю и совершить что-нибудь, например, показать ему какой-нибудь popup. Для этого по-старинке, можно с интервалом, скажем в 1 секунду опрашивать сервер и смотреть не завершилась ли наша задача, но в пору HTML5 делать это, по крайней мере, не престижно. Будем использовать для этих целей инструмент по обмену сообщениями между сервером и клиентом Faye.

Инструменты

Нам понадобится Rails приложение, к которому мы и будем привязывать всю эту функциональность. Также будем использовать Resque для выполнений фоновых задач – инструмент, зарекомендовавший себя, как надежный и стабильный помощник, спасающий всегда, когда нужно выполнить тяжелые задачи в фоне. Вместе с faye воспользуемся оберткой для него от Ryan Bates Private Pub, который мне очень облегчил жизнь, надеюсь, и облегчит вам.

Реализация

Для начала установим redis и обновим наш Gemfile, дополнив его необходимыми гемами resque, faye, private_pub, thin.

Я буду рассказать все на примере Mac OS, установка подобного инструментарий, скажем, на Ubuntu, не должна вызвать вопросов, потому что инструменты очень распространенные.

Устанавливал redis я с помощью всем известного пакетного менеджера Homebrew, напечатав в терминале всего одну команду brew install redis

Допустим у нас имеется какой-нибудь тяжелый объект с несколькими картинками, которые лежат в Amazon S3 и, чтобы создать копию этого объекта нам понадобится скачать все эти картинки, чтобы вновь их туда загрузить, привязав в новому объекту. Не спрашивайте почему так сложно, так работает CarrierWave или я просто не нашел лучшего решения.

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

1
2
3
def clone
  Resque.enqueue(CloneProfileWorker, params[:id])
end

Теперь тяжелая задача будет добавляться в очередь всякий раз, когда мы пройдем по ссылке /profiles/#{ id }/clone.

Давайте поставим и настроим PrivatePub, который будет со стороны клиента подписываться на определенные события, и со стороны сервера, после наступления определенного события (в нашем примере это, когда resque job отработает) делать нужные нам вещи.

Для этого в консоли нужно запустить rails g private_pub:install

и добавить в файл app/assets/javascripts/application.js[.cofeee] строчку #= require private_pub, если вы используете cofeeScript или же //= require private_pub, если js

и дальше во вьюхе /profile/clone.html.haml (я использую haml в данном проекте)

1
2
3
4
5
6
= subscribe_to "/profile_cloning_#{ params[:id] }"

:javascript
  PrivatePub.subscribe('/profile_cloning_#{ params[:id] }', function(data, channel) {
    location.href = data.url;
  });

Первая строчка это метод из гема, который инициализует объект необходимыми параметрами из файла /config/private_pub.yml, а после мы “подписываемся на событие ‘/profile_cloning_#{ params[:id] }’, где в params[:id] содержится текущий id профиля. При наступлении данного события, мы перенаправляем пользователя на страницу ‘/profile/#{ new_id }/edit”, полный урл мы получим после того, как resque job отработает.

app/workers/clone_profile_worker.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'resque'

class CloneProfileWorker
  @queue = :default

  def self.perform(profile_id)
    c = Profile.find profile_id
    new_profile = c.clone_self

    if new_profile.is_a? Profile
      PrivatePub.publish_to "/profile_cloning_#{ profile_id }", :url => "/profiles/#{ new_profile.id }/edit"
    end
  end
end

В данном воркере нет ничего магического: сначала мы клонируем наш профиль и если все прошло успешно, то оповещаем нашего клиента и передаем туда url, на который он перенаправится.

Проверяем работоспособность

Запускаем в разных окнах терминала:

  • rails s
  • redis-server /usr/local/etc/redis.conf (у меня MacOs, на других ОС должно быть нечто подобное)
  • VERBOSE=1 rake resque:work QUEUE=* (запускаем все очереди, устанвливаем verbose=1 для того, чтобы видеть что происходит внутри resque)
  • rackup private_pub.ru -s thin -E production (сервер для PrivatePub и Faye)

Проходим по ссылке наподобие /profiles/73/clone и смотрим в терминале как отрабатывает наш resque job и мы перенаправляемся на редактирование уже склонированного профиля, если все отработало без ошибок. Если же возникли ошибки, то они отобразятся в терминале, если произойдет что-то невообразимое и непредвиденное, пишите в комментариях, я попробую помочь вам.

Комментарии