Background Jobs Framework nào phù hợp với ứng dụng Ruby on Rails của bạn?

Nếu bạn dạo quanh hệ sinh thái Ruby / Rails một chút, bạn đã có thể nghe thuật ngữ "background job" hoặc "offline processing". Nhưng điều đó thực sự có nghĩa gì? Làm thế nào để bạn biết được tác vụ nào phù hợp để chạy "offline processing"? Một khi bạn xác định những tác vụ này, làm thế nào để chọn các Background Jobs Framework phù hợp cho ứng dụng của bạn?

Trong bài này tôi sẽ trả lời tất cả các câu hỏi trên, cũng như so sánh sự khác biệt của một số Background Jobs Framework phổ biến nhất hiện nay.

Background Jobs là gì?

Hãy bắt đầu với một số thuật ngữ. Một background hoặc asynchronous jobs là một tiến trình xử lý bên ngoài luồng request/response thông thường và là một thành phần của bất kỳ web framework hiện đại nào. Thông thường, các ứng dụng web nhận được request từ bên ngoài, thực hiện một số xử lý (chẳng hạn như truy vấn một cơ sở dữ liệu) và sau đó trả về một response trong vòng một vài mili giây. Đây là mô hình bình thường mà chúng ta đều quen thuộc để phát triển các ứng dụng cho các trang web và được gọi là truyền thông đồng bộ (synchronous communication).

Asynchronous tasks theo một cách khác, nó có thể được bắt đầu từ một request web bình thường, nhưng đòi hỏi thời gian lâu hơn để hoàn thành so với request bình thường. Bởi vì những request không thể được xử lý ngay lập tức và trả lại một response nên được gọi là không đồng bộ. Để không làm gián đoạn công việc đồng bộ bình thường của một ứng dụng, nhiệm vụ không đồng bộ thường được xử lý trên một luồng riêng biệt hoặc được sinh ra như một quá trình riêng biệt hoàn toàn.

Khi nào tôi cần sử dụng một Background Jobs?

Có thể dễ dàng giải thích những gì một background (asynchronous) job bằng cách mô tả một trường hợp sử dụng phổ biến mà nhiều ứng dụng phải thực hiện. Hãy tưởng tượng bạn đang viết một hệ thống quản lý thông tin khách hàng (CRM) trong Rails. Các chi tiết thực tế của ứng dụng này là không quan trọng, nhưng hãy tưởng tượng rằng một tính năng chính của ứng dụng này là nó phải gửi email cho khách hàng khi một lệnh được thực hiện hay khi một người hỗ trợ đã thực hiện một bản cập nhật để yêu cầu hỗ trợ.

Bạn có thể thực hiện tính năng này 'inline', có nghĩa là ngay sau khi một hành động gây ra một email, hành động đó không thể hoàn thành (một response không thể được trả lại cho người sử dụng) cho đến khi email đã được gửi đi. Cách này có thể vẫn làm việc, tuy nhiên hãy xem qua một số trường hợp mà cách thức này bị sụp đổ:

  1. Điều gì xảy ra nếu máy chủ email của bạn bị lỗi và email không thể gửi đi?
  2. Điều gì xảy ra nếu dịch vụ email của người dùng lỗi và không thể nhận email tại thời điểm đó?
  3. Điều gì xảy ra nếu hộp thư của khách hàng đã bị 'full' và sẽ không chấp nhận bất kỳ email nào?

Tất cả những trường hợp phát sinh lỗi là cái gì mà một sản phẩm ứng dụng phải được thiết kế để xử lý. Điều này cũng có nghĩa là ứng dụng của bạn sẽ bị đứng thay vì trả lại một response ngay lập tức cho người dùng. Đây không phải là điều lý tưởng cho người dùng không thích chờ đợi một phút hoặc thậm chí vài giây cho một ứng dụng không phản hồi. Một request quá lâu cũng có thể gây ra vấn đề năng lực cho các máy chủ ứng dụng, trì hoãn thời gian đáp ứng cho các yêu cầu từ người dùng khác mà dẫn đến quá tải.

Background jobs là một cách phổ biến để giảm bớt vấn đề này. Hãy tưởng tượng lại cùng một quy trình làm việc này nhưng với việc sử dụng một background job. Khi một email được kích hoạt, ứng dụng của bạn tạo ra một lịch chạy hoặc hàng đợi cho một EmailSendJob có chứa tất cả các thông tin liên quan cần thiết như người nhận, nội dung, chủ đề... Ứng dụng của bạn có thể tạo ra một hàng đợi các công việc trước khi chúng thực sự được xử lý. Điều này được cho phép bởi vì việc xử lý các công việc này xảy ra trên một luồng chạy nền và không ảnh hưởng đến công việc đồng bộ bình thường của ứng dụng.

