🐟

【LINE通知機能】Docker+Redis+Sidekiq+Active Jobを使い設定時間にチャットが送信される処理を行う(ローカル)

2024/09/14に公開

はじめに

こんにちは! プログラミング初学者のyukimuraです!
今回は私自身の個人開発したアプリで実装した、LINE通知機能について記述しています
間違いなどありましたら、優しくご指摘いただけましたら幸いです

個人開発アプリ
https://scammers-notebook.com/
GithubURL
https://github.com/yuki51b/scammer-s-notebook

実装手順について

  • 今回は、開発者が設定した時間にLINEアカウントを友達追加したユーザーに向けてチャットが送られるという実装を行います。
  • 参考画像(午前7時にメッセージが送信されます)
    Image from Gyazo
  • 実装する順序としては

  1. LINE Developersでチャネルを作成する(公式アカウントの作成)
  2. 一度、ターミナルからの操作でメッセージが送れるようにする
  3. RedisをDockerコンテナに導入する
  4. SidekiqをDockerコンテナに導入する
  5. Active Jobを導入する
  6. 指定した時間にメッセージが送られる

環境

  • docker
  • rails 7.0.8.4
  • ruby 3.2.3
  • tailwind
  • Render.com (デプロイ先)

Redisについて

Redisはデーターベースです。RDBMS(Relational database management system)とは違う仕組みで、高速にデータを処理することができる、という特徴などがあります。
今回の実装では、ジョブを永続的に保存するために使用します
詳しい解説は下記記事が非常に参考になりました
https://qiita.com/keinko/items/60c844bcf329bd3f4af8

Sidekiqについて

Sidekiqは、Ruby on Railsなどのウェブアプリケーションでよく使われる「バックグラウンドジョブ」を処理するためのツールです。

Sidekiqの役割

Sidekiqはこのバックグラウンドジョブを効率よく処理する役割を持っています。例えば、ユーザーがサインアップしたときに確認メールを送るような処理を、Sidekiqがアプリとは別に裏で行います。これにより、アプリはユーザーにすぐに反応できるようになり、時間がかかる処理は後で自動的に進められます。
今回はRedisで溜まっているジョブを取り出してくれる役割で使用します
https://github.com/sidekiq/sidekiq/wiki/Getting-Started
https://dev.icare.jpn.com/dev_cat/sidekiq/
https://zenn.dev/overflow_offers/articles/20230130-how-to-use-sidekiq

Active Jobについて

Active Job(アクティブジョブ)は、Railsアプリケーションで使われる「バックグラウンドジョブ」を簡単に管理できる機能です。

バックグラウンドジョブとは?

通常、ウェブアプリはユーザーのリクエストにすぐに応答しますが、例えばメールを送ったり、画像を処理したりするような作業は時間がかかることがあります。そうした作業をその場でやると、アプリが遅くなってしまいます。そこで、これらの時間がかかる作業を「バックグラウンドジョブ」として、アプリとは別に後で実行するようにします。
Active Job自体は、バックグラウンドでジョブを実行するための仕組みを提供しますが、ジョブの保存(キューイング)や管理は行いません。
https://railsguides.jp/active_job_basics.html

まとめ

1. Active Jobでジョブを定義

Active Jobを使って、例えば「メールを送る」というジョブを定義します。この段階では、どのバックエンドを使ってジョブを処理するかは決まっていません。

2. ジョブのキューイング

定義されたジョブをバックエンド(Sidekiqなど)に渡します。バックエンドは、このジョブを「キュー」というリストに保存します。Redisは、この「キュー」を管理するために使われます。

3. ジョブの実行

Sidekiqなどのバックエンドが、Redisに保存されたジョブを順番に取り出し、実行します。Redisは非常に速いデータベースなので、このジョブのキューイングと取り出しが効率的に行えます。

こちらの記事の例えが個人的にわかりやすかったです

言うなれば、Active Jobは洗濯物をホイホイ洗濯かごに投げ込んでいるだけの人で、洗濯かごを管理する人(Redis)、洗濯かごから洗濯物をとって洗濯機を回す人(Sidekiq)も必要ということです。

https://zenn.dev/yoiyoicho/articles/03863702867eb0

LINE Developersでチャネルを作成する(公式アカウントの作成)

1. 実装を始めていきます

まずは~LINE Developersのコンソールでチャネルを設定~して、アプリの公式アカウントを作成してください
2024/9/4以降からチャネルの設定方法が変わりました
公式ドキュメントを参考に作成をしてください
https://developers.line.biz/ja/
https://developers.line.biz/ja/docs/messaging-api/getting-started/

