Action Cable e WebSocket in Rails: Guida Completa per Colloqui Tecnici 2026

Approfondimento su Action Cable per colloqui Rails. Connessioni, channel, Solid Cable, Turbo Streams, scalabilità con Redis e pattern di test con esempi di codice.

Action Cable e WebSocket in Rails

Action Cable integra il supporto WebSocket direttamente nel framework Rails, abilitando funzionalità in tempo reale come chat, notifiche e dashboard live senza dipendenze esterne. Per i colloqui tecnici del 2026, la padronanza degli aspetti interni di Action Cable — dal ciclo di vita delle connessioni alla scalabilità in produzione — rappresenta un vantaggio decisivo rispetto agli altri candidati.

Concetto Chiave per i Colloqui

Action Cable integra i WebSocket nel framework Rails utilizzando le stesse convenzioni impiegate per controller e model. Rails 8 introduce Solid Cable, un adapter basato su database che elimina la dipendenza da Redis per il messaging pub/sub.

Fondamenti del Protocollo WebSocket nel Contesto Rails

Il protocollo WebSocket (RFC 6455) stabilisce un canale di comunicazione persistente e full-duplex su una singola connessione TCP. A differenza dei cicli request-response HTTP, i WebSocket mantengono una connessione aperta in cui sia il client che il server possono inviare messaggi in qualsiasi momento.

Action Cable racchiude questo protocollo in un'astrazione compatibile con Rails. Il server gestisce gli upgrade WebSocket su /cable, amministra l'autenticazione delle connessioni e instrada i messaggi attraverso i channel. La libreria JavaScript lato client crea e gestisce le subscription automaticamente.

Una tipica richiesta HTTP si completa in millisecondi e chiude la connessione. Una connessione WebSocket rimane aperta per minuti, ore o l'intera durata della sessione. Questa differenza fondamentale guida ogni decisione architetturale in Action Cable.

Architettura di Action Cable: Connection, Channel e Subscription

Action Cable segue un'architettura stratificata con tre astrazioni principali: le connection gestiscono l'autenticazione, i channel incapsulano la logica di business, e le subscription collegano i consumer a channel specifici.

ruby
# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

    def find_verified_user
      # Cookies are available in WebSocket handshake
      if verified_user = User.find_by(id: cookies.encrypted[:user_id])
        verified_user
      else
        reject_unauthorized_connection
      end
    end
  end
end

La classe connection viene eseguita una sola volta per ogni handshake WebSocket. L'autenticazione avviene qui — non nei singoli channel. La dichiarazione identified_by registra l'identità dell'utente, rendendola disponibile in tutte le subscription dei channel su quella connessione.

ruby
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    room = ChatRoom.find(params[:room_id])
    # Authorization: verify user belongs to this room
    if room.members.include?(current_user)
      stream_for room
    else
      reject
    end
  end

  def receive(data)
    message = ChatMessage.create!(
      room_id: params[:room_id],
      user: current_user,
      body: data["body"]
    )
    ChatChannel.broadcast_to(
      message.room,
      { id: message.id, body: message.body, user: current_user.name }
    )
  end

  def unsubscribed
    # Cleanup: mark user as offline
    AppearanceTracker.mark_offline(current_user)
  end
end

I channel definiscono tre callback del ciclo di vita: subscribed, receive e unsubscribed. Il metodo stream_for associa la subscription a una specifica istanza del model, creando uno stream con namespace. Il broadcasting su quello stream consegna i messaggi a ogni subscriber connesso.

Subscription Lato Client con JavaScript

Il consumer lato client si connette al server Action Cable e gestisce le subscription. Ogni subscription corrisponde a un channel lato server.

app/javascript/channels/chat_channel.jsjavascript
import consumer from "./consumer"

const chatChannel = consumer.subscriptions.create(
  { channel: "ChatChannel", room_id: roomId },
  {
    connected() {
      // Called when the subscription is ready
      console.log("Connected to chat room", roomId)
    },

    disconnected() {
      // Called when the subscription is closed
      console.log("Disconnected from chat room")
    },

    received(data) {
      // Called when data is broadcast to the channel
      const messageList = document.getElementById("messages")
      messageList.insertAdjacentHTML("beforeend",
        `<div class="message"><strong>${data.user}</strong>: ${data.body}</div>`
      )
    },

    sendMessage(body) {
      // Calls ChatChannel#receive on the server
      this.perform("receive", { body: body })
    }
  }
)

Il callback received si attiva ogni volta che il server trasmette allo stream sottoscritto. Il metodo perform invia dati dal client al server, invocando il metodo corrispondente del channel.

Pronto a superare i tuoi colloqui su Ruby on Rails?

Pratica con i nostri simulatori interattivi, flashcards e test tecnici.

Solid Cable in Rails 8: Pub/Sub Basato su Database

Rails 8 introduce Solid Cable come parte della "Solid Trifecta" insieme a Solid Queue e Solid Cache. Solid Cable sostituisce Redis come backend pub/sub memorizzando i messaggi in una tabella del database.

yaml
# config/cable.yml (Rails 8 default)
development:
  adapter: solid_cable

production:
  adapter: solid_cable
  connects_to:
    database:
      writing: cable
  polling_interval: 0.1.seconds
  message_retention: 1.day

Solid Cable funziona scrivendo ogni messaggio broadcast in una tabella del database e interrogando periodicamente per nuovi messaggi a un intervallo configurabile. L'intervallo di polling predefinito di 100ms fornisce una consegna quasi in tempo reale per la maggior parte delle applicazioni.

Il compromesso è chiaro: Solid Cable elimina una dipendenza infrastrutturale (Redis) al costo di una latenza leggermente superiore e maggiore carico sul database. Per le applicazioni che già utilizzano PostgreSQL o MySQL, questo compromesso è spesso sensato. Per il broadcasting ad alta frequenza (migliaia di messaggi al secondo), Redis rimane la scelta migliore.

ruby
# config/database.yml (Rails 8 multi-database)
production:
  primary:
    <<: *default
    database: myapp_production
  cable:
    <<: *default
    database: myapp_cable_production
    migrations_paths: db/cable_migrate

Solid Cable utilizza un database dedicato per evitare contesa con il database primario. Questa separazione impedisce al polling dei messaggi di interferire con le query dell'applicazione.

Pattern di Broadcasting e Trigger Lato Server

Il broadcasting da model, job e controller copre i tre pattern più comuni nelle applicazioni Rails in produzione.

ruby
# Broadcasting from a model callback
class Notification < ApplicationRecord
  belongs_to :user
  after_create_commit :broadcast_to_user

  private

  def broadcast_to_user
    ActionCable.server.broadcast(
      "notifications_#{user_id}",
      { id: id, title: title, read: false }
    )
  end
end

# Broadcasting from a background job
class DashboardUpdateJob < ApplicationJob
  queue_as :default

  def perform(dashboard_id)
    dashboard = Dashboard.find(dashboard_id)
    stats = dashboard.compute_stats
    ActionCable.server.broadcast(
      "dashboard_#{dashboard_id}",
      { stats: stats, updated_at: Time.current.iso8601 }
    )
  end
end

I callback dei model sono adatti per notifiche semplici attivate da modifiche ai record. I background job gestiscono calcoli più pesanti — come statistiche dashboard o aggregazioni di dati — senza bloccare il ciclo di richiesta. Il broadcast stesso è sempre leggero: serializza il payload e lo pubblica attraverso l'adapter.

Turbo Streams e Integrazione con Action Cable

Rails 8 con Hotwire utilizza Action Cable come livello di trasporto per i Turbo Streams, abilitando aggiornamenti DOM in tempo reale senza scrivere JavaScript personalizzato.

ruby
# app/models/message.rb
class Message < ApplicationRecord
  belongs_to :chat_room
  # Automatically broadcasts append to subscribers
  broadcasts_to :chat_room
end

# app/views/chat_rooms/show.html.erb
<%= turbo_stream_from @chat_room %>
<div id="messages">
  <%= render @chat_room.messages %>
</div>

La macro broadcasts_to genera callback after_create, after_update e after_destroy che trasmettono frammenti Turbo Stream via Action Cable. L'helper turbo_stream_from lato client si sottoscrive allo stream corrispondente. La creazione di un nuovo messaggio aggiunge automaticamente il suo partial renderizzato al DOM di ogni client connesso.

Questo pattern riduce le funzionalità in tempo reale a una dichiarazione nel model e un helper nella view — nessun channel personalizzato, nessun handler JavaScript, nessuna manipolazione manuale del DOM.

Scalabilità di Action Cable in Produzione

I deployment in produzione richiedono considerazioni attente sulla scelta dell'adapter, sui limiti di connessione e sulla scalabilità orizzontale.

ruby
# config/cable.yml — Redis adapter for high-scale production
production:
  adapter: redis
  url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
  channel_prefix: myapp_production

Redis funge da backbone pub/sub, garantendo che i messaggi pubblicati su un processo Rails raggiungano i subscriber connessi a qualsiasi altro processo. Senza Redis (o Solid Cable), l'adapter async limita il broadcasting a un singolo processo — inutilizzabile in qualsiasi deployment multi-processo o multi-server.

I limiti di connessione dipendono dal server. Puma con Action Cable gestisce le connessioni WebSocket sullo stesso processo delle richieste HTTP. Ogni connessione WebSocket mantiene una connessione persistente, consumando un thread (o una fiber in Ruby 3.x con scheduler basati su fiber). Una configurazione tipica di Puma con 5 thread e 4 worker può gestire circa 200 connessioni WebSocket simultanee prima della saturazione.

Per le applicazioni che richiedono migliaia di connessioni simultanee, AnyCable sostituisce il server WebSocket Ruby con un server basato su Go, gestendo le connessioni a livello di protocollo mentre instrada la logica dei channel verso Rails tramite gRPC. Questa architettura supporta oltre 10.000 connessioni simultanee per nodo.

Spunto per il Colloquio

Gli intervistatori chiedono frequentemente i limiti di scalabilità di Action Cable. La risposta chiave: Action Cable in sé non è il collo di bottiglia — lo è il processo Ruby che gestisce le connessioni WebSocket. AnyCable risolve questo problema delegando la gestione delle connessioni a Go mantenendo la logica dei channel in Ruby.

Test dei Channel di Action Cable

Rails fornisce ActionCable::Channel::TestCase per gli unit test dei channel e ActionCable::Connection::TestCase per il test dell'autenticazione delle connessioni.

ruby
# test/channels/chat_channel_test.rb
require "test_helper"

class ChatChannelTest < ActionCable::Channel::TestCase
  test "subscribes to a valid room" do
    room = chat_rooms(:general)
    user = users(:alice)
    stub_connection current_user: user

    subscribe room_id: room.id

    assert subscription.confirmed?
    assert_has_stream_for room
  end

  test "rejects subscription for unauthorized user" do
    room = chat_rooms(:private)
    user = users(:outsider)
    stub_connection current_user: user

    subscribe room_id: room.id

    assert subscription.rejected?
  end

  test "broadcasts messages to room subscribers" do
    room = chat_rooms(:general)
    stub_connection current_user: users(:alice)
    subscribe room_id: room.id

    assert_broadcast_on(room, hash_including(body: "Hello")) do
      perform :receive, body: "Hello"
    end
  end
end

Il metodo stub_connection configura il contesto della connessione senza un WebSocket reale. assert_has_stream_for verifica l'associazione allo stream. assert_broadcast_on cattura i broadcast all'interno di un blocco, confermando che il payload corretto raggiunga lo stream giusto.

Domande Frequenti nei Colloqui su Action Cable

I colloqui tecnici su Action Cable verificano tipicamente la comprensione del protocollo, delle decisioni architetturali e delle problematiche di produzione.

Come autentica Action Cable le connessioni? L'autenticazione avviene in ApplicationCable::Connection#connect durante l'handshake WebSocket. I cookie dalla sessione HTTP sono disponibili in questo momento. L'autenticazione basata su token passa il token come parametro query nell'URL WebSocket e lo valida in connect.

Cosa succede quando una connessione WebSocket si interrompe? La libreria client implementa la riconnessione automatica con backoff esponenziale. Lato server, unsubscribed viene attivato per ogni channel a cui la connessione era sottoscritta. La pulizia dello stato (segnare utenti offline, rilasciare lock) appartiene a unsubscribed.

Quando utilizzare Solid Cable invece di Redis? Solid Cable è adatto per applicazioni con esigenze di tempo reale moderate (sotto 100 messaggi/secondo) che vogliono evitare l'infrastruttura Redis. Redis rimane necessario per scenari ad alto throughput o quando è richiesta una latenza di consegna inferiore a 10ms.

Errore Comune

Inserire la logica di autorizzazione nella classe connection invece che nei singoli channel. La connection autentica l'identità (Chi è questo utente?). I channel autorizzano l'accesso (Questo utente può accedere a questa stanza?). Mescolare queste responsabilità crea vulnerabilità di sicurezza.

Conclusione

  • Action Cable racchiude il protocollo WebSocket nelle convenzioni Rails con connection, channel e subscription come astrazioni principali
  • L'autenticazione appartiene a ApplicationCable::Connection; l'autorizzazione appartiene ai callback subscribed dei singoli channel
  • Solid Cable in Rails 8 elimina la dipendenza da Redis utilizzando il database per il pub/sub — adatto per applicazioni con throughput moderato
  • I Turbo Streams sfruttano Action Cable per aggiornamenti DOM automatici in tempo reale con codice minimale
  • La scalabilità in produzione richiede Redis o Solid Cable per deployment multi-processo; AnyCable gestisce oltre 10.000 connessioni delegando la gestione WebSocket a Go
  • Il test dei channel utilizza stub_connection, assert_has_stream_for e assert_broadcast_on — nessuna connessione WebSocket reale necessaria
  • Esercitarsi con le domande su ActionCable e WebSocket per consolidare questi concetti con esercizi mirati

Inizia a praticare!

Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.

Tag

#ruby-on-rails
#action-cable
#websockets
#real-time
#interviews

Condividi

Articoli correlati