Một lợi ích khác của việc sử dụng background job là chúng có thể được thực hiện lặp lại nhiều lần. Trong kịch bản thất bại trên, email có thể không được cung cấp vì một 'tắc nghẽn' đâu đó trong quá trình gửi nhận. Tại một thời điểm, máy chủ email của bạn sẽ hoạt động trở lại, hoặc khách hàng sẽ dọn dẹp hộp thư đến của họ và email sẽ có thể được gửi thành công.

Tôi nên dùng những Framework nào?

Hy vọng rằng bây giờ bạn đã hiểu làm thế nào bạn có thể sử dụng một background job framework trong ứng dụng của bạn. Nhưng mà bạn nên chọn nền tảng nào? Cũng giống như việc không chỉ có một ngôn ngữ lập trình duy nhất nào có thể giải quyết vấn đề của tất cả mọi người, nên không chỉ có một framework duy nhất phù hợp. Chúng ta hãy xem xét xem framework nào phù hợp nhất với ứng dụng của bạn.

Như đã nói ở trên, đây là một số framework mà tôi tin rằng đáp ứng được các mục đích chung và đủ độ ổn định để sử dụng trong sản phẩm của bạn.

DELAYED::JOB

Delayed::Job là một Ruby background job framework đã được phát triển bởi các nhân viên tại Shopify, một trang web thương mại điện tử phổ biến. Delayed::Job hoạt động bằng cách duy trì một bảng 'công việc' trong cơ sở dữ liệu để theo dõi một nhiệm vụ và vị trí của nó trong vòng đời của công việc (theo lịch trình, trạng thái, hoàn thành, đã thất bại,...). Delayed::Job tích hợp dễ dàng với Rails và ActiveRecord nếu bạn đang sử dụng một cơ sở dữ liệu quan hệ.

Delayed::Job là rất ổn định. Thực tế là nó giúp Shopify chạy core hệ thống bằng cách xử lý các tác vụ như thay đổi kích thước hình ảnh, gửi thư thông báo và cập nhật chỉ số tìm kiếm... Trong trường hợp hệ thống bị lỗi, Delayed::Job có thể được khởi động lại miễn là công việc đã ghi thành công vào cơ sở dữ liệu. Đây có thể là một lợi ích rất lớn khi làm việc trong một hệ thống phân tán.

Một trong những nhược điểm của các framework sử dụng cơ sở dữ liệu là khi thêm hoặc thực thi phụ thuộc vào cơ sở dữ liệu. Nếu bảng 'công việc' của bạn đặt trong cơ sở dữ liệu sử dụng chung với ứng dụng của bạn, điều này có thể đặt cơ sở dữ liệu chịu tải cao không cần thiết, nhất là trong trường hợp bạn có một hàng đợi tồn đọng nhiều jobs chưa xử lý. Cơ sở dữ liệu sẽ dễ trở thành nút cổ chai của ứng dụng.

SIDEKIQ

Sidekiq có lẽ là một trong những Ruby background job framework nổi tiếng nhất chủ yếu là vì độ tin cậy và hiệu quả của nó. Sidekiq được hỗ trợ bởi Redis - bộ nhớ lưu trữ dữ liệu rất phổ biến mà rất nhiều các ứng dụng web chúng ta sử dụng hàng ngày. Redis, giống như một cơ sở dữ liệu, chạy trong một tiến trình riêng biệt hoặc nhiều hơn thường trên một máy chủ riêng biệt so với các ứng dụng của bạn. Các lợi ích chính mà Redis có hơn một cơ sở dữ liệu truyền thống là tốc độ của nó. Bởi vì Redis là (gần như) hoàn toàn trong bộ nhớ, tạo dữ liệu và phục hồi là cực kỳ nhanh chóng.

Sidekiq cải thiện tốc độ của nó bằng cách sử dụng Redis như là nơi lưu trữ các jobs của mình. Theo một tài liệu của Sidekiq, nó có thể xử lý lên đến 100.000 jobs trong 22 giây, so với 465 giây mà Delayed::Job thực hiện. Một lợi ích khác của Sidekiq là nó đi kèm với một bảng điều khiển xây dựng sẵn cho phép bạn xem tất cả các hàng đợi công việc của bạn và các tiến trình xử lý của nó. Điều này có thể rất hữu ích khi gỡ lỗi một job thất bại hoặc bị mắc kẹt, hoặc chỉ khi bạn muốn có cái nhìn sâu sắc hơn vào công việc mà ứng dụng của bạn là thực sự làm. Trong khi Sidekiq là mã nguồn mở, nó có hai phiên bản trả tiền khác, Sidekiq Pro và Sidekiq dành cho doanh nghiệp đi kèm với các tính năng bổ sung như hạn chế tốc độ, việc theo lịch trình định kỳ, và các công việc độc đáo cũng như email ưu tiên và hỗ trợ chatting.

