こんにちは! エンジニアインターンの平八重です。今回取り組んだプロジェクトの中で、指定した日時に自動的に処理を行う非同期処理をSidekiqを用いて実装しました。
その際に使用したライブラリと構築について紹介します。
今回のシステムでは、指定した日時にデータベースに登録されている全てのページ(URL)に対して HTTPリクエストを行うため、非同期処理を定期実行する必要がありました。
Sidekiq は、非同期ジョブ処理のための Ruby 向けのシステムであり、Railsとの親和性が非常に高いことが特徴です。
Rails は、Webアプリケーションの構築に必要な要素を提供するフレームワークであり、Sidekiq は、このフレームワークの機能を拡張して非同期ジョブ処理を実現するためのツールとして位置づけられます。
Rails には、ActiveRecordというオブジェクトリレーショナルマッピング(ORM)ライブラリがあり、データベースとのやり取りを行うことができます。
Sidekiq は、この ActiveRecord をサポートしているため、データベースにアクセスするジョブを実行することができます。
またジョブを定義し、非同期で実行する API のフレームワークであるActiveJobをサポートしています。
その他にも Sidekiq の構築にはRedisというインメモリデータベースを使用します。
Redis を使用してジョブをキューに格納し、非同期に処理するため、Webアプリケーションのレスポンス時間を短縮することができます。
また、ジョブを定期実行するための拡張gemである `sidekiq-scheduler` を使用しました。
今回は Rails + PostgreSQL + Redis + Sidekiq の環境で構築を行ったため、以下に docker-compose.yml ファイルの例を示します。
docker-compose.yml
version: 'version: '3'
services:
db:
image: postgres:13
environment:
POSTGRES_USER: <%= ENV.fetch('POSTGRES_USER') { 'postgres' } %>
POSTGRES_PASSWORD: <%= ENV.fetch('POSTGRES_PASSWORD') { 'postgres' } %>
POSTGRES_DB: <%= ENV.fetch('POSTGRES_DB') { 'myapp_development' } %>
volumes:
- ./tmp/db:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:latest
volumes:
- ./tmp/redis:/data
ports:
- "6379:6379"
web:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
volumes:
- .:/app
- bundle:/bundle
ports:
- "3000:3000"
links:
- db
depends_on:
- db
- redis
- sidekiq
tty: true
stdin_open: true
sidekiq:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/app
- bundle:/bundle
env_file:
- .env
volumes:
bundle:
各サービスには、必要な環境変数、ポートマッピング、ボリューム、依存関係が含まれています。また、上記のコードでは、データベースとRedisのデータを永続化するためにボリュームが定義されています。
これにより、データがコンテナを再起動しても失われなくなります。
gem 'redis', '~> 5.0', '>= 5.0.6'
gem 'sidekiq', '~> 6.5', '>= 6.5.8'
アダプターを Sidekiq にします。
config/application.rb
config.active_job.queue_adapter = :sidekiq
Sidekiq の設定ファイルを追加します。
config/sidekiq.yml
:concurrency: 5
:pidfile: ./tmp/pids/sidekiq.pid
:logfile: ./log/sidekiq.log
production:
:concurrency: 5
:queues:
- default
:daemon: true
次に Sidekiq から接続する Redis の URL を設定します。
config/initializes/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: ENV["REDIS_URL"] }
end
Sidekiq.configure_client do |config|
config.redis = { url: ENV["REDIS_URL"] }
end
以下の手順で、実行するジョブの作成と実行スケジュールの追加、そして管理画面の追加を行います。
ジョブを作成するために、下記のコマンドを実行します。
rails generate job Sample
これにより app/jobs ディレクトリ以下にジョブファイルが生成されます。ジョブファイルには perform メソッドが定義されています。このメソッド内に、ジョブで実行したい処理を記述します。
今回実装した機能では、HTTPリクエスト処理を行うため `Typhoeus` ライブラリを使用しました。
Typhoeus などの並行HTTPリクエストライブラリを使用することで、処理速度を向上させることができます。Typhoeus の利用はこちらの記事を参考にしました。
以下は、ジョブファイルの例です。
app/jobs/sample_job.rb
require 'typhoeus'
class Sample < ApplicationJob
queue_as :default
def
perform(*args)
# HTTPリクエスト処理を行う
end
end
ジョブの実行スケジュールを追加するためには、sidekiq-scheduler というGemを使用します。
gem 'sidekiq-scheduler'
次に config/sidekiq.yml にスケジュールを追記します。以下は、毎朝午前3時に SampleJob を実行するスケジュールの例です。
config/sidekiq.yml
:schedule:
sample:
cron: "0 3 * * * Asia/Tokyo"
class: SampleJob
Sidekiq には、ジョブを管理するためのWebインターフェイスがあります。
config/routes.rb
require "sidekiq/web"
Rails.application.routes.draw do
mount Sidekiq::Web => "/sidekiq" # 追記
end
これにより Sidekiq を立ち上げた状態で `/sidekiq` にアクセスすることで管理画面にアクセスできます。
管理画面ではジョブの状態や履歴の管理、再試行や削除などの操作も簡単に行うことができます。
Sidekiq は、ジョブの実行中にプロセスがクラッシュした場合に、自動的にジョブを再試行するフェイルオーバーを持っています。デフォルトでは、Sidekiq は25回のリトライを行いますが、リトライ回数を変更することもできます。
例えば sidekiq.yml の中で `max_retries` を指定することでデフォルト値を変更することができます。また、ジョブごとにリトライ回数を指定する場合は `sidekiq_options` を使用します。
リトライを無効にする場合は
sidekiq_options retry: 0 #または
sidekiq_options retry: false
のいずれかを設定します。
両者の違いは `retry: 0` の場合はエラー時にジョブがデッド状態になり、画面から再試行が可能であるのに対して `retry: false` の場合は画面から再試行ができなくなるという点です。
リトライに失敗した場合に実行される処理も指定することができます。
リトライに関する設定は、ジョブの実行中に発生するエラーに対処するために重要な設定であるため、今回の実装でも慎重に検討を行いました。
今回は Sidekiq を使用して指定した日時に非同期処理を行う方法を紹介しました。一度構築を終えているため、新たなジョブも簡単に設定できます。スケジューリングされた非同期処理を実装する場合には、この方法を参考にしてみてください。
※2023年3月14日時点