Статьи

Новые рельсы блестящие: ActiveJob

активная работа

Одной из ожидаемых особенностей Rails 4.2 является введение библиотеки ActiveJob. Как и многие компоненты в Rails, ActiveJob служит в качестве уровня адаптера в некоторых из самых популярных библиотек очередей в экосистеме Ruby.

С новой библиотекой ActiveJob выбор библиотеки очередей с уникальным API больше не о чем беспокоиться. Rails теперь предоставляет уникальный интерфейс организации очередей, который позволяет вам менять жемчужины очереди по своему желанию, не изменяя код приложения. Хотите переключиться с отложенной работы на Backburner? Не проблема. ActiveJob позволяет вам делать это с минимальной болью.

Еще одна замечательная функция — полная интеграция с ActionMailer. Электронная почта всегда является одной из тех задач, которые могут быть выполнены без необходимости знать, что электронная почта была отправлена. ActiveJob также обеспечивает тот же уровень абстракции с методами deliver_later! и deliver_now! , с очевидной функциональностью.

Наконец, с добавлением ActiveJob, Rails 4.2 также будет включать библиотеку Global ID, которая предоставляет уникальный идентификатор экземпляра модели: gid://Application/Model/id . Это особенно полезно при планировании заданий, поскольку нам нужно ссылаться на объект модели, а не на сам сериализованный объект. Вместо того, чтобы планировщик был связан с конкретной моделью и ее идентификатором, планировщику просто нужно использовать глобальный идентификатор, чтобы найти точный экземпляр модели.

Чтобы полностью понять набор функций ActiveJob, нам нужно взглянуть на его основные функциональные возможности ActiveJob, функциональность ActionMailer и глобальный идентификатор .

Использование ActiveJob

В этом примере мы будем использовать приложение Rails 4.2 (точнее 4.2.0.beta2), которое имеет дело с геолокацией. В приложении есть модель Account с полями для почтового индекса, города, штата, широты и долготы. Поскольку почтовый индекс выдает результаты для других элементов в списке, это единственное, что наше приложение будет запрашивать у пользователя.

Ниже приведен пример класса нашего аккаунта:

 { "id": null, "name": null, "city": null, "state": null, "zipcode": null, "latitude": null, "longitude": null, "created_at": null, "updated_at": null } 

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

Давайте создадим нашу первую работу для этого действия!

Во-первых, обязательно добавьте gem 'delayed_job_active_record' в ваш gemfile, запустите пакетную bundle install в терминале и следуйте инструкциям на странице github delayed_job, чтобы полностью установить отложенное задание .

После установки отложенного задания (или выбранной вами системы очередей) вам потребуется сгенерировать класс задания из терминала. С помощью следующей команды новый файл появится в папке app / jobs :

 $ rails g job geolocate_account create app/jobs/geolocate_account_job.rb 

Приведенная выше команда генерирует следующий класс:

 class GeolocateAccountJob < ActiveJob::Base queue_as :default def perform(*args) # Do something later end end 

В вышеприведенном классе действие execute — это место, где будет жить вся логика задачи. Для нашего приложения мы хотим найти геолокацию по почтовому индексу. Мы будем использовать geocoder gem, который дает нам API для подключения к стороннему сервису геолокации. Вот код:

 def perform(account) result = Geocoder.search(account.zipcode).first if result account.latitude = result.latitude account.longitude = result.longitude account.city = result.city account.state = result.state account.save! end end 

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

 Account.create(name: 'Dunder Mifflin', zipcode: '18505') 

А затем поставьте в очередь учетную запись с нашим GeolocateAccountJob :

 GeolocateAccountJob.perform_later Account.first 

Вы можете видеть, что задание было успешно поставлено в очередь, запустив Delayed::Job.count . Вы также можете просмотреть свои журналы и увидеть, что задание успешно зафиксировано в базе данных.

Очередь в будущее

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

 GeolocateAccountJob.perform_later Account.first 

Однако у вас также есть возможность поставить в очередь задания на будущее:

 GeolocateAccountJob.set(wait: Date.tomorrow.noon).perform_later Account.first 