Những lợi ích của Redis không đến mà không có nhược điểm của nó. Bởi vì Redis lưu trữ trong bộ nhớ, nó có thể dẫn đến mất dữ liệu nếu Redis của bạn bị treo khi thêm vào hoặc xoá đi một job. Ngoài ra, nếu ứng dụng của bạn không dùng Redis, sử dụng Sidekiq như background job đòi hỏi thêm các yêu cầu về cơ sở hạ tầng.

SUCKERPUNCH

SuckerPunch có cách tiếp cận hoàn toàn khác khi nói đến quản lý công việc bằng cách hoạt động trong quá trình tương tự như ứng dụng của bạn. SuckerPunch đạt được điều này bằng cách xây dựng trên đầu trang của đồng-ruby , một khuôn khổ Ruby trợ bằng văn bản đề an toàn mã Ruby. Kể từ SuckerPunch hoạt động trong quá trình, nó cũng lưu trữ trạng thái công việc hoàn toàn trong bộ nhớ. Điều này có nghĩa rằng không có phụ thuộc bổ sung khi thêm SuckerPunch để ứng dụng của bạn.

Bởi vì SuckerPunch chạy 'trong' ứng dụng hiện tại của bạn, nó có thể là lý tưởng khi chạy trong một môi trường mà xử lý bổ sung đi kèm và chi phí cao như Heroku. SuckerPunch không đòi hỏi một nhiệm vụ Rake riêng biệt hoặc quá trình Ruby như Delayed :: Job và Sidekiq. Ngoài ra, vì tất cả sự kiên trì trạng thái trong bộ nhớ, SuckerPunch có thể hoạt động rất nhanh trên các nhiệm vụ nhỏ.

Nó có thể đi mà không nói, nhưng SuckerPunch là ít bền và đàn hồi khi nói đến giao dịch với các lỗi hệ thống. Mô hình kiên trì hoàn toàn trong bộ nhớ SuckerPunch có nghĩa rằng nếu ứng dụng của bạn là ngừng hoặc khởi động lại, bất kỳ công việc trong tiến trình hoặc những người trong hàng đợi công việc cấp phát bị mất. Bang tài liệu SuckerPunch của: "... Sucker Punch thường được khuyến cáo cho các công việc được nhanh chóng và không nhiệm vụ quan trọng (ví dụ như các bản ghi, email, vv.)" .

Những yếu tố cần cân nhắc

Chọn một khuôn khổ công việc nền cho ứng dụng của Ruby của bạn là nhiệm vụ không hề nhỏ. Không có khuôn khổ công việc đúng hay sai, nhưng có những người mà có thể phù hợp với trường hợp sử dụng của bạn tốt hơn so với những người khác. Một số điều cần xem xét khi quyết định theo khung để lựa chọn bao gồm:

Làm thế nào kiên cường làm bạn cần công việc của bạn là để thất bại? Làm thế nào nhiều thêm phụ thuộc được bạn thoải mái thêm vào hệ thống của bạn? Làm thế nào nhiều sự hỗ trợ nào bạn nghĩ rằng bạn có thể yêu cầu trong tương lai? Làm thế nào có liên quan là bạn về việc chia sẻ tài nguyên với các ứng dụng hiện tại của bạn? Làm thế nào ổn định bạn có yêu cầu API được? Bạn có cần 'cao cấp' các tính năng như tốc độ giới hạn, trạm trộn, vv? Dưới đây là một bảng đơn giản so sánh từng trong những khuôn khổ, chúng tôi đã đề cập trong một số lĩnh vực quan trọng:

Delayed::Job Sidekiq SuckerPunch
Persistence Database (ActiveRecord / Mongoid) Redis In Memory
Dependencies Database Redis None
Priority Queues Yes Yes Yes (specify # of workers)
Retriable Jobs Yes Yes No
Scheduling Yes Yes Yes
Realtime Dashboard No Yes No
Support Open Source Open Source/ Paid Open Source
Ruby/Rails Version Ruby 1.9/Rails 3.0+ Ruby 2.0+/ Rails 3.2+ Ruby 2.0+

Như bạn có thể thấy, các khuôn khổ công việc khác nhau đề cập từng có những lợi ích riêng của họ và hạn chế cũng như bộ tính năng của riêng mình mà ứng dụng của bạn có thể yêu cầu. Như với hầu hết mọi thứ, có những sự đánh đổi đó phải được đưa vào tài khoản khi lựa chọn một khuôn khổ công việc nền. Hy vọng bài viết này đã giúp bạn có được một nắm bắt tốt hơn về những gì mỗi Ruby khuôn khổ công việc nền hàng đầu đã cung cấp, và cách họ có thể phù hợp với nhu cầu của bạn.