Background jobs với resque gem, cách hoạt động của các background job framework

I, giới thiệu

Resque là một trong ba background processing frameworks được sử dụng phổ biến nhất hiện nay (delay_job, sidekiq, resque). Resque có nhiều ưu điểm trong đó có ưu điểm về tốc độ và sự phân tách tốt giữa background và foreground code trong ứng dụng. Bài viết này tôi sẽ giới thiệu đến các bạn cách sử dụng resque cũng như là cách thực hoạt động bên trong của resque như thế nào.

II, Cài đặt và tạo các background jobs với resque

1, Cài đặt

  • Giống như sidekiq, resque hoạt động dựa trên redis. Redis là một kỹ thuật lưu dữ liệu mới xuất hiện từ năm 2009 với ưu điểm là tốc độ và khả năng lưu được những cấu trúc dữ liệu phức tạp. Để hiểu hơn về redis các bạn có thể đọc thêm tại đây (http://redis.io/). Do đó, để có một app resque hoạt động trước tiên bạn cần phải cài đặt redis.

Cài redis-server:

sudo apt-get install redis-server

Khởi động redis-server:

redis-server

redis-server2.png

Nếu trên terminal xuất hiện thông báo như hình trên có nghĩa là bạn đã cài đặt redis server thành công.

  • Tiếp theo tiến hành cài đặt resque

Trong Gemfile thêm dòng:

 gem "resque"

Sau đó gõ lệnh:

           bundle install

Cài đặt rake tasks

Resque có cung cấp một số rake tasks cho phép chạy workers trong ứng dụng. Để có thể sử dụng các rake tasks này thêm file lib/tasks/resque.rake và định nghĩa rake tasks liên quan của resque

                   require 'resque/tasks'

Để các workers có thể access tới models bạn cần thêm dòng sau tới file resque.rake vừa tạo

                  task “resque:setup” => :environment

Như vậy đến đây chúng ta đã thực hiện xong phần cài đặt và cấu hình cho resque

2. Tạo background jobs

Tạo thư mục app/workers
Thư mục này sẽ là nơi chứa các jobs của resque

Trong phần này tôi sẽ viết một job đơn giản tên là print với chức năng cơ bản là in ra terminal dòng có dạng như: “Hello everybody. My input: ….”

Tạo file print.rb trong thư mục app/workers/ có nội dung như sau:

class Print
  @queue = :print

  class << self
    def perform input
      puts "hello everybody. My input is #{input}"
    end
  end
end

Đầu tiên bạn định nghĩa một biến instance class có giá trị là tên của queue mà Print jobs được đưa vào. Sau đó định nghĩa class method perform đây chính là method được gọi để xử lý job trong queue. Đến đây bạn đã định nghĩa xong một jobs. Bước tiếp theo là đưa job vào queue và dùng worker để xử lý các jobs

Ở phần 1 chúng ta đã cấu hình cho các rake tasks của resque, đây là lúc sử dụng chúng. Hãy gõ lệnh sau để bật worker cho Print jobs

                rake resque:work QUEUE=print

Lưu ý print ở đây chính là giá trị của biến instance class @queue

woker_st2.png

worker đã được start, lúc này nó sẽ kiểm tra xem trong print queue có job nào không nếu có sẽ thực hiện lần lượt cho đến hết. Nếu không có thì mặc định 5s sau nó lại kiểm tra tiếp. Quy trình cứ tiếp tục cho đến khi nào worker bị stop. Hiện tại trong print queue chưa có job nào nên worker không xử lý gì cả. Vậy làm thế nào để tạo ra các jobs, resque có cung cấp class method “enqueue” để tạo ra các jobs trong queue. Bật rails console và thực hiện cách lệnh sau:

enqueue2.png

Khi đó 1 job đã được tạo và đưa vào queue tương ứng là “print”. Worker phát hiện trong queue có một job và nó sẽ thực hiện xử lý job đó. print_out2.png

3. Giao diện quản lý resque

Resque có cung cấp một giao diện Sinatra-base, từ đây có thể xem trang thái hiện tại của các queue

Để sử dụng được giao diện này. Thêm dòng require 'resque/server' ở vị trí đầu của route.rb file. Sau đó định nghĩa path cho resque như sau trong route.rb file

     mount Resque::Server.new, :at => "/resque"

Sau đó access url sau để xem kết quả: localhost:3000/resque

resque_interface2.png

III, Cách thức làm việc bên trong của resque

Như đã biết, resque cũng như sidekiq làm việc dựa trên redis. Mọi xử lý bên trong có thể minh họa bằng ví dụ đơn giản dưới đây:

  • Đầu tiên tạo một biến global là instance của Redis Tạo file config/initializers/redis.rb và thêm dòng sau:
                $redis = Redis.new
  • Cấu hình để rails tự động load các file .rb trong thư mục /lib/
    Trong file application.rb thêm dòng sau:
            config.autoload_paths += %W(#{config.root}/lib)
  • Tạo file lib/bg/queue_job.rb Trong file này chúng ta sẽ tiến hành tạo Bg::QueueJob class sau đó thêm các methods cần thiết để mô phỏng một hệ thống tạo các background job, queuing jobs và processing jobs:
  • Tạo các methods để enqueueing, xóa hết jobs trong queue, xóa queue, xem số lượng jobs trong queue, xóa một job trong queue, xem job tiếp theo trong queue

class Bg::QueueJob
  def enqueue queue_name, data
    $redis.sadd "queues", queue_name
    $redis.rpush "queue:#{queue_name}", data.to_json
  end

  def clear queue_name
    $redis.del "queue:#{queue_name}"
  end

  def destroy queue_name
    self.clear queue_name
    $redis.srem "queues", "queue:#{queue_name}"
  end

  def length queue_name
    $redis.llen "queue:#{queue_name}"
  end

  def remove_job queue_name, data
    $redis.lrem queue_name, 0, data.to_json
  end

  def peek queue_name
    $redis.lrange "queue:#{queue_name}", 0, 0
  end

end

Ở đây, mỗi queue được đại diện bởi một list trong redis, mỗi job trong queue là một phần tử trong list.

implement_job2.png

  • Tiếp theo thêm các methods sau vào QueueJob class để xử lý các jobs có trong queues
def dequeue queues
    $redis.blpop(*queues.map{|q| "queue:#{q}"}.push(60))
  end

  def work queues
    while true do
      job = self.dequeue queues
      process_job job unless job.nil?
    end
  end

  private
  def process_job job
    puts "#{job.inspect} \n"
  end

Phương thức dequeue dùng để lấy ra các jobs trong các queues, nếu trong các queues đó không có jobs nào thì tiếp tục chờ trong 60s, trong khoảng thời gian chờ này nếu có job mới được đưa vào queue thì trả về job nếu vẫn không có job nào được đưa vào thì hết 60s sẽ trả về nil

Phương thức work có cách thức hoạt động giống worker trong resque, nó tạo một vòng lặp vô hạn nghĩa là work luôn luôn làm việc và cứ 60s lại check xem có job nào được đưa vào queue không nếu có thì xử lý job với phương thức process_job

Phương thức process_job đơn giản chỉ để in ra dữ liệu của job được đưa vào queue

first_worker2.png

worker ban đầu xử lý các jobs đang có trong các queue sau đó chờ các jobs khác được đưa vào queue để xử lý

add_job2.png

Thêm một job vào queue

last_worker2.png

Kết quả là job được xử lý bởi worker đang được bật.

IV. Kết luận

Bài viết trình bày cơ bản cách sử dụng resque để tạo các background jobs. Tiếp theo bài viết có đề cập đến cách thức các framework background job hoạt động dựa trên redis thông qua một ví dụ đơn giản. Mong rằng bài viết này sẽ giúp bạn có cái nhìn sau hơn về resque cũng như các background framework khác. Cám ơn bạn đã quan tâm đến bài viết. Hẹn gặp lại trong các bài viết tiếp theo.


_**Tài liệu tham khảo**_
1. https://github.com/resque/resque
2. http://www.sitepoint.com/comparing-background-processing-libraries-resque/
3. Redis Cookbook by Tiago Macedo and Fred Oliveira (O’Reilly). Copyright 2011 Tiago Macedo and Fred Oliveira, 978-1-449-30504-8, (page 38 - 42)

All Rights Reserved