はじめに
個人開発にて、Sidekiq
、Redis
、ActiveJob
を組み合わせたLINE通知機能を実装しました。
備忘録として、実装の流れをまとめていきたいと思います!
▼個人開発についてまとめた記事
私はプログラミング学習中で、初学者です。
内容に誤りがある場合がございます。
もし間違いがあればご指摘いただけますと幸いです。
環境
- Ruby 3.2.2
- Ruby on Rails 7.0.8
- Render
事前準備
- Renderアカウントの作成
- LINE Developersコンソールでチャネルを作成し、チャネルアクセストークンとチャネルシークレットを発行
手順
(1)環境の準備
-
Redis
のセットアップ -
Sidekiq
のインストール -
ActiveJob
の設定
(2)LINE通知機能の実装
-
LINE Messaging API
の設定 - LINE通知サービスの作成
(3)ActiveJobを使ったジョブの定義
ジョブクラスの作成
(4)Sidekiq-Cronでの定期実行設定
-
Sidekiq-Cron
の追加 - 定期実行のスケジュール設定
-
Sidekiq
の設定
(5)動作確認、デプロイ
- 動作確認
- 手動での動作確認
- デプロイ
長くなりますが、1つずつ書いていきます!
(1)環境の準備
1. Redisのセットアップ
①Renderのダッシュボードにログインし、「New +」ボタンをクリックして、ドロップダウンメニューから「Redis」を選択します。
②ドキュメントに沿って、Redisサービスの名前・地域・プランなどを設定します。
③「Create Redis」ボタンをクリックして、Redisサービスが作成されたら、Renderのダッシュボードでそのサービスを開き、Connectionsの項目の「Internal Redis URL」を控えておきます。このアドレスは、他のRender内で動作するアプリケーションからRedisにアクセスするために使用します。
④「Internal Redis URL」を環境変数に設定して、アプリケーションからRedisに接続できるようにします。
REDIS_URL = XXXXXXXXXX
そもそもRedisとは?
キーバリューストア型のインメモリデータベース(データをキーと値のペアとしてメモリ上に保存し、高速にアクセスできるよう設計されたデータベースシステムのこと)。高速なデータ書き込み・読み出しが可能で、データ構造の保存に対応している。Sidekiqでは、ジョブのキュー(待ち行列)、スケジュール、実行状態などを管理するためにRedisが使用される。
2. Sidekiqのインストール
Gemfile
にgem gem 'sidekiq'
を追記して、bundle install
を実行します。
gem 'sidekiq'
そもそもSidekiqとは?
Rubyのバックグラウンド処理を行うためのライブラリ。マルチスレッド(コンピュータのプロセス内で複数のスレッド[実行の流れ]を同時に動作させる技術)を活用しており、Railsアプリケーションなどで非同期タスクを効率的に実行できる。Redisをジョブストア(バックグラウンドで実行するジョブの情報を一時的に保存しておく場所)として使用し、ジョブのキューイング(タスクやジョブを待ち行列[キュー]に入れ、先入れ先出しの原則に従って順番に処理すること)、実行、失敗時の再試行などを管理する。
3. ActiveJobの設定
Rails
でActiveJob
のバックエンドにSidekiq
を使用するよう設定します。
config.active_job.queue_adapter = :sidekiq
# この設定により、ActiveJobがジョブをキューに入れる際にSidekiqを使用するようになる
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
# RailsのキャッシュストアとしてRedisを使用する設定を追加
# RedisサーバーのURLを環境変数から取得
そもそもActiveJobとは?
ジョブを宣言し、それによってバックエンドでさまざまな方法によるキュー操作を実行するためのフレームワーク。
キャッシュストア
アプリケーションで使用される一時的なデータや計算結果を高速にアクセス可能な形で保存しておくためのストレージのこと。キャッシュストアは、データベースへのクエリ結果、計算結果、ウェブページのコンテンツ、APIのレスポンスなど、再利用可能な情報をメモリ内やディスク上に一時的に保存することで、アプリケーションのパフォーマンスを向上させる目的で使用される。
※開発環境やテスト環境での設定を行いたい場合は、config/environments/development.rb
やconfig/environments/test.rb
に同様の設定を追加します。
(2)LINE通知機能の実装
1. LINE Messaging APIの設定
チャネルアクセストークンとチャネルシークレットを発行する手順は、事前準備の欄に書いたため割愛させていただきます。
発行したチャネルアクセストークンとチャネルシークレットを環境変数に設定します。
LINE_CHANNEL_TOKEN = XXXXXXXXXX
LINE_CHANNEL_SECRET = XXXXXXXXXX
2. LINE通知サービスの作成
LINE通知を送信するサービスクラスを作成します。このクラスは、LINE Messaging API
へのHTTPリクエストを行い、メッセージを送信します。
require 'net/http'
require 'uri'
require 'json'
class LineNotifyService
def self.send_message(uid, message)
uri = URI.parse('https://api.line.me/v2/bot/message/push')
# LINEのメッセージ送信APIのエンドポイントURLを解析してURIオブジェクトを生成
request = Net::HTTP::Post.new(uri)
# 上で解析したURIを使ってPOSTリクエストのオブジェクトを生成
request.content_type = 'application/json'
# リクエストのコンテンツタイプをJSONに設定
request['Authorization'] = "Bearer #{ENV['LINE_CHANNEL_TOKEN']}"
# 環境変数から取得したLINEチャネルトークンを使用して、認証ヘッダーを設定
request.body = JSON.dump({
to: uid,
messages: [{ type: 'text', text: message }]
})
# リクエストボディに、送信先のユーザー識別子と、送信するメッセージの内容をJSON形式で設定
req_options = { use_ssl: uri.scheme == 'https', read_timeout: 10, open_timeout: 5 }
# リクエストオプションを設定
# HTTPSを使用し、読み取りタイムアウトとオープンタイムアウトの値を指定
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
http.request(request)
end
# 上記で設定したオプションを用いてHTTPリクエストを送信し、レスポンスを受け取る
handle_response(response)
end
def self.handle_response(response)
case response
when Net::HTTPSuccess
Rails.logger.info 'LINE通知 送信成功'
when Net::HTTPUnauthorized
Rails.logger.error 'LINE通知 送信失敗: 認証エラー'
when Net::HTTPNotFound
Rails.logger.error 'LINE通知 送信失敗: リソースが見つかりません'
else
Rails.logger.error "LINE通知 送信失敗: #{response.code} #{response.message}"
Rails.logger.error "レスポンスボディ: #{response.body}"
end
end
end
読み取りタイムアウト(Read Timeout)
リクエストを送信した後、サーバーからのレスポンスデータを読み込み始めるまでの時間。この時間を超えると、クライアントはタイムアウトエラー(例:Net::ReadTimeout)を発生させ、処理を中断する。サーバーが過負荷で応答が遅い場合や、ネットワークの遅延が発生している場合に重要になる。
オープンタイムアウト(Open Timeout)
リクエストを開始してからサーバーへの接続が確立されるまでの最大待ち時間を指す(=TCPコネクションが確立するまでの時間)。この時間内に接続が確立されない場合(例:サーバーがダウンしている、ネットワーク障害が発生しているなど)、クライアントはタイムアウトエラー(例:Net::OpenTimeout)を発生させる。
TCPコネクション
Transmission Control Protocol(TCP)を使用してネットワーク上の2つのデバイス(例:クライアントとサーバー)間で確立される信頼性の高い通信接続のこと。
(3)ActiveJobを使ったジョブの定義
ジョブクラスを作成します。例として、ユーザーに対して定期的なリマインダーを送信する場合のジョブを定義します。
class ReminderNotificationJob < ApplicationJob
queue_as :default
def perform
User.find_each do |user|
# 全ユーザーを対象にリマインダー通知を送信する
next unless user.receive_reminder_notifications
# ユーザーが通知を受け取る設定になっているかチェック
reminder_message = generate_reminder_message(user)
# 通知メッセージをカスタマイズ
LineNotifyService.send_message(user.uid, reminder_message) if user.uid.present?
# LINE通知サービスを使用してリマインダーを送信
end
end
private
def generate_reminder_message(user)
"こんにちは、#{user.name}さん。今日のタスクを忘れずにチェックしてくださいね!"
end
end
(4)Sidekiq-Cronでの定期実行設定
1. Sidekiq-Cronの追加
Gemfile
にgem 'sidekiq-cron'
を追記して、bundle install
を実行します。
gem 'sidekiq-cron'
2. 定期実行のスケジュール設定
config/schedule.yml
などの設定ファイルを作成し、定期的に実行したいジョブとそのスケジュールを定義します。
reminder_notification_job:
cron: "0 10 * * *"
class: "ReminderNotificationJob"
スケジュールの指定方法は、左から順に
- 分(0から59)
- 時(0から23)
- 日(1から31)
- 月(1から12)
- 曜日(0(日曜日)から6(土曜日))
アスタリスクは「任意の値」という意味で使われ、具体的な数値を指定しない場合に使用
3. Sidekiqの設定
Sidekiq
の初期化ファイル(例:config/initializers/sidekiq.rb
)にSidekiq-Cron
のジョブを読み込む設定を追加します。
require 'sidekiq'
require 'sidekiq-cron'
Sidekiq.configure_server do |config|
# Sidekiqサーバーの設定を開始 ブロック内の設定は、ジョブを実行するサーバー側で適用
schedule_file = 'config/schedule.yml'
# 定期的に実行するジョブのスケジュールを記述したYAMLファイルのパスを指定
config.redis = {
url: ENV['REDIS_URL'],
# Redisへの接続設定 ENV['REDIS_URL']は環境変数からRedisサーバーのURLを取得
connect_timeout: 5,
read_timeout: 5,
write_timeout: 5
# 接続、読み取り、書き込みそれぞれに5秒のタイムアウトを設定
}
Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) if File.exist?(schedule_file)
# config/schedule.ymlファイルが存在する場合、そのファイルを読み込み、
# YAMLファイルに記述されたスケジュールに従って定期的なジョブを設定
end
Sidekiq.configure_client do |config|
# Sidekiqクライアントの設定を開始 この設定は、ジョブをキューに投入する側で適用
config.redis = {
url: ENV['REDIS_URL'],
# Redisへの接続設定 ENV['REDIS_URL']は環境変数からRedisサーバーのURLを取得
connect_timeout: 5,
read_timeout: 5,
write_timeout: 5
# 接続、読み込み、書き込みそれぞれに5秒のタイムアウトを設定
}
end
(5)動作確認、デプロイ
1. Sidekiqのダッシュボードでの動作確認
定期的にLINE通知が送られるかどうかを確認します。
Sidekiq
のダッシュボードを使ってジョブが正しくエンキューされているかを確認できます。
①ルーティングの設定
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq'
②Sidekiq
のダッシュボードでジョブの状況を確認
/sidekiq
でアクセスした際にSidekiq
のダッシュボードが表示され、エンキューされているジョブの状況を確認できます。
2. 手動での動作確認
Rails
コンソールを使用して、手動でジョブの動作を確認することもできます。
コンソールを起動して、実行したいジョブのクラス名とperform_later
メソッドやperform_now
メソッドを使ってジョブを実行します。
-
perform_later
メソッド ジョブを非同期で実行する(ジョブシステムのキューにジョブをエンキューする)場合に使用 -
perform_now
メソッド すぐにジョブを実行したい場合に使用
ReminderNotificationJob.perform_now
3. デプロイ
動作確認ができたら、各ホスティングサービスの設定方法に則り、Redis
サーバーへの接続情報(例:REDIS_URL
)などを環境変数に設定し、アプリケーションを本番環境にデプロイします。
エンキュー(Enqueue)
キューに新しい要素を追加する操作。キューの末尾(最後)に要素が追加され、キュー内の要素数が一つ増える。バックグラウンドジョブ処理システムにおいて、新しいジョブをキューに追加する行為をエンキューと呼ぶ。これにより、ジョブが後で処理されるための「待ち行列」に入れられる。
デキュー(Dequeue)
キューから要素を取り出す操作。キューの先頭(最初)にある要素が取り出され、キューからその要素が削除される。バックグラウンドジョブ処理システムにおいて、処理が準備できたジョブをキューから取り出して実行することをデキューと呼ぶ。ジョブがデキューされると、そのジョブはキューから削除され、即座にまたはスケジュールに従って処理が開始される。
おわりに
長くなってしまいましたが、文字に起こしてみることで、より理解を深めることができたかなと思います。
まだまだ勉強不足なので、引き続き学習に励んでいきたいと思います!
最後まで読んでいただきありがとうございました。