и

 GeolocateAccountJob.set(wait_until: 1.day).perform_later Account.first 

Оба этих фрагмента кода дадут примерно одинаковые результаты.

Вы также можете установить приоритет заданий в очереди:

 GeolocateAccountJob.set(queue: :low_priority).perform_later Account.first 

Спасение от неудачи

Еще одна отличная особенность ActiveJob — это возможность ловить исключения, возникающие в вашем методе execute. Используя тот же класс GeolocateAccountJob , давайте изменим наш метод execute, чтобы ServiceDown исключение ServiceDown :

 class GeolocateAccountJob < ActiveJob::Base queue_as :default def perform(account) raise Geolocation::ServiceDown end end 

Затем мы можем добавить спасательный блок для обработки нашего исключения:

 class GeolocateAccountJob < ActiveJob::Base queue_as :default rescue_from(Geolocation::ServiceDown) do |exception| # Handle failed exception end def perform(account) raise Geolocation::ServiceDown end end 

Интеграция с ActionMailer

Еще одна полезная функция, включенная в ActiveJob, — это интеграция с ActionMailer. Отправка электронной почты, безусловно, является функцией, которая должна выполняться через очередь. Вместо того, чтобы использовать методы Mailer выбранной вами библиотеки, используйте два метода, которые ActiveJob изначально поддерживает функцию delivery_now deliver_now! и deliver_later! ,

Как следует из его названия, deliver_now! выполняет отправку сразу. Используйте это вместо устаревшего метода deliver . Метод, который реализует очереди, является deliver_later! ,

 AccountMailer.welcome(Account.first).deliver_now! AccountMailer.welcome(Account.first).deliver_later! 

Нужна такая функциональность, как set ? Используйте эти необязательные параметры!

 AccountMailer.welcome(Account.first).deliver_later!(wait: 1.hour) AccountMailer.welcome(Account.first).deliver_later!(wait_until: 10.hours.from_now) 

Глобальный идентификатор

Последней функцией в библиотеке ActiveJob является Global ID, который создает URI для представления экземпляра модели. Это полезно в ситуациях, связанных с работой. Вместо сериализации целого объекта или пары «идентификатор / класс» просто сохраните глобальный идентификатор экземпляра в задании.

Во время выполнения задания Глобальный идентификатор используется для поиска соответствующего экземпляра.

Давайте посмотрим на это в действии:

 account = Account.first account.global_id.to_s => "gid://sample/Account/1" GlobalID::Locator.locate account.global_id => Account:0x007f9746c8a1b0 account.global_id.app => "sample" account.global_id.model_name => "Account" account.global_id.model_class => Account(id: integer, name: string, city: string, state: string, zipcode: string, latitude: integer, longitude: integer, created_at: datetime, updated_at: datetime) account.global_id.model_id => "1" 

Обратите внимание, что по отношению к глобальному идентификатору хранится много данных, что позволяет получить много метаданных об экземпляре объекта.

Не таким отдаленным родственником Global ID является Signed Global ID, который похож на Global ID, за исключением того, что это подписанный объект. Доступ к подписанной версии можно получить с помощью метода sgid объекта.

 account.sgid => SignedGlobalID:0x007f97470cac98 account.sgid.to_s => "BAhJIhtnaWQ6Ly9zYW1wbGUvQWNjb3VudC8xBjoGRVQ=--4b86065bf01ecf72de68ea3d34d69f5241178ea1" 

SignedGlobalID может использоваться для проверки и извлечения GlobalID и включает возможность установить дату истечения срока действия для подписанного глобального ID.

ActiveJob совместимые библиотеки

На момент написания этой статьи ActiveJob может поддерживать следующие библиотеки очередей:

Завершение

ActiveJob является мощным и очень необходимым дополнением к инфраструктуре Rails. Теперь, когда вы понимаете библиотеку ActiveJob, аналог ActionMailer и Global ID, вы можете использовать ActiveJob в полной мере. Мне бы хотелось узнать больше о том, как вы использовали ActiveJob для своего приложения и о том, что вы уже узнали. Пожалуйста не стесняйтесь оставить комментарий. Давайте поговорим!