Linkers Tech Blog

リンカーズ株式会社の開発者ブログです。

ActionCableの基本的な挙動と、ログの意味を理解する

はじめに

情報システム部の横山です。 ActionCable(Railsで扱えるWebsocketフレームワーク)のログを出力すると、見慣れない言葉が出てきます。subscribebroadcasttransmitとは何を指すのだろう……と疑問を抱いたのでこの記事を書きました。

検証バージョン

  • Rails 6.1
  • ActionCable 6.1
  • アダプタにはRedisを使用

前準備

ログをWebサーバーから分離する

ログを見やすくするのはとても大切です。ログを丁寧に扱わないのは、犯人の指紋が残っているドアノブに、刑事がべたべたと自分の指紋をつけてしまうような愚行です。エラーが起きたときの手がかりを少しでも多く残すために、手間を惜しまないようにしましょう。

ActionCableサーバーのloggerは、デフォルトでRails.loggerになっています。そのため、WebサーバーとActionCableサーバーのプロセスが同一のマシンにあった場合、Rails.loggerにはActiveRecordのログやらActionCableのログやらがごちゃまぜに出力されてしまいます。それは大変見にくいので、ActionCableのログ出力はRails.loggerから分けましょう。

ActionCableサーバーのプロセスとWebサーバーのプロセスが別のマシンにある場合は、この手順は必ずしも必要ありません。

# config/initializers/action_cable.rb
action_cable_logger = ActiveSupport::Logger.new('log/action-cable.log', 'monthly')
action_cable_logger.formatter = ->(severity, datetime, _progname, message) do
  "#{datetime.strftime('%Y-%m-%d %H:%M:%S.%L')} [#{severity}] - #{message}\n"
end
ActionCable.server.config.logger = action_cable_logger

お好きなコマンドでログを見るようにしてください。

tail -n 30 -f log/action-cable.log
ログを確認する

ログを見ると、streaming, Broadcasting, transmittingという単語があるのが分かります。これが何なのかを理解していきましょう。

2022-01-25 12:08:44.747 [INFO] - Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2022-01-25 12:08
2022-01-25 12:08:44.747 [INFO] - Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
2022-01-25 12:08:44.795 [INFO] - Registered connection (Z2lkOi8vaG9rdWcvQmFua2VyLzM)
2022-01-25 12:08:44.842 [DEBUG] - ChatChannel is transmitting the subscription confirmation
2022-01-25 12:08:44.842 [INFO] - ChatChannel is streaming from room_1
2022-01-25 12:08:45.210 [DEBUG] - [ActionCable] Broadcasting to room_1: "今夜麻雀しませんか?"
2022-01-25 12:08:45.257 [DEBUG] - ChatChannel transmitting "今夜麻雀しませんか?" (via streamed from room_1)
2022-01-25 12:08:45.313 [DEBUG] - ChatChannel transmitting "今夜麻雀しませんか?" (via streamed from room_1)
2022-01-25 12:08:45.313 [DEBUG] - ChatChannel transmitting "今夜麻雀しませんか?" (via streamed from room_1)

ActionCableの基本用語

用語は、まずは公式に目を通すことが大切です。 Action Cable の概要 - 2 用語について

ただ、英語をそのまま翻訳した文章のようなので少し分かりにくいかもしれないので、ここではいくつかの大事な用語を私の理解で説明します。

channel

MVCフレームワークのController的な存在です。例えば、チャット機能でActionCableを使いたいならChatChannelを作ったり、オンラインゲーム機能で使いたいならGameChannelを作ったりします。 そのChatChannelの中に、「雑談部屋」や「ほのぼのnews部屋」や「ブラウザゲームについて語る部屋」などの論理的な部屋、仕切りを作ることができます。

stream

なぜか公式の用語一覧には載っていない(記事の中には載っている)のですが、channel内の論理的な一つ一つの部屋を呼ぶ用語をstreamといいます。この用語はとても大切で、ChatChannel is streaming from room_1みたいなログとして出てきたり、あとは stream_from のようにコード中にも登場したりします。 streamは日本語で「流れ」のような意味です。