2. 友達追加ボタンを実装する

  • チャネルの作成が完了したら、下記画像部分のLINE Official Account Managerにアクセスしてください
    Image from Gyazo

  • ホーム友達追加ガイドボタンを作成にアクセスしてください
    Image from Gyazo

  • アクセスすると友達追加用のコードが発行されるので、それをコピーして任意のviewに貼り付けでボタンの実装は完了です

    Image from Gyazo

ターミナルからの操作でメッセージを送る

1. gem 'line-bot-api'のインストール

https://github.com/line/line-bot-sdk-ruby
Gemfile

gem 'line-bot-api'

2. bundle install

bundle install

3. credentialsファイルにチャネルのトークンなどを記録する

下記コマンドをコンテナ内で実行してvimで操作します

EDITOR="vim" bin/rails credentials:edit

矢印キーでカーソルを操作し、任意の箇所でiを押すと挿入モードに切り替わります。

line_notify:
	chanel_id: YOUR_ID ## チャネルID
	chanel_secret: YOUR_SECRET ## チャネルシークレット 
	chanel_taken: YOUR_TAKEN ## チャネルアクセストークン(長期)

を入力した上でescキーで挿入モードを終えたのち、:wqそしてEnterを押せば保存完了です。

https://railsguides.jp/security.html#独自のcredential

4. config/initialize/line_bot.rbを作成する

  • config/initialize/line_bot.rbを作成して、LINE Botを使うために必要な「初期設定(初期化)」を行います。設定した3つをRails.application.credentialsから取り出して設定することで、このアプリがLINE Botとして認証され、LINEとやり取りができるようになります。
config/initialize/line_bot.rb
require 'line/bot'

LINE_NOTIFY_CLIENT = Line::Bot::Client.new { |config|
  config.channel_id = Rails.application.credentials.dig(:line_notify, :channel_id)
  config.channel_secret = Rails.application.credentials.dig(:line_notify, :channel_secret)
  config.channel_token = Rails.application.credentials.dig(:line_notify, :channel_access_token)
}

5. app/service/notification_service.rbを新規で作成してpushメッセージを設定する

notification_service.rb
class NotificationService
  def self.call
    new.call
  end

  def call
    send_push_message
  end

  private

  def send_push_message
    message = {
      type: 'text',
      text: 'これはPushメッセージです。'
    }

    response = LINE_NOTIFY_CLIENT.broadcast(message)
    Rails.logger.info "Push message response: #{response.inspect}"
  end
end
  • app/serviceディレクトリは、Railsアプリケーションでビジネスロジックを整理するために使われます。簡単に言うと、「複雑な処理や共通する処理を1つのクラスやモジュールにまとめる場所」です。
    外部サービスとのやり取りをまとめたクラスを作成し、APIへのリクエストを整理することもできます。
    https://qiita.com/game4967/items/6592697777669b91d4fd

6. メッセージが送られるかを確認する

rails consoleをターミナルで実行して

 > NotificationService.call

上記コマンドを打った時に友達追加したLINEアカウントからメッセージが送信されているかを確認してください

RedisとSidekiqをDockerコンテナに導入する

1. compose.ymlにredisとsidekiqを追加します

  redis:
    image: "redis:7.0-alpine"
    volumes:
      - redis_volume:/data
    command: redis-server --appendonly yes
    ports:
      - "6379:6379"
  sidekiq:
    build:
      context: .
    command: bundle exec sidekiq
    volumes:
      - .:/myapp
    depends_on:
      - db
      - redis

2. volumes:にもredis_volume:を追加します。

volumes:
  redis_volume:

参考記事
https://qiita.com/lemonade_37/items/296bc211cf3e781c5600#sidekiqredisの導入

3. docker compose buildコマンドで再ビルドします

4. gem sidekiqとsideliq-schedulerを導入する

https://github.com/sidekiq/sidekiq
https://github.com/sidekiq-scheduler/sidekiq-scheduler

Gemfile

gem 'sidekiq'
gem 'sidekiq-scheduler'

5. bundle install

bundle install
sidekiq-cronとsidekiq-schedulerの違いについて説明

共通点

  • 目的: 両方ともSidekiqジョブのスケジューリングを行うためのgemです。
  • 機能: 定期的に実行するジョブを設定するための機能を提供します。

sidekiq-cron

  1. Cron形式のスケジュール:
    • 特徴: sidekiq-cronは、UNIXのcron形式のスケジュールを使用してジョブを定義します。これにより、非常に柔軟かつ詳細なスケジューリングが可能です。
    • 使い方: cron形式のスケジュールを設定ファイルに記述し、その通りにジョブが実行されます。
    • :
      # config/sidekiq.yml
      :schedule:
        send_random_scam_push_message:
          cron: "0 7 * * *"  # 毎朝7時に実行
          class: "SendRandomScamPushMessageJob"
      
      
  2. 依存関係:
    • 依存: rufus-schedulerを内部で使用しています。
    • 設定: Cron形式に慣れている人にとって使いやすい。
  3. 柔軟性:
    • 高い: 非常に細かい時間指定が可能で、複雑なスケジュールを設定するのに適しています。

sidekiq-scheduler

  1. シンプルなスケジュール形式:
    • 特徴: sidekiq-schedulerは、YAML形式でシンプルにスケジュールを定義できます。人間が読みやすい形式でスケジュールを設定できるため、使い勝手が良いです。
    • 使い方: YAML形式のスケジュールを設定ファイルに記述し、その通りにジョブが実行されます。
    • :
      # config/sidekiq_scheduler.yml
      send_random_scam_push_message:
        every: '1h'  # 毎時実行
        class: 'SendRandomScamPushMessageJob'
      
      
  2. 依存関係:
    • 独自: rufus-schedulerを使用せず、独自のスケジューリングロジックを使用します。
    • 設定: よりシンプルで直感的な設定が可能です。
  3. 柔軟性:
    • 中程度: Cron形式ほどの細かい設定はできませんが、頻度ベースのスケジューリングには十分対応できます。

どちらを選ぶべきか?

  • 複雑なスケジュールが必要な場合:
    • sidekiq-cronを選ぶと良いでしょう。Cron形式のスケジュールを使用することで、柔軟かつ詳細なスケジューリングが可能です。
  • シンプルなスケジュールが必要な場合:
    • sidekiq-schedulerを選ぶと良いでしょう。YAML形式で簡単にスケジュールを設定できるため、使いやすさが魅力です。

まとめ

  • sidekiq-cron: UNIXのcron形式を使用して詳細なスケジュール設定が可能。複雑なスケジュールに適している。
  • sidekiq-scheduler: YAML形式でシンプルにスケジュール設定が可能。シンプルなスケジュールに適している。

それぞれの用途に応じて適切なgemを選択してください。

6. config/application.rbに下記を追加

config/application.rb
class Application < Rails::Application
  # 〜〜省略〜〜
  config.active_job.queue_adapter = :sidekiq
  # 〜〜省略〜〜
end

Railsアプリケーションでバックグラウンドジョブを処理するために、Sidekiqを使うように指定します。具体的には、Active Jobのジョブを実行する際の「キューアダプタ」をSidekiqに設定しています。

7. config/initializers/sidekiq.rbを作成し、下記のように記載。

Sidekiq.configure_server do |config|
  config.redis = { url: 'redis://redis:6379' }
end

Sidekiq.configure_client do |config|
  config.redis = { url: 'redis://redis:6379' }
end

Sidekiqのサーバー(ジョブを処理する部分)とクライアント(ジョブをキューに追加する部分)が、どちらも同じRedisサーバーに接続してデータをやり取りするように設定します

8. config/sidekiq.ymlを作成し、下記のように記載

:concurrency: 3
:queues:
  - default

9. 導入の確認

別ターミナルで
docker compose exec web bundle exec sidekiqを実行して、下記のように起動されれば導入完了
Image from Gyazo

Active Jobを設定する

1. jobファイルを作成する

bin/rails g job send_line_message

下記コードを記述してください

send_line_message_job.rb
class SendLineMessageJob < ApplicationJob
  queue_as :default

  sidekiq_options retry: 1
  # Sidekiqはジョブの実行に失敗した場合、デフォルトで25回リトライするため、
  # 何度も通知が送られないよう、リトライ回数を1回に指定

  sidekiq_retry_in do |_count|
    60
  end
  # ジョブのリトライ間隔を60秒に設定

  def perform
    NotificationService.call
  end
end

2. sidekiq.ymlにsucheduleの設定を行う

:scheduler:
  :schedule:
    send_line_message_job:
      every: '2m' #一旦2分に一回送る
      class: "SendLineMessageJob"
      queue: default

3, 実装の確認をする

sucheduleの設定を完了したら、一度サーバーを再起動してください
その後、Sidekiqのターミナルで下記画像の用になっていたら、設定完了です
Image from Gyazo

### この部分で設定が確認できます
Scheduling send_line_message_job {"every"=>"2m", "class"=>"SendLineMessageJob", "queue"=>"default"}

2分ごとにメッセージが送られていたら成功です

感想

今回では、ローカル環境のみでの実装を記述しました
次の記事で、Render.com(本番環境)でのデプロイまでの実装を記述しています
https://zenn.dev/yukimura_n/articles/181c85d127fb10
Redis,Sidekiqなど聞きなれない言葉での実装で最初は全然理解が進まなかったですが、少しずつ調べていき、結果として視野が広がったと感じたのでこの実装に挑戦して良かったなと思いました

Discussion