はじめに
コロナ騒ぎで時間があるので Rails の Action Cable を触ってみました。
基本的な使い方は Rails ガイドで調べましたが、いまいちピンとこなかったので理解するために図を描きました。特に用語についての章を読んでもそれぞれの用語の関係性がよく分からなかったので、自分なりに整理しています。
用語について
Connection, Consumer
WebSocket の基礎
- Web 上でクライアント、サーバ間の双方向通信を実現するための通信規格
-
Upgrade: websocket
ヘッダをつけた HTTP リクエストで開始 - レスポンスとして HTTP ステータス 101(Switching Protocols) が返ってきたら TCP コネクションを使いまわして WebSocket として通信ができる
- 送信できるデータはテキストまたはバイナリ
- HTTP とは別のプロトコルなので Cookie などは送られない
- 通信が確立したあとはクライアント、サーバどちらからでもメッセージを送ることが可能
Connection
- WebSocket としてのコネクションを表すクラス
- (おそらく)WebSocket として通信が確立した時点でインスタンスが生成される
- 開始時のリクエストはふつうの HTTP なので Cookie も送られる
- なので
connection.rb
ではcookies
が使える - 認証処理はこのクラス内に記述
Consumer
- WebSocket コネクションのクライアント(ブラウザなど)
- HTTP とは異なり、同じブラウザの異なるタブは完全に別のクライアントとして扱われる
Consumer を区別する方法
HTTP とは異なり毎回認証情報が送られたりはしないので、コネクション確立時に「このコネクションは誰のものか」をサーバー側で管理する必要がある。
そのためのしくみとして Action Cable では Connection クラスの identified_by
を使う。
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = User.find_by(id: cookies.encrypted[:user_id])
end
end
end
Channel, Subscription
Channel
- 文字通りのチャンネル
- Controller のようなもの
Subscription
- チャネルの購読
- ひとつのコネクション内でいくつでも作成可能
- あるチャネルの購読をしている、という意味ではコンシューマをサブスクライバとも呼ぶ
以下のように consumer.subscriptions.create
で新たな Subscription
オブジェクトが作成され、consumer.subscriptions
に追加される。
import consumer from "./consumer"
consumer.subscriptions.create("ChatChannel", {
// 略
});
Stream
- ブロードキャストを行う場合に使う
- チャネルがサブスクリプションとストリームの紐付けを行う
- あるストリームにブロードキャストした場合、そのストリームに紐づくサブスクリプションにメッセージが送られる
例
たとえば以下のようなチャネルクラスを定義しておくと ChatChannel
をサブスクライブしたときに chat_001
というストリームに紐付けられる。
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_001"
end
end
この状態で以下のコードが実行されると、サブスクライバに {"text": "Hello, World!"}
というJSON文字列が送られる。
data = {text: "Hello, World!"}
ActionCable.server.broadcast("chat_001", data)
ストリームに対するブロードキャスト自体は、Rails アプリケーションのどこからでも実行可能。
複数のStream
前節の例では固定文字列だったので、どのサブスクリプションも同じストリームに紐付いていました。しかし、チャネルをサブスクライブするときサーバー側にパラメータを渡すことができるので、ユーザーごと、もしくは開いているページごとに異なるストリームに紐付けることが可能です。
まとめ
ストリームとサブスクリプションの関係がよく分からなかったのでそこを中心に図示してみました。この図だとストリームが必須みたいに見えてしまうので、action パラメータによってサーバー側のメソッドを実行する、という観点でも整理して別途記事にしてみようと思います。