subscribe

subscribeは日本語で「購読する」のような意味です。 ブラウザ(クライアント)は、streamに対してsubscribeを行います。「よし、雑談部屋を見よう」という時には、雑談部屋のstreamをブラウザがsubscribeするということです。subscribeしている人やブラウザのことを、subscriber(サブスクライバ)とも言います。

broadcast

雑談部屋を現在subscribeしている人たちに、「今夜麻雀しませんか?」という言葉を投げかけるとしたら、その部屋のsubscriberたちに一斉送信、つまりbroadcastすることになります。

RedisのPub/Sub

詳しくは後で解説します。RedisにはPub/Subという機能があります。これは普段のKVSとしてのRedisとは全く異なるマイナーな機能なので、注意が必要です。

図解(チャットでメッセージを送る)

この図では、Client 1, 2, 3がそれぞれ「雑談部屋」stream (room_1)を①subscribeしています。

そこへ、Client 1が「今夜麻雀しませんか?」と②broadcastします。すると、ActionCableサーバーがRedisのpub/subに対して、メッセージを発行(PUBLISH)します。すると、それをSUBSCRIBEしている各ActionCableサーバーはメッセージを受け取ることができます。

※この画像ではActionCableサーバーは1つだけです。Redisがあれば、ActionCableサーバーのプロセスが複数あっても問題ありません。 ※ここでいう「SUBSCRIBE」はRedis⇐ActionCableサーバーのSUBSCRIBEなので、ActionCable⇐ブラウザのsubscribeとは別です。

そしてRedisからメッセージを受け取ったActionCableサーバーは、「雑談部屋」stream (room_1)をsubscribeしているsubscriberたちに、「今夜麻雀しませんか?」を③transmit(送信)します。

RedisのPub/Sub

なぜ使用するのか

Redisは複数のActionCableサーバープロセスがある場合に活躍します。ActionCableサーバーのプロセス1とプロセス2で、それぞれChatChannelや雑談部屋streamがあります。複数のサーバー間で新しく流れてくるデータを受け渡すためにRedisを利用できます。

Pub/Subとは何か

Redis - Pub/Subは公式を読むのが一番良いですが、英語なのでかいつまんで説明します。RedisといえばKVSですが、この機能はKVSとは全く異なるマイナーな機能です。 簡単に言うと、SUBSCRIBEしているクライアントたちにPUBLISHでメッセージを送ることができる機能です。何度も言いますが、このSUBSCRIBEはブラウザがstreamに対してsubscribeするのとは別です。

Pub/Sub機能には注意点があります。Pub/SubはRedisがデフォルトで持っているような0 ~ 15のdatabaseとは何も関係がありません。

Pub/Sub has no relation to the key space. It was made to not interfere with it on any level, including database numbers. (訳) Pub / Subはキースペースとは関係ありません。データベース番号を含め、どのレベルでも干渉しないように作られています。

実際に動かして確認する

ローカルの開発環境でRedisを利用する場合、以下のようにしてPSUBSCRIBE(※パターン指定でのSUBSCRIBE)を行い、実際にPUBLISHされるメッセージを確認することができます。オプションのホスト名やポート番号は読み替えてください。

redis-cli -h 127.0.0.1 -p 6379 psubscribe '*'

メッセージが発行されると、このように表示がされます

1) "pmessage"
2) "*"
3) "some_channel_prefix:room_1"
4) "今夜麻雀しませんか?"(実際はエンコードされた文字列が出る)

これをActionCableサーバーがSUBSCRIBEしているため、複数のActionCableサーバーがあっても大丈夫ということです。

おわりに

ActionCableについて何もわからない状態から実際の動作を見つつ調査しました。間違っていたら教えてください。あまりドキュメントが見つけられなかったので、Channelって何? からスタートした後、結構理解するまでが大変でした。

この記事がActionCableを新しく触る人の理解を深めてくれたら幸いです。

参考