はじめに
Action Cableは、 WebSocketとRailsのその他の部分をシームレスに統合するためのものです。ちなみに、WebSocketとは、Webにおいての双方向通信を、従来のHTTP等よりも低コストで行うための仕組み。
事前知識
コンシューマー
WebSocketコネクションのクライアント。複数のケーブルチャネルにサブスクライブできます。
Pub/Sub
パブリッシャ-サブスクライバ(pub/sub)型モデルとも呼ばれる、メッセージキューのパラダイムです。パブリッシャ側(Publisher)が、サブスクライバ側(Subscriber)の抽象クラスに情報を送信
します。
購読(subscription)
クライアントがWebsocket通信を通じたチャネルと関連付け。購読は、一度に複数のチャネルに対して作成可能。
app/channels/〇〇_channel.rb
Websocket処理におけるコントローラーのような役割を果たす。
class RoomChannel < ApplicationCable::Channel
# 購読後に呼ばれるメソッド
def subscribed
end
# 購読解除後に呼ばれるメソッド
def unsubscribed
end
# クライアントから呼び出された時に実行されるメソッド
def speak
end
end
app/javascript/channels/〇〇.js
クライアントサイドを受け持つファイル。
import consumer from "./consumer"
consumer.subscriptions.create("RoomChannel", {
// 接続時の処理
connected() {
},
// 切断時の処理
disconnected() {
},
// サーバーからデータを受信した際の処理
received(data) {
},
// 購読しているチェネルのspeakメソッドをWebsocket通信経由で呼び出し
speak: function() {
return this.perform('speak');
}
});
ブロードキャスト
接続している全購読者への送信。
利用方法
ActionCableを用いてチャットアプリを作成してみる。
1.チャットルーム画面のコントローラーを作成する。
# 今回は単一のチャットルームのためshowのみ作成
$ rails g controller rooms show
2.チャットメッセージを格納するモデルを作成する。
$ rails g model message content:text
$ rails db:migrate
3.ビュー側の実装をする。
class RoomsController < ApplicationController
def show
@messages = Message.all
end
end
<div class='message'>
<p><%= message.content %></p>
</div>
<form>
<label>Say something:</label><br>
<input type="text" data-behavior="room_speaker">
</form>
<h1>ChatRoom</h1>
<div id='messages'>
<%= render @messages %>
</div>
4.ActionCable用のファイルを生成する。
$ rails g channel room speak
5.サーバーサイド側の処理を実装する。
class RoomChannel < ApplicationCable::Channel
def subscribed
# ブロードキャスト用のストリーム名を設定
stream_from "room_channel"
end
def unsubscribed
end
def speak
# クライアントがメッセージを送信した時に実行される処理
message = Message.create!(content: data["message"])
ActionCable.server.broadcast(
"room_channel", { message: render_message(message) }
)
end
private
def render_message
ApplicationController.render(
partial: "messages/message",
locals: { message: message }
)
end
end
6.クライアント側の処理を実装する。
import consumer from "./consumer"
consumer.subscriptions.create("RoomChannel", {
connected() {
document,
querySelector('input[data-behavior="room_speaker"]'),
addEventListener('keypress', (event) => { // メッセージが入力された時にイベント発火
if (event.key === 'Enter') { // Enterキーを押下時、speakメソッドを呼び出す
this.speak(event.target.value);
event.target.value = '';
return event.preventDefault();
}
});
},
disconnected() {
},
received(data) {
// 受け取ったメッセージをHTML上に追加
const element = document.querySelector('#messages')
element.insertAdjacentHTML('beforeend', data['message'])
},
speak: function(message) {
return this.perform('speak', { message: message });
}
});
Tips
アダプタの設定
Websocketの接続管理を行うアダプターと、接続先を指定する。
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: actioncable_app_production
接続可能なオリジンを設定する
オリジンはURIスキーム・ホスト・ポート番号
を組み合わせたものです。development環境で別のドメインを利用する場合やproduction環境では明示的に設定する必要がある。
...
config.action_cable.allowed_request_origins = [
'http://example.com', /http:\/\/example.*/
]
...
ワーカー数を指定
デフォルトでは1プロセスにつき最大4つのスレッドが使われる設定となっています。(環境ごとに設定してもOK)
※config/database.yml
のpoolの値に、ActionCableのスレッド数とWeb用のスレッド数を足した数と同じもしくはそれ以上の値を設定しておく。
...
config.action_cable.worker_pool_size = 10
...
終わりに
少し自分的にはややこしいのですが、LINEやSlackなどのようなチャット機能を実装したい場合は、導入を検討してみても良いですね!
参考
[Action Cable の概要]
(https://railsguides.jp/action_cable_overview.html)
[【Rails】Action Cableについてまとめてみた]
(https://qiita.com/yokoto/items/fb1e66844b70f52682dd)