Perfect Ruby on Rails ActionCable - wahei628/ShareSuke GitHub Wiki

パーフェクトRailsの内容をもとに記述

ActionCable/WebScoketsとは

  • ActionCable

[Rails 7でリアルタイム通信を実現! Action Cableの基本をチュートリアルとともに理解しよう](https://codezine.jp/article/detail/17960)

ActionCableはWebSocketを使ったリアルタイム処理を提供するライブラリ。

ActionCable用のファイルを作成するには下記のコマンドを実行する

Rails g channel room speak

下記のファイルが作成される。

スクリーンショット 2024-05-16 1.10.41.png

  • app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
  def subscribed
    # クライアントがこのチャンネルにサブスクライブ(接続)
    # したときに呼び出されるメソッド
  end

  def unsubscribed
    # クライアントがこのチャンネルからサブスクリプションを解除(切断)
    # したときに呼び出されるメソッド
  end

  def speak
	  # クライアントからの特定のアクションを処理するために使用される。
	  # この例ではメソッドの本体が空ですが、例えばクライアントから送信された
	  # メッセージを処理するロジックをここに記述
  end
end
  • app/javascript/channels/room_channel.js
import consumer from "channels/consumer"
// consumerをchannels/consumerからインポートしています。
// このconsumerは、ActionCableのコンシューマ(クライアント)
// インスタンスを表します。これを使ってチャンネルのサブスクリプションを作成

consumer.subscriptions.create("RoomChannel", {
// RoomChannelという名前のチャンネルに対するサブスクリプションを作成
// 第2引数にはサブスクリプションの設定オブジェクトを渡します

  connected() {
// サブスクリプションがサーバーで準備完了したときに呼び出されるメソッド
//このメソッド内で、接続が確立されたときの処理を記述します
  },

  disconnected() {
	// サブスクリプションがサーバーで終了されたときに呼び出されるメソッド。
	// このメソッド内で、接続が切断されたときの処理を記述
  },

  received(data) {
	// このチャンネルでWebSocketからデータを受信したときに呼び出されるメソッド
	// サーバーから送られてきたデータは、引数dataとして渡される。
	// このメソッド内で、受信したデータを処理する
  },

  speak: function() {
  // speakという名前のメソッドを定義。このメソッドは、performメソッド
  // を使ってサーバー側のspeakアクションを呼び出します。
  
    return this.perform('speak');
	// クライアントからこのメソッドが呼ばれると、サーバーに「speak」
	// アクションを実行するようにリクエストが送られます。
  }
});

Rails g channelコマンドでActionCable用のファイルを作成した後

サーバーを再起動しブラウザにアクセスするとWebSocketでの接続ができる。

スクリーンショット 2024-05-16 1.26.51.png

チャットアプリを作成していく

サーバーサイドの実装

  • app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
  stream_from "room_channel"
	  # クライアントがサブスクライブしたときに、room_channelという
	  # 名前のブロードキャストストリームを購読するためのメソッド
	  # このストリームにブロードキャストされたメッセージをクライアントが受信できるようになる。
end

def unsubscribed

end

def speak
  ActionCable.server.broadcast(
	# ActionCable.server.broadcast: このメソッドを使って、特定のチャンネルに
	# メッセージをブロードキャストする。この場合、room_channelという名前のチャンネルにメッセージを送信。

    "room_channel", { message: data["message"] }
			# room_channelという名前のチャンネルに対して、
			# messageキーにdata["message"]の値を持つハッシュをブロードキャストします。
			# data["message"]は、クライアントから送信されたメッセージです。
  )
end
end

クライアントからメッセージが届くとActionCable.server.broadcastメソッドを使ってストリーム名room_channelで通信するクライアント全てに、届いたメッセージを送信する。

  • ブロードキャストとは?

クライアントサイドの実装

  • app/javascript/channels/room_channel.js
import consumer from "channels/consumer";

window.App = consumer.subscriptions.create("RoomChannel", {
// コンソールから呼び出せるようにwindow.Appに代入
  connected() {},

  disconnected() {},

  received(data) {
  // サーバーからデータを受け取った時に呼び出されるメソッド
    alert(data["message"]);
   // 受け取ったデータのmessageプロパティをアラート表示
   
  },

  speak: function (message) {
  // サーバーサイドへメッセージを送信するメソッド
  //引数messageを取り、speakアクションに対してこのメッセージを含むリクエスト送る
    return this.perform("speak", { message: message });
  },
});

処理の流れ

1. クライアント側でspeakメソッドを呼び出し、引数に与えられたメッセージをサーバーサイドへ送信

クライアント側のコードで、**speak**メソッドが呼び出されると、メッセージがサーバーに送信されます。

window.App.speak("Hello, World!");

この呼び出しにより、以下のコードが実行されます:

speak: function (message) {
  return this.perform("speak", { message: message });
}

ここでは、**performメソッドがRoomChannelspeakアクションを呼び出し、message**を含むデータをサーバーに送信します。

2. サーバーサイドのspeakメソッドが呼び出され、引数(data)を取得し、「room_channel」という名前のストリームにdataのmessageプロパティを変数messageとしてクライアント側へブロードキャスト送信

サーバーサイドでは、**RoomChannelspeak**メソッドが呼び出され、データが処理されます。

class RoomChannel < ApplicationCable::Channel

  def speak(data)
    ActionCable.server.broadcast("room_channel", { message: data["message"] })
  end
end

このメソッドは、**data引数からmessageプロパティを取り出し、そのメッセージをroom_channel**ストリームにブロードキャストします。

3. receivedメソッドでサーバーサイドから送信されたdataを受け取り、dataのメッセージプロパティをアラートで表示する

クライアント側では、**received**メソッドが呼び出され、サーバーから送信されたデータを受け取ります。

received(data) {
  alert(data["message"]);
}

ここでは、**datamessage**プロパティを取り出し、アラートで表示します。

チャットを完成させる

フォームから文字を入力してブラウザ上で確認できる様にしていく

テキスト入力部分を追加

  • app/views/rooms/show.html.erb
<h1> chatroom </h1>

<div id = "message"> 
  <%= render @messages %>
</div>

<form>
  <label> Say Something </label>
  <input type = "text" data-behavior = "room_speaker">
</form>

data-behavior属性はJavaScript側で、入力されたテキストを取得するための識別子として利用する。

クライアントサイドを実装

クライアントサイドの送受信処理を実装

  • app/javascript/channels/room_channel.js
import consumer from "channels/consumer";

consumer.subscriptions.create("RoomChannel", {
  connected() {
	  document.querySelector('input[data-behavior="room_speaker"]')
      .addEventListener("keypress", (event) => {
        if (event.key == "Enter") {
          this.speak(event.target.value);
          event.target.value = "";
          return event.preventDefault();
        }
      });
  },

  disconnected() {},

  received(data) {
    const element = document.querySelector("#message");
    element.insertAdjacentHTML("beforeend", data["message"]);
  },

  speak: function (message) {
    return this.perform("speak", { message: message });
  },
});
  • insertAdjacentHTMLとは?

[innerHTML より insertAdjacentHTML を使う - Qiita](https://qiita.com/amamamaou/items/624c22adec32515e863b)

document.querySelector('input[data-behavior="room_speaker"]')
	.addEventListener("keypress", (event) => {
	  if (event.key == "Enter") {
	    this.speak(event.target.value);
	    event.target.value = "";
	    return event.preventDefault();
	  }
	});
  • document.querySelector('input[data-behavior="room_speaker"]'):

ページ内の**data-behavior属性が"room_speaker"であるinput**要素を取得します。

querySelectorとは?

[要素を取得する3つのJSメソッド 〜挙動の違いをまとめてみた〜](https://zenn.dev/harryduck/articles/e3e6c9d37e0169c05096)

  • addEventListener("keypress", (event) => {...}): 取得した**input要素に対してkeypress**イベントリスナーを追加します。ユーザーがキーを押したときに実行されます。
  • if (event.key == "Enter"): 押されたキーが"Enter"キーであるかどうかを確認します。
  • this.speak(event.target.value): **speakメソッドを呼び出し、input**要素の値を引数として渡します。これにより、ユーザーが入力したメッセージがサーバーに送信されます。
  • event.target.value = "": メッセージ送信後に**input**要素の値を空にします。
  • return event.preventDefault(): **Enter**キーのデフォルトの動作を防止します。これにより、フォームの送信や改行が行われません。

preventDefault()とは?

[【JavaScript】event.preventDefault()が何をするのか - Qiita](https://qiita.com/yokoto/items/27c56ebc4b818167ef9e)

サーバーサイドを実装

メッセージをデータベースに保存し、部分テンプレートから生成したHTMLを送信する様にする

class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "room_channel"
    # ストリーム名を固定にして、DBから保存しているメッセージを取得すれば
    # 半永久的にデータは残りそう
  end

  def unsubscribed
  end

  def speak(data)
    message = Message.create!(content: data["message"])
    ActionCable.server.broadcast(
      "room_channel", { message: render_message(message) }
    )
  end

  private

  def render_message(message)
    ApplicationController.render(
      partial: "messages/message",
      locals: { message: message }
    )
  end
end

**ApplicationController.render**メソッドは、Railsアプリケーションでコントローラのコンテキスト外でビューや部分テンプレートをレンダリングするために使用されるメソッドです。このメソッドを使用すると、通常のコントローラアクションからではなく、任意の場所からビューをレンダリングすることができます。

主な用途

  • バックグラウンドジョブでメールのプレビューを作成する
  • サービスオブジェクトやモデルのコールバックで部分テンプレートをレンダリングする
  • WebSocketを介してリアルタイムで更新されるデータをレンダリングする

メソッドのシグネチャ

**ApplicationController.render**メソッドのシグネチャは次の通りです:

ApplicationController.render(options = {}, assigns = {}, &block)

主なオプション

  • :template - レンダリングするテンプレートのパスを指定します。例えば、"users/show"
  • :partial - レンダリングする部分テンプレートのパスを指定します。例えば、"users/user"
  • :locals - テンプレートに渡すローカル変数のハッシュを指定します。例えば、{ user: @user }
  • :formats - テンプレートのフォーマットを指定します。例えば、[:html, :json]
  • :handlers - テンプレートのハンドラを指定します。例えば、[:erb, :haml]
  • :layout - レイアウトテンプレートを指定します。例えば、"layouts/application"

ActionCabelの設定

  • config/cable.yml
development:
  adapter: async
# 非同期アダプターを使用することを指定。非同期アダプターは、
# 開発環境で動作するシンプルなアダプターで、外部のサービス(例えばRedis)を必要としない
# インメモリで接続情報を管理
test:
  adapter: test

production:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
# 環境変数REDIS_URLの値を取得。環境変数が設定されていない場合は、
# デフォルトでredis://localhost:6379/1を使用する。このURLは、ローカルマシンのRedisサーバーに接続するためのものです。
# 6379はRedisのデフォルトポートで、1は使用するデータベース番号を示します。
  channel_prefix: actioncable_sample_production

**config/cable.yml**ファイルは、Railsアプリケーションの3つの異なる環境(development、test、production)ごとにActionCableの設定を定義します。

  • channel_prefix: actioncable_sample_production

channel_prefix は、ActionCableがRedisに格納するデータのキーにプレフィックス(接頭辞)を追加するための設定です。これにより、同じRedisサーバーを共有する複数のRailsアプリケーションや異なる環境のアプリケーション間でデータの競合を防ぐことができます。

具体例

例えば、channel_prefixactioncable_sample_production と設定した場合、ActionCableはRedisにメッセージを保存する際に、このプレフィックスをキーに追加します。

例を使った説明

プレフィックスなしの場合

もし**channel_prefix**が設定されていない場合、デフォルトのキーは次のようになります:

  • action_cable/
  • action_cable/channels/
  • action_cable/broadcasting/

これらのキーは、Redis内で他のアプリケーションや同じアプリケーションの異なる環境と競合する可能性があります。

プレフィックスありの場合

一方、**channel_prefix: actioncable_sample_production**が設定されている場合、キーは次のようにプレフィックス付きで保存されます:

  • actioncable_sample_production:action_cable/
  • actioncable_sample_production:action_cable/channels/
  • actioncable_sample_production:action_cable/broadcasting/

これにより、同じRedisサーバーを共有していても、このアプリケーションのデータは他のデータと混ざらないようになります。

Rackアプリケーションとは?

Rackは、RubyのWebサーバーとWebアプリケーションを接続するためのインターフェースです。簡単に言えば、Webサーバー(Puma、Unicorn、WEBrickなど)とWebアプリケーション(Rails、Sinatraなど)の間の「接着剤」として機能します。Rackは、Webリクエストを処理し、レスポンスを返すためのシンプルで一貫した方法を提供します。

ActionCable専用のサーバープロセスを立ち上げる

デフォルトでは一つのプロセスにWEB用のアプリケーション、WebSockert用のアプリケーションが混在しているので、下記のような問題に遭遇することがある。

  • リソースの競合:

同じプロセスでHTTPリクエストとWebSocket接続を処理すると、リソースが共有され、競合が発生します。

  • スケーラビリティの問題:

異なる特性を持つ処理を同じプロセスで行うと、スケーリングが難しくなります。

  • 応答時間の遅延:

WebSocket接続の増加により、通常のHTTPリクエストの応答時間が遅れる可能性があります。

これらの問題を避けるために、WebサーバーとWebSocketサーバーを分離し、それぞれを異なるプロセスやサーバーで役割を分けるのが良い。

  • Webサーバー:
    • 通常のHTTPリクエストを処理します(例:Webページの読み込みやAPI呼び出し)。
    • 例:NginxやApacheを使用。
  • WebSocketサーバー:
    • WebSocket接続を処理します(例:リアルタイムチャットのメッセージ送受信)。
    • 例:専用のPumaサーバーやActionCable専用サーバーを使用。

スタンドアローン用のスクリプトを作成する

  • cable/config.ru
require_relative "../config/environment"
# ../config/environment" によって、Railsアプリケーションの環境が設定され、
# 初期化プロセスが実行されます。
Rails.application.eager_load!
# eager_load することで、アプリケーションのコードがメモリに事前に読み込まれ、
# 最初のリクエストが処理される際のパフォーマンスが向上します
run ActionCable.server

スタンドアローン環境でActionCableを使用する場合は、

Web用のサーバーにActionCableがマウントされているのを解除する事

下記ファイルのコメントアウトを解除すれば良い。

  • config/environments/production.rb
config.action_cable.mount_path = nil

ActionCableへの接続先を変更する

ActionCable専用のサーバープロセスを立ち上げた場合、一般的には

別のサブドメインで運用することになる。

クライアントからのActionCableへの接続はデフォルトではlocalhostが指定されているので変更する必要がある。

  • config/environments/production.rb
config.action_cable.url = "wss://example.com/cable"

RailsアプリケーションのActionCableのWebSocket接続先URLを指定。

具体的には、クライアントがWebSocket接続を確立するために使用するURLを設定しています。

WebSocketとWebSocket Secure(WSS)

WebSocket(ws)

WebSocketは、双方向のリアルタイム通信を可能にするプロトコルです。通常のHTTPリクエスト/レスポンスのモデルとは異なり、WebSocketではクライアントとサーバーが継続的な接続を確立し、その接続を通じてデータを送受信します。

  • ws: WebSocketプロトコルのスキーム。通常のWebSocket接続を表します。ws:// で始まるURLは、暗号化されていないWebSocket接続を示します。

WebSocket Secure(wss)

WebSocket Secure(WSS)は、WebSocketプロトコルの暗号化されたバージョンです。これにより、データの送受信がTLS(Transport Layer Security)またはSSL(Secure Sockets Layer)によって保護されます。

  • wss: WebSocket Secureプロトコルのスキーム。暗号化されたWebSocket接続を表します。wss:// で始まるURLは、暗号化されたWebSocket接続を示します。

なぜWSSを使用するのか?

  1. セキュリティ:
    • データの盗聴防止: WSSを使用すると、WebSocket通信が暗号化されるため、第三者が通信内容を盗聴することができません。
    • データの改ざん防止: TLS/SSLにより、通信データの改ざんが防止されます。
  2. 信頼性:
    • セキュリティの向上: 多くのネットワークでは、暗号化されていない通信をブロックすることが一般的です。WSSを使用することで、ファイアウォールやプロキシサーバーによるブロックを回避できます。

設定をクライアントサイド全体に反映させる

JavaScriptmの読み込みタグの前にaction_cable_meta_tagを追加

クライアントサイドのActionCableはaction_cable_meta_tagによって生成されたタグを見て接続先を判別する。

  • app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>ActioncableSample</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
 +   <%= action_cable_meta_tag %>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>

上記で生成されるメタタグの例

<meta name="action-cable-url" content="ws://localhost:3000/cable">

このメタタグは、WebSocket接続のURL(例えば、ws://localhost:3000/cable)を指定します。ここで**wsはWebSocketを示し、localhost:3000はサーバーのホスト名とポート、/cable**は接続先のエンドポイントです。

ActionCableへの接続可能なオリジンを設定する

  • オリジンとは?

オリジンは、Webリクエストがどこから来たのかを示す情報で、以下の3つの要素から構成されます:

  1. URIスキーム: 通信プロトコル(例:httphttps
  2. ホスト: ドメイン名またはIPアドレス(例:localhostexample.com
  3. ポート番号: サーバーが使用するポート(例:3000443

例えば、**http://localhost:3000**というURLのオリジンは、以下のように分解されます:

  • スキーム: http
  • ホスト: localhost
  • ポート番号: 3000

ActionCableでのオリジン制限

ActionCableはセキュリティのために、WebSocket接続を許可するオリジンを制限します。これにより、許可されたオリジンからの接続のみを受け付け、不正なオリジンからの接続を防ぎます。

デフォルトの設定(development環境)

Railsの開発環境(development)では、以下のような正規表現が自動的に設定されており、http://localhost:3000 のようなURLからの接続が許可されます:

/https?:\/\/localhost:\d+/

この正規表現の意味は以下の通りです:

  • https?: http または https のスキームを許可します。
  • :\/\/localhost: localhost ホストを許可します。
  • \d+: 任意のポート番号(1桁以上の数字)を許可します。

別のドメインを利用する場合

開発環境以外で、例えば別のドメイン(example.com)から接続を許可する場合や、本番環境(production)で接続を許可する場合は、明示的にオリジンを設定する必要があります。

  • config/environments/production.rb
  config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ 

これは、ActionCableが許可するオリジンのリストを設定するためのオプションです。

配列として設定し、許可するオリジンを文字列で指定します。

ActionCableの認証処理を実装する

上記までで作成したチャットアプリはすべてのユーザーがWebSocketで接続可能になっている。

コネクションを利用するとWebSockets接続の認証許可処理を行うことができる。

Userモデルを作成し、ユーザーがログイン済みの時にCookieへのユーザーIDが設定されている状態を想定。

仮のログイン機能を用意する為に。Userモデルを作成。

rails g model User name
rails db:migrate
rails console

irb> User.create(name: 'チャット太郎')

CookieへユーザーIDを保存する処理をコントローラーへ追加する。

実際のユーザー認証をかくと多くのコードが必要になるのでここでは決め打ちの処理を追加。

cookieへユーザーID保存処理

  • app/controllers/rooms_controller.rb
class RoomsController < ApplicationController
  def show
 +  cookies.signed[:user_id] = User.first.id
    @messages= Message.all
  end
end

CookieへユーザーIDが保存されている仮のログイン機能ができたので

これを元にActionCable側で認証処理を追加

ActionCable側で認証処理を追加

  • app/channels/application_cable/channel.rb
module ApplicationCable
  class Channel < ActionCable::Channel::Base
    identified_by :current_user
    # 接続ごとに一意の識別子を設定 current_userを識別子として使用。
		# current_userは接続ごとにユニークなユーザーオブジェクトを格納します。
		# この識別子を使用して、接続されたユーザーを特定できる
		
    def connect 
	    # クライアントがWebSocket接続を確立したときに呼び出されます。
      self.current_user = find_verified_user
      # find_verified_userメソッドを呼び出して、認証されたユーザーを代入
    end

    private

    def find_verified_user
      if verified_user = User.find_by(id: cookies.signed[:user_id])
      # cookieに格納されたuser_idを使用して、ユーザーをデータベースから検索
      # cookies.signed[:user_id]は、署名付きクッキーからユーザーIDを取得します。
      # 署名付きクッキーは、改ざん防止のために署名されており、安全に使用できます
      
        verified_user
      else
        reject_unauthorized_connection
        # 認証されていない接続を拒否します。認証されていないユーザーはWebSocket接続を確立できません
      end
    end
  end
end
  • ActionCableをサブドメインで運用している場合は両方のドメインでcookieを共有できるように設定する必要がある。
  • identified_byメソッドに引数:current_user渡すとcurrent_userおよび

**current_user=**メソッドが作成される。

[Action Cable の概要 - Railsガイド](https://railsguides.jp/action_cable_overview.html)

ActionCableのワーカー数を設定する

WebSoket経由で受け取ったメッセージはActionCable用のワーカースレッドで処理されます。

デフォルトでは1プロセスにつき最大4つのスレッドが使われる設定になっている

ワーカースレッドとは?

  • スレッド:
    • スレッドは、プロセス内で実行される軽量な実行単位です。スレッドを使うと、同じプログラム内で複数のタスクを同時に実行できます。
  • ワーカースレッド:
    • ワーカースレッドは、特定のタスク(ここではWebSocket経由で受け取ったメッセージの処理)を実行するために使用されるスレッドです。

ActionCable用のワーカースレッド

  • ActionCableは、WebSocket経由で受け取ったメッセージを処理するためにワーカースレッドを使用します。
  • メッセージがサーバーに届くと、それを処理するためにワーカースレッドが割り当てられます。
  • ワーカースレッドは、並行して複数のメッセージを処理することで、リアルタイム通信のパフォーマンスを向上させます。

デフォルト設定: 1プロセスにつき最大4つのスレッド

  • デフォルトでは、1つのActionCableプロセスあたり最大4つのワーカースレッドが使用されるように設定されています。
  • これは、プロセスが同時に4つのメッセージを並行して処理できることを意味します。

例: チャットアプリケーション

  1. シナリオ:
    • ユーザーA、ユーザーB、ユーザーC、ユーザーD、ユーザーEの5人がチャットルームに参加しているとします。
    • それぞれが同時にメッセージを送信します。
  2. メッセージの受信と処理:
    • これらのメッセージはWebSocket経由でサーバーに届きます。
    • サーバー側のActionCableがこれらのメッセージを受け取ります。
  3. ワーカースレッドによる処理:
    • サーバーには4つのワーカースレッドがあるため、最初の4つのメッセージ(ユーザーA、B、C、Dのメッセージ)はそれぞれのスレッドによって同時に処理されます。
    • 5番目のメッセージ(ユーザーEのメッセージ)は、最初の4つのメッセージのいずれかが処理を終えるまで待機します。

スレッドの最大数を増やす

スレッドの最大数を変更するにはproduction.rbに記述すれば良い

  • config/environments/production.rb
config.action_cable.worker_pool_size = 10

config/database.ymlのpoolの数値も変更する。/cableにマウントされている同一プロセスでWebとWebsocketjを提供している時はActionCableのスレッド数とWeb用のスレッド数を足し合わせたもの、or それ以上にする。

WebSocket用のサーバーをWeb用と分離しスタンドアローンで起動している時はそれぞれスレッド数と同じ値、もしくはそれ以上にする

どういうことか?

1. 同じプロセスでWebとWebSocketを提供している場合

  • 同じプロセス: WebサーバーとWebSocketサーバーが同じプロセスで動作している場合、つまり、同じサーバーが両方の役割を果たしている場合です。
  • ActionCableのスレッド数とWeb用のスレッド数を足し合わせたもの:
    • 例えば、Webサーバーが10個のスレッドを使い、ActionCable(WebSocketサーバー)が10個のスレッドを使う場合、合計で20個のスレッドが必要です。
  • データベース接続プールのサイズ:
    • データベース接続プールのサイズを20(10 + 10)に設定することで、同時に20個のデータベース接続を確保します。
    • これにより、すべてのスレッドがデータベースにアクセスできるようになります。

2. WebSocketサーバーをWebサーバーと分離している場合

  • スタンドアローンで起動: WebサーバーとWebSocketサーバーが別々のプロセス(またはサーバー)として動作している場合です。これにより、WebサーバーとWebSocketサーバーが異なるリソースを使って動作します。
  • それぞれのスレッド数と同じ値、もしくはそれ以上:
    • Webサーバーが10個のスレッドを持ち、WebSocketサーバー(ActionCable)が10個のスレッドを持つ場合、各プロセスでデータベース接続プールのサイズを10以上に設定します。

WebSocket

[WebSocketについて調べてみた。 - Qiita](https://qiita.com/south37/items/6f92d4268fe676347160)

⚠️ **GitHub.com Fallback** ⚠️