Sidekiq、REDISとレール6で考案リアルタイム通知システム
このポストでは、我々は非同期機能について多くの話をしようとしています.
これは必ずしも新しいものだということを意味していませんが、今日のJS生態系のおかげで、世界は現実に起こります.
このポストで、私はこれの重要な概念を教えたいです.しかし、いつものように、我々は理論に滞在していない、我々は我々のアプリケーションからのリアルタイム通知などの実際の実装を見るつもりです.
私はできるだけ簡単でシンプルにしようとします.
非同期プログラミング:我々のプログラムで起こるイベントの発生を参照します.これらは、メインプログラムとは独立して実行され、実行を妨げることはありません.これに先立って、我々は、ユーザーの経験を真剣に実行を継続する応答を待つことを余儀なくされた.
WebSockets : WebSocketsは、クライアント/サーバーのWeb技術の長い待望の進化を表します.彼らは、長い間保持された単一のTCPソケット接続をクライアントとサーバーの間で確立させます.そして、双方向の、完全な二重のメッセージが非常に低い待ち時間接続に結果としてほとんどオーバーヘッドですぐに分配されるメッセージを考慮に入れます.Keep reading
言い換えれば、クライアントとサーバとの間の接続ごとにピアツーピアを確立することができます.この前に、クライアントはサーバがどこにあるかを知っていたが、その逆ではなかった.
これのおかげで、我々はサーバーに要求を送ることができて、あなたの応答を待つことなく、我々のプログラムを実行し続けます.その後、サーバーはクライアントがどこにあるかを知っていて、あなたに応答を送ることができます.
上記のすべては、すでに我々の通知システムのために意味をなします?
続行する前にREADISをインストールしてください.SideKiqはすべてのジョブと操作データを格納するREDISを使用します.
👋 Redisが何であるかを知らないならばofficial site
Sidekiq 私たちはスーパーシンプルで効率的な方法でバックグラウンドで動作するように役立ちます.また、私の好きな宝石の1つ♥️)
私の創造this project この記事のために直接興味を集中するには.プロジェクトは、私たちの通知を表示するには、ユーザー認証と必要なフロントと簡単なブログです.あなたはそれをダウンロードし、私との記事に従ってください.
注:「通知」ブランチで完全な実装を見ることができます
イン
だから
そのために、アプリケーションで初期化子を作成できます.
注意:アイテムはpolymorphic association , 私は様々なタイプの通知を追加することができますので、私はこのようにそれを行う
これと他の詳細については
そのために
私は例を終了するためにバックエンドにいくつかのものを追加するつもりです.
まず第一に、私は私が私のユーザーモデルにメソッドを加えるつもりです.そして、モデルはこの質問をする良い場所です.イン
それはすべて、私はあなたに役立つことを望む👋
これは必ずしも新しいものだということを意味していませんが、今日のJS生態系のおかげで、世界は現実に起こります.
このポストで、私はこれの重要な概念を教えたいです.しかし、いつものように、我々は理論に滞在していない、我々は我々のアプリケーションからのリアルタイム通知などの実際の実装を見るつもりです.
私はできるだけ簡単でシンプルにしようとします.
定義と概念
非同期プログラミング:我々のプログラムで起こるイベントの発生を参照します.これらは、メインプログラムとは独立して実行され、実行を妨げることはありません.これに先立って、我々は、ユーザーの経験を真剣に実行を継続する応答を待つことを余儀なくされた.
Concurrency, Parallelism, Threads, Processes, Async and Sync — Related? 🤔
WebSockets : WebSocketsは、クライアント/サーバーのWeb技術の長い待望の進化を表します.彼らは、長い間保持された単一のTCPソケット接続をクライアントとサーバーの間で確立させます.そして、双方向の、完全な二重のメッセージが非常に低い待ち時間接続に結果としてほとんどオーバーヘッドですぐに分配されるメッセージを考慮に入れます.Keep reading
言い換えれば、クライアントとサーバとの間の接続ごとにピアツーピアを確立することができます.この前に、クライアントはサーバがどこにあるかを知っていたが、その逆ではなかった.
これのおかげで、我々はサーバーに要求を送ることができて、あなたの応答を待つことなく、我々のプログラムを実行し続けます.その後、サーバーはクライアントがどこにあるかを知っていて、あなたに応答を送ることができます.
しましょう👊
上記のすべては、すでに我々の通知システムのために意味をなします?
続行する前にREADISをインストールしてください.SideKiqはすべてのジョブと操作データを格納するREDISを使用します.
👋 Redisが何であるかを知らないならばofficial site
Sidekiq 私たちはスーパーシンプルで効率的な方法でバックグラウンドで動作するように役立ちます.また、私の好きな宝石の1つ♥️)
私の創造this project この記事のために直接興味を集中するには.プロジェクトは、私たちの通知を表示するには、ユーザー認証と必要なフロントと簡単なブログです.あなたはそれをダウンロードし、私との記事に従ってください.
注:「通知」ブランチで完全な実装を見ることができます
initの設定
イン
config/routes.rb
我々のルートをマウントしますActionCable ( WebSocketsのリアルタイム通信のためのフレームワーク)Rails.application.routes.draw do
# everything else...
mount ActionCable.server => '/cable'
end
さて、WebSocketの仕組みを覚えていますか?Peer - to - peer接続は、他の語でよく、また、チャンネル(それは我々がRailsでそれを呼ぶように)で、そのチャンネルの中で、我々は常に各々のユーザーを特定しなければなりません.これは、サーバーが誰に返信するかを知ることができますし、誰がリクエストを知っている.この場合、ユーザーとそれを識別します.id ( deviseを使っています)だから
app/channels/application_cable/connection.rb
:module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_user
end
def find_user
user_id = cookies.signed["user.id"]
current_user = User.find_by(id: user_id)
if current_user
current_user
else
reject_unauthorized_connection
end
end
end
end
私たちはクッキーにログインしたユーザを保存します(他の場所からそれを得るのを助けるでしょう、我々は見ます)、これのためのおもしろい解決(少なくとも工夫で)は使用ですWarden Hooks そのために、アプリケーションで初期化子を作成できます.
config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user, auth, opts|
auth.cookies.signed["user.id"] = user.id
auth.cookies.signed["user.expires_at"] = 30.minutes.from_now
end
Warden::Manager.before_logout do |user, auth, opts|
auth.cookies.signed["user.id"] = nil
auth.cookies.signed["user.expires_at"] = nil
end
さあ、私たちが作成するすべての通知を保存するために、私たちのデータベースのテーブルを作りましょう.$ rails g model Notification user:references item:references viewed:boolean
注意:アイテムはpolymorphic association , 私は様々なタイプの通知を追加することができますので、私はこのようにそれを行う
これと他の詳細については
db/migrate/TIMESTAMP_create_notifications.rb
):class CreateNotifications < ActiveRecord::Migration[6.0]
def change
create_table :notifications do |t|
t.references :user, foreign_key: true
t.references :item, polymorphic: true
t.boolean :viewed, default: false
t.timestamps
end
end
end
および$ rails db:migrate
インapp/models/notification.rb
我々は、我々が行くところで見る2、3のものをするつもりですclass Notification < ApplicationRecord
belongs_to :user
belongs_to :item, polymorphic: true # Indicates a polymorphic reference
after_create { NotificationBroadcastJob.perform_later(self) } # We make this later
scope :leatest, ->{order("created_at DESC")}
scope :unviewed, ->{where(viewed: false)} # This is like a shortcut
# This returns the number of unviewed notifications
def self.for_user(user_id)
Notification.where(user_id: user_id).unviewed.count
end
end
を作成しましょうconcern , 最も尊敬された柵の哲学の一つは、(自分自身を繰り返す)、乾いていることを忘れないでください、現在、各通知は、同じように動作するようにする必要があります(モデルで)(再び、このプロジェクトでは、我々は出版物を持っていますが、我々は我々の通知システムと統合する多くの他のものを持つことができるので、この形式では、それは超簡単です).そのために
app/models/concerns/notificable.rb
module Notificable
extend ActiveSupport::Concern # module '::'
included do # this appends in each place where we call this module
has_many :notifications, as: :item
after_commit :send_notifications_to_users
end
def send_notifications_to_users
if self.respond_to? :user_ids # returns true if the model you are working with has a user_ids method
NotificationSenderJob.perform_later(self)
end
end
end
今、我々はそれを含めることができますapp/models/post.rb
. 私たちのsend_notifications_to_users
方法を予想するuser_ids
それぞれの修正をあなたに返信する.そのようにしましょう.class Post < ApplicationRecord
include Notificable
belongs_to :user
def user_ids
User.all.ids # send the notification to that users
end
end
我々は、作成するつもりですjob 通知を送信するの担当では、これは我々がバックグラウンドに送信され、我々はSideKiqで処理されます.そのために$ rails g job NotificationSender
仕事の中app/jobs/notification_sender_job.rb
):class NotificationSenderJob < ApplicationJob
queue_as :default
def perform(item) # this method dispatch when job is called
item.user_ids.each do |user_id|
Notification.create(item: item, user_id: user_id)
end
end
end
最終的に、SideKIQをインストールする必要があります(そして、Sinatraは少し簡単にするためにいくつかのことを作る)ので、アウトでGemfile
:# everything else...
gem 'sinatra', '~> 2.0', '>= 2.0.8.1'
gem 'sidekiq', '~> 6.0', '>= 6.0.7'
忘れないで.$ bundle install
SideKIQをジョブのキューアダプタで使用するようにRailsに指示しますconfig/application.rb
):# everything else...
module Blog
class Application < Rails::Application
# everything else...
config.active_job.queue_adapter = :sidekiq
end
end
また、SideKiqが私たちに提供するルートを設定するつもりです.その中で、バックグラウンドのためのバックオフィスの一種(後に、localhostからのaccesを持つことができます.インconfig/routes.rb
:require 'sidekiq/web'
Rails.application.routes.draw do
# everything else...
mount Sidekiq::Web => '/sidekiq'
end
今、我々は我々が我々の通知を送るチャンネルをつくるつもりです.$ rails g channel Notification
このチャンネルのバックエンドでapp/channels/notification_channel.rb
), ユーザーを購読します.class NotificationChannel < ApplicationCable::Channel
def subscribed
stream_from "notifications.#{current_user.id}" # in this way we identify to the user inside the channel later
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
そして、チャンネルのフロントエンドapp/javascript/channels/notification_channel.js
) これは、ブラウザにプッシュ通知を送信するには興味深いですが、多くのJSライブラリが非常に簡単にthis ), しかし、ポストを非常に重くしないために、我々はコンソールに単純なメッセージを印刷するつもりです.それで// everything else...
consumer.subscriptions.create("NotificationChannel", {
// everything else...
received(data) {
if(data.action == "new_notification"){
cosole.log(`New notification! Now you have ${data.message} unread notifications`)
} // we will define action & message in the next step
}
});
この時点で、我々はすでに多くの実行している、その通知をユーザーに送信しましょう!このために、我々はちょうどこれをするもう一つの仕事をつくるつもりです、覚えていてください、前の仕事は通知をつくることを担当しています、これは放送をします.それで$ rails g job NotificationBroadcast
内部app/jobs/notification_broadcast_job.rb
:class NotificationBroadcastJob < ApplicationJob
queue_as :default
def perform(notification)
notification_count = Notification.for_user(notification.user_id)
ActionCable.server.broadcast "notifications.#{ notification.user_id }", { action: "new_notification", message: notification_count }
end
end
ファンタスティック、我々はすでにすべての作業を持っている!🎉私は例を終了するためにバックエンドにいくつかのものを追加するつもりです.
まず第一に、私は私が私のユーザーモデルにメソッドを加えるつもりです.そして、モデルはこの質問をする良い場所です.イン
app/models/user.rb
:class User < ApplicationRecord
# everything else...
def unviewed_notifications_count
Notification.for_user(self.id)
end
end
私もコントローラを作るつもりです.$ rails g controller Notifications index
. コントローラ内部app/controllers/notifications_controller.rb
) いくつかのメソッドを追加します.class NotificationsController < ApplicationController
def index
@notifications = Notification.where(user: current_user).unviewed.leatest
respond_to do |format|
format.html
format.js
end
end
def update
@notification = Notification.find(params[:id])
message = @notification.update(notification_params) ? "Viewed notification" : "There was an error"
redirect_to :back, notice: message
end
private
def notification_params
params.require(:notification).permit(:viewed)
end
end
私は、リモート対応して、NAVで私のドロップダウンで最新の通知を表示することができるJSビューを作成します.インapp/helpers/notifications_helper.rb
:module NotificationsHelper
def render_notifications(notifications)
notifications.map do |notification|
render partial: "notifications/#{notification.item_type.downcase}", locals:{notification: notification}
end.join("").html_safe
end
end
あなたのNavで私のケースで関連を加えてくださいapp/views/partials/notifications.html.erb
):<%= link_to notifications_path, remote: true, data:{ type:"script" } %>
忘れずにパスを追加しましょうapp/config/routes.rb
) この新しいコントローラのために.# everything else...
Rails.application.routes.draw do
# everything else...
resources :notifications, only: [:index, :update]
end
この項目の一部を作成するapp/views/notifications/_post.rb
). このようにして「マークされたマーク」へのリンクを含めることができます.<%= link_to notification_path(id: notification, notification:{viewed: true}), method: :put %>
ローカルで実行するにはREDISを実行しなければなりません.$ redis-server
) とsidekiq$ bundle exec sidekiq
) + $ rails s
, これらの3つのコマンドを並列に実行すると、3ターミナルウィンドウが開きます.それはすべて、私はあなたに役立つことを望む👋
Reference
この問題について(Sidekiq、REDISとレール6で考案リアルタイム通知システム), 我々は、より多くの情報をここで見つけました https://dev.to/matiascarpintini/real-time-notification-system-with-sidekiq-redis-and-devise-in-rails-6-33l9テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol