Active Job
Introduction
Active Job is a framework that helps developers writing codes and run them on the background automatically under different scenarios. It’s an interface that adapts different queueing backends like Backburner[1], Delayed Job[2], Qu[3] and so on. Jobs can vary from schedule newsletter, follow-up emails to database housekeeping. <ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” </ref> Overall, Active Job is a interface which you can work with common queues.
Ruby On Rails versions support Active Job
Version | Date |
---|---|
4.2 | 2014/12/19 |
4.2.5 | 2015/11/13 |
Active Job adapters<ref name="Active-Job-Adapter">Active Job Adapter"Active Job Adapter" </ref>
List of queueing backends Active Job support:
- Backburner[4]
- Delayed Job[5]
- Qu[6]
- Que[7]
- queue_classic[8]
- Resque 1.x[9]
- Sidekiq[10]
- Sneakers[11]
- Sucker Punch[12]
- Active Job Async Job
- Active Job Inline[13]
How to use <ref>http://guides.rubyonrails.org/active_job_basics.html</ref>
We will introduce how to creating a job and and how to add the job into a queue. <ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>
Download
With RubyGems you can install Active Job:
$ gem install activejob
Souce code of Active Job available on GitHub, as part of Rails.<ref> GitHub “Active Job -- Make work happen later” 2015 </ref>
Create a Job
In Active Job, a process inserted in a queue and waiting for carry out is called “Job”. It’s possible to generate a Job using the Generator provided by Rails. You can create a Job in app/jobs. By doing the following, we created a Job called “update_wiki”.
$ bin/rails generate job update_wiki invoke test_unit create test/jobs/update_wiki_job_test.rb create app/jobs/update_wiki_job.rb
Active Job provides the ability to run your Job on a specific queue by creating a job:
$ bin/rails generate job update_wiki --queue urgent
Files inside of app/jobs can be created manually, instead of using a generator. In Rails 4.2 an ActiveJob class inherits from ActiveJob::Base. In Rails 5.0, it has changed to now inherit from ApplicationJob. When upgrading from Rails 4.2 to Rails 5.0, an application_job.rb file is needed to be created in app/jobs/ and add the following content:<ref> Rails Guides “A Guide for Upgrading Ruby on Rails” 2015 </ref>
class ApplicationJob < ActiveJob::Base end
In Rails 4.2 a Job class defined a perform method and set a “queue_as” value:
class UpdateWikiJob < ActiveJob::Base queue_as :default def perform(*wiki) # Do something later wiki.update_contents end end
There is a perform method to be called when the Job was first enqueued.
Adding a Job to the queue
If you wish your Job be processed as soon as the queuing system is free, you can enqueue a Job like:
UpdateWikiJob.perform_later wiki
Or you can add a Job be performed tomorrow at noon:
UpdateWikiJob.set(wait_until: Date.tomorrow.noon).perform_later(wiki)
If you want your Job be performed a week from now, most of the queueing backends ( Sidekiq , Delayed Job, etc. ) allow you to set a delay time.
UpdateWikiJob.set(wait: 1.week).perform_later(wiki)
Execution of Job
Active Job provides adapters for multiple queueing backends (Sidekiq, Resque, Delayed Job and others).<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref> Without setting any adapter, the job would be performed immediately.
Queueing backend can be set at: /config/application.rb, in this example we use the Sidekiq.
module YourApp class Application < Rails::Application # Be sure to have the adapter's gem in your Gemfile and follow # the adapter's specific installation and deployment instructions. config.active_job.queue_adapter = :sidekiq end end
Play with Queues
Active Job allows to schedule the job to be processed on a specific queue, this became helpful as common adapters support multiple queues.
class UpdateWikiJob < ActiveJob::Base queue_as :low_priority #.... end
Queue name can be prefixed for all jobs using config.active_job.queue_name_prefixin application.rb:
# config/application.rb module YourApp class Application < Rails::Application config.active_job.queue_name_prefix = Rails.env end end # app/jobs/update_wiki_job.rb class UpdateWikiJob < ActiveJob::Base queue_as :low_priority #.... end # Now your job will run on queue production_low_priority on your # production environment and on staging_low_priority on your staging # environment
You can control a job to run on a queue you like, by passing a :queue option to #set :
MyJob.set(queue: :another_queue).perform_later(record)
All the code snippets are referred from the source<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>
Examples
In this section, a example is provided to demonstrate how to use ActiveJob in practice.
Background Mail Sender
You can send emails asynchronously with Action Mailer which Active Job is already integrated in<ref name= "Action-Mailer-Basics">Rails Guides “Action Mailer Basics” 2014</ref>. Here in this example, we try to use Active Job with Action Mailer.
1. Getting started with Rails<ref name= "Welcome_to_Rails">GitHub “Welcome to Rails” 2015</ref>
# Install Rails if you haven’t done it yet: $ gem install rails # Create a new Rails application, “myapp_activejob” # is the name of the application: $ rails new myapp_activejob # Change your directory to myapp_activejob $ cd myapp_activejob
Edit the Gemfile in the folder, add following into it:
gem 'responders'
Run “ $ bundle update “ and make sure “$ rails server” works.
2. Resque setup<ref name= "Download_Redis">Redis “Download Redis” 2016</ref>
We will use Resque as the enqueuing backend. You need to install Redis before you can run Resque. You can get Redis using Homebrew:
$ brew install redis
Or you can download, extract and compile Redis using:<ref name= "Download_Redis">Redis “Download Redis” 2016</ref>
$ wget http://download.redis.io/releases/redis-3.0.7.tar.gz $ tar xzf redis-3.0.7.tar.gz $ cd redis-3.0.7 $ make
Redis does not officially support Microsoft Windows[14] platform, but you can find Redis on Windows by Microsoft Open Tech group.
Next get Resque be installed. To use resque with Active Job, we also need resque-scheduler.
Add the following into Gemfile, and run “$ bundle install”.
gem 'resque' gem 'resque-scheduler'
After the installation, create a Resque configuration file resque.rb in config/initializers/
#config/initializers/resque.rb Resque.redis = Redis.new(host: 'localhost', post: 6379) Resque.after_fork = Proc.new { ActiveRecord::Base.establish_connection }
As the Resque tasks and Resque Scheduler rake tasks are needed in this example, we need to create a resque.rake file in lib/tasks/:
#lib/tasks/resque.rake require 'resque/tasks' require 'resque/scheduler/tasks' namespace :resque do task setup: :environment do ENV['TERM_CHILD'] ||= '1' ENV['QUEUE'] ||= '*' require 'resque' require 'resque-scheduler' end end
3. Creating a Mailer<ref name= "Action-Mailer-Basics">Rails Guides “Action Mailer Basics” 2014</ref>
By doing the things above, we already have our Rails project and the Resque for queuing get set. Next we will create a Mailer.
$ bin/rails generate mailer user_mailer
Create a method and a view for sending the email.
In app/mailer/user_mailer.rb:
#app/mailers/user_mailer.rb class UserMailer < ActionMailer::Base default from: 'from@example.com' def test_email(email) mail( to: email, subject: 'We are testing Active Job!' ) end end
In app/views/user_mailer/test_email.text
#app/views/user_mailer/test_email.text Hey, we are testing Active Job!
4. Active Job<ref name= "Active-Job-Basics">Rails Guides “Active Job Basics” 2014</ref>
Create a configuration file active_job.rb in config/initializers/ , to set Resque as queue_adapter.
#config/initializers/active_job.rb ActiveJob::Base.queue_adapter = :resque
Create a Job with generator.
$ bin/rails generate job test_email
In the generated file app/jobs/test_email_job.rb, define the perform method and set it’s queue_as.
#app/jobs/test_email_job.rb class TestEmailJob < ActiveJob::Base queue_as :email def perform(email) UserMailer.test_email(email).deliver_now end end
Now we can use a UserController to put the job into the queue for later execution. Here we set the email to be sent one minute later for test.
$ bin/rails generate job test_email
#app/controllers/users_controller.rb class UsersController < ApplicationController def new @user = User.new end def create @user = User.create(user_params) TestEmailJob.new(@user.email).deliver_later!(wait: 1.minute) # redirect somewhere end end
Routes and view need to set to make it work.
#config/routes.rb Rails.application.routes.draw do resources :users, only: [:new, :create] end
#app/views/users/new.html.erb <%= form_for @user do |f| %> <%= f.email_field :email %> <%= f.submit %> <% end %>
5. Try execution
Before we try the Mailer, we need MailCatcher for the test. Add following into Gemfile and “$ bundle install”
gem 'mailcatcher'
To set the environment, add following things into config/environments/development.rb.
#config/environments/development.rb Rails.application.configure do ... config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: "localhost", port: 1025 } end
Run it!
# Start mailcatcher $ mailcatcher # Start redis-server $ redis-server # Start resque:work $ bundle exec rake resque:work # Start resque:scheduler $ rake environment resque:scheduler # Finally start rails server $ rails server
Open the browser, visit localhost:3000/users/new and sign up as a new user. One minute later you can see following in Resque scheduler:
resque-scheduler: [INFO] 2016-02-05T22:53:15+09:00: Processing Delayed Items
And in MailCatcher:
==> SMTP: Received message from '<from@example.com>' (315 bytes)
References
<references/>