12/08/2018, 13:02

Tìm hiểu Sidekiq gem

Tìm hiểu Sidekiq gem I. Giới thiệu Sidekiq Sidekiq là framework để thực hiện các công việc chạy ngầm, nó là giải pháp đơn giản và hiệu quả nhất tích hợp với các ứng dụng Rails cần yêu cầu cao về performance. Sidekiq sử dụng các luồng _ thread_ để thực hiện nhiều công việc cùng lúc trong ...

Tìm hiểu Sidekiq gem

I. Giới thiệu Sidekiq

Sidekiq là framework để thực hiện các công việc chạy ngầm, nó là giải pháp đơn giản và hiệu quả nhất tích hợp với các ứng dụng Rails cần yêu cầu cao về performance.

Sidekiq sử dụng các luồng _ thread_ để thực hiện nhiều công việc cùng lúc trong cùng một tiến trình xử lý. Nó không yêu cầu về Rails nhưng có thể tích hợp chặt chẽ với Rails để xử lý chạy ngầm các tiến trình nặng nề một cách đơn giản.

Resque cũng là một nền tảng dùng để chạy ngầm các job. Tuy nhiên, Sidekiq có thể tương thích hoàn toàn với Resque, nó sử dụng cùng định dạng message. Ta có thể sử dụng cả Sidekiq và Resque chạy đồng thời cùng thời điểm và sử dụng máy client chạy Resque để thêm các job vào Redis và được xử lý bởi Sidekiq

Ta có bảng thống kê về tính hiệu quả của Sidekiq

Version Latency Garbage created for 10,000 jobs Time to process 100,000 jobs Throughput
Sidekiq 4.0.0 10ms 151MB 22 sec 4500 jobs/sec
Sidekiq 3.5.1 22ms 1257MB 125 sec 800 jobs/sec
Resque 1.25.2 420 sec 240 jobs/sec
DelayedJob 4.1.1 465 sec 215 jobs/sec

ta có thể thấy Sidekiq xử lý các job chạy ngầm rất nhanh và hiệu quả

II. Sử dụng Sidekiq

1. Yêu cầu

  • Ruby:

     Ruby 2.2, 2.1 và 2.0
    
     JRuby 9k
    
     Ruby 1.9 không được hỗ trợ
    
  • Rails:

     Các phiên bản Rails từ 3.2 trở đi
    
  • Redis:

     Redis từ 2.8 trở lên,
    
     Bản 3.0.3 là tối ưu cho việc xử lý hàng nghìn thread
    

2. Cài đặt

Ta thêm Sidekiq vào trong Gemfile

gem 'sidekiq'

chạy bundle exec sidekiq để cài đặt

3. Cách dùng

Thêm một Worker trong app/workers để xử lý job không đồng bộ

class HardWorker
  include Sidekiq::Worker
  def perform(name, count)
    # thực hiện công việc
  end
end

Tạo 1 job:

Có vài cách tạo job để chạy ngầm:

HardWorker.perform_async('bob', 5)

hay

Sidekiq::Client.push('class' => MyWorker, 'args' => ['bob', 5])

Ngoài ra, ta cũng có thể hẹn giờ để 1 job được thực hiện

HardWorker.perform_in(5.minutes, 'bob', 5)

Hoặc tạo job thông qua hàm delay

User.delay.do_some_stuff(current_user.id, 20)

4. Cơ chế xử lý

Tạo Job

Thông qua gọi các hàm tạo Job, sẽ sinh ra 1 Hash tương ứng với job, chuyển đổi Hash thành JSON String và đẩy String đấy vào hàng đợi của Redis.

Các tham số truyền vào của job chỉ đơn thuần là các kiểu dữ liệu JSON đơn giản (numbers, strings, boolean, array, hash)

Các đối tượng phức tạp hơn (như Date, Time, các model Active Record) sẽ không được chuyển đổi một cách thích hợp

Redis

Redis cung cấp nơi chứa dữ liệu cho Sidekiq. Nó giữ tất cả dữ liệu job trong thời gian thực và lịch sử của dữ liệu trong Sidekiq's Web

Server

Mỗi server Sidekiq lấy các job từ hàng đợi trong Redis và thực thi chúng. Giống như các tiến trình của trang web, Sidekiq tải lại framework Rails cho các job và workers có đầy đủ các Rails API bao gồm Active Record để thực hiện. Server sẽ khởi tạo các worker và truyền vào các tham số. Mọi thứ sẽ được thực hiện dựa theo mục đích của job.

III. Một vài cách tối ưu Sidekiq

1. Truyền các tham số đơn giản

Ví dụ khi tạo 1 job

quote = Quote.find(quote_id)
SomeWorker.perform_async(quote)

Để thực hiện chạy ngầm, Sidekiq sẽ phải chuyển đổi đối tượng Quote cho Redis. Chuyện gì sẽ xảy ra nếu hàng đợi trong Redis được backup và đối tượng quote thay đổi cùng lúc, Sidekiq sẽ không lưu trạng thái của nó. Vì vậy nên truyền các tham số đơn giản

SomeWorker.perform_async(quote_id)

Các tham số truyền vào perform_async phải là các kiểu dữ liệu JSON đơn giản: string, integer, boolean, null, array và hash. API của Sidekiq client sẽ sử dụng JSON.dump để gửi dữ liệu đến Redis. Sidekiq server đẩy dữ liệu JSON từ Redis và sử dụng JSON.load để chuyển dử liệu về kiểu Ruby truyền vào hàm thực thi.

Không truyền kiểu symbol hay các đối tượng Ruby phức tạp (như Date hay Time), nó sẽ khiến quá trình dump/load thực hiện thiếu chính xác.

2. Tạo job idempotent and transactional

Idempotency nghĩa là job có thể thực hiện nhiều lần. Ví dụ, cố gắng thực thi khi gặp lỗi, job có thể thực hiện một nửa, bắn ra lỗi, sau đó thực hiện lại, lặp đi lặp lại tới khi thực hiện thành công. Điều này có thể thấy ở các job thực hiện giao dịch cho thẻ tín dụng rồi gửi email đến người dùng, có cho phép họ biết được tiền của họ đã bị hoàn lại.

def perform(card_charge_id)
  charge = CardCharge.find(card_charge_id)
  charge.void_transaction
  Emailer.charge_refunded(charge).deliver
end

Điều gì sẽ xảy ra khi email bị lỗi, khi đó hàm transaction phải bổ sung trường hợp khi tiền bị gửi trả lại. Ta có thể sử dụng transaction của database để đảm bảo dữ liệu thay đổi được khôi phục lại nếu có một lỗi hay một đoạn code trả về lỗi nào đó. Tuy nhiên Sidekiq chỉ thực thi job đúng một lần.

3. Đảm bảo liên tục

Sidekiq được thiết kế để xử lý song song, để có thể chạy nhiều job cùng một lúc.

Ta có thể sử dụng một công cụ kết nối giới hạn tổng số kết nối đến một server bị giới hạn tài nguyên khi các tiến trình Sidekiq bị tắc nghẽn.

Sidekiq không cung cấp các đặc tính để xử lý cho job thiếu tính liên tục.

IV. Tổng kết

Sidekiq là một công cụ rất hữu ích để cải thiện performance cho Rails, giúp giảm bớt tác vụ mà Rails server phải xử lý, tăng tốc hệ thống.

Ngoài ra, Sidekiq cũng có các phiên bản ứng với các mục đích khác nhau như Sidekiq Pro, Sidekiq Enterprise, cung cấp nhiều đặc tính tốt hơn.

Nguồn tài liệu tham khảo

  • gem Sidekiq
  • Railscasts.com
0