Skip to content

WebSockets guide

Roman Samoilov edited this page Aug 6, 2024 · 5 revisions

Rage Cable

Rage Cable is an ActionCable-compatible implementation of WebSocket server, allowing real-time communication between the client and the server.

Similar to Action Cable, it routes messages based on "channels" and "streams" using just one underlying WebSocket connection and is fully compatible with @rails/actioncable.

See API docs for more information:

Enabling Rage Cable

Rage Cable is always mounted as a separate Rack application, which gives you the freedom to use it either as part of your application or in standalone mode using a separate process.

To enable Rage Cable, update your config.ru file to mount the RageCable component at the URL of your choice:

map "/cable" do
  run Rage.cable.application
end

You can also use middlewares that are specific to the WebSocket clients:

map "/cable" do
  use MyWebSocketRateLimiter
  run Rage.cable.application
end

Connections

The connection setup is similar to Action Cable - inherit from the Rage::Cable::Connection class to accept or reject connections:

# app/channels/rage_cable/connection.rb

module RageCable
  class Connection < Rage::Cable::Connection
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

    def find_verified_user
      if verified_user = User.find_by(id: cookies.encrypted[:user_id])
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

Inside the connection class, you have access to the request, cookies, and session objects. Also, query parameters can be accessed using the params object.

Channels

A channel represents a specific type of messages coming through the underlying WebSocket connection. Rage::Cable::Channel exposes the transmit and broadcast helpers along with the before_subscribe/after_subscribe hooks and the periodically helper:

# app/channels/chat_channel.rb

class ChatChannel < Rage::Cable::Channel
  def subscribed
    stream_from "chat"
    broadcast("chat", { message: "A new member has joined!" })
  end

  def unsubscribed
    broadcast("chat", { message: "A member has left!" })
  end
end

Migrating from ActionCable

To migrate from ActionCable, mount the Rage.cable.application in your config.ru file and update parent classes for your connections and channels. Rage.cable.broadcast can be used to publish messages to streams.

Benchmarks

The following benchmark shows the ability of both Rage and Rails to handle 10000 concurrent WebSocket connections.

Client application:

  • Opens 10000 connections.
  • Once a connection has been established, it subscribes to the Chat channel and starts sending messages at random intervals between 500ms and 2s.
  • Additionally, once all connections are established, the client starts sending a broadcast message every second.
  • The test is running for five minutes.

Server application:

  • Running two workers on an EC2 m5.large instance.
  • Once a connection is accepted, the application subscribes it to the chat_Best Room stream.
  • Once a regular message comes in, the server responds with the current timestamp.
  • Once the broadcast message comes in, the server broadcasts the current timestamp to the chat_Best Room stream.
class ChatChannel < Rage::Cable::Channel
  def subscribed
    stream_from "chat_#{params[:room]}"
  end

  def receive(data)
    transmit({ i: (Time.now.to_f * 1000).to_i })
  end

  def broadcast_me
    broadcast("chat_#{params[:room]}", { i: (Time.now.to_f * 1000).to_i })
  end
end

Results


Clone this wiki locally