# Action Cable và WebSockets trong Rails: Hướng dẫn toàn diện cho phỏng vấn kỹ thuật > Hướng dẫn chi tiết về Action Cable và WebSockets trong Rails để chuẩn bị phỏng vấn kỹ thuật. Kiến trúc, Solid Cable Rails 8, Turbo Streams và các câu hỏi thường gặp. - Published: 2026-05-07 - Updated: 2026-05-07 - Author: Anthony Fillion-Maillet - Tags: action-cable, websockets, rails, real-time, solid-cable - Reading time: 12 min --- Trong thế giới phát triển web hiện đại, khả năng truyền tải dữ liệu theo thời gian thực đã trở thành một yêu cầu thiết yếu. Từ các ứng dụng chat, thông báo đẩy đến cập nhật dashboard trực tiếp, người dùng ngày nay kỳ vọng trải nghiệm mượt mà và tức thì. Action Cable là giải pháp tích hợp sẵn của Ruby on Rails để xử lý WebSockets, cho phép các nhà phát triển xây dựng các tính năng thời gian thực một cách thanh lịch và hiệu quả. Bài viết này sẽ khám phá sâu về kiến trúc Action Cable, các mẫu thiết kế phổ biến và những cải tiến quan trọng trong Rails 8 với Solid Cable. > **Điểm mấu chốt cho phỏng vấn** > > Action Cable là một trong những chủ đề nâng cao thường xuất hiện trong các buổi phỏng vấn cho vị trí Senior Rails Developer. Hiểu rõ về WebSockets và cách Rails xử lý giao tiếp thời gian thực sẽ giúp ứng viên nổi bật trong các cuộc phỏng vấn kỹ thuật. Rails 8 giới thiệu Solid Cable, adapter dựa trên database giúp loại bỏ yêu cầu Redis cho pub/sub messaging. ## Nền tảng giao thức WebSocket Trước khi đi sâu vào Action Cable, việc hiểu rõ giao thức WebSocket là điều cần thiết. Khác với HTTP truyền thống hoạt động theo mô hình request-response, WebSocket thiết lập một kết nối hai chiều liên tục giữa client và server. Điều này có nghĩa là server có thể chủ động gửi dữ liệu đến client mà không cần client phải gửi yêu cầu trước. Giao thức WebSocket bắt đầu bằng một HTTP handshake, sau đó nâng cấp kết nối lên giao thức WebSocket. Kết nối này duy trì mở cho đến khi một trong hai bên đóng nó. Điều này mang lại hiệu suất vượt trội so với các kỹ thuật như polling hay long-polling, vì không cần thiết lập kết nối mới cho mỗi lần truyền dữ liệu. Trong bối cảnh Rails, Action Cable trừu tượng hóa sự phức tạp của WebSocket, cung cấp một API quen thuộc theo phong cách Rails. Các nhà phát triển có thể tập trung vào logic nghiệp vụ thay vì phải xử lý các chi tiết cấp thấp của giao thức. ## Kiến trúc Action Cable Action Cable được xây dựng trên ba thành phần chính: Connections, Channels và Subscriptions. Mỗi thành phần đóng vai trò riêng biệt trong việc quản lý giao tiếp thời gian thực. ### Connections Connection đại diện cho kết nối WebSocket giữa client và server. Đây là nơi xác thực người dùng và thiết lập định danh cho kết nối. Mỗi kết nối WebSocket tương ứng với một instance của Connection class. ```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 ``` Trong ví dụ trên, phương thức `connect` được gọi khi một kết nối WebSocket mới được thiết lập. Việc xác thực thực hiện thông qua cookies, vì cookies được gửi cùng với WebSocket handshake. Nếu không tìm thấy người dùng hợp lệ, kết nối bị từ chối thông qua `reject_unauthorized_connection`. ### Channels Channels là các đơn vị logic nơi công việc thực sự diễn ra. Mỗi channel xử lý một loại tính năng cụ thể, ví dụ như ChatChannel cho tin nhắn, NotificationChannel cho thông báo. Channels định nghĩa cách xử lý subscription, receiving data và broadcasting. ```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 ``` Channel này minh họa nhiều khái niệm quan trọng. Phương thức `subscribed` thực hiện kiểm tra quyền truy cập trước khi cho phép subscription. Phương thức `stream_for` tạo một stream động dựa trên object cụ thể. Phương thức `receive` xử lý dữ liệu gửi từ client và broadcast đến tất cả subscribers. Phương thức `unsubscribed` thực hiện cleanup khi kết nối đóng. ### Subscriptions Subscriptions đại diện cho mối quan hệ giữa client và một channel cụ thể. Một connection có thể có nhiều subscriptions đến các channels khác nhau. Điều này cho phép một trang web có thể đồng thời nhận cập nhật chat, thông báo và dữ liệu dashboard thông qua một kết nối WebSocket duy nhất. ## Subscriptions phía Client Phía client, Action Cable cung cấp một JavaScript framework để quản lý subscriptions và xử lý dữ liệu nhận được. ```javascript // app/javascript/channels/chat_channel.js 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", `
` ) }, sendMessage(body) { // Calls ChatChannel#receive on the server this.perform("receive", { body: body }) } } ) ``` Consumer là điểm khởi đầu cho tất cả các kết nối WebSocket trong ứng dụng. Phương thức `subscriptions.create` tạo một subscription mới với các callbacks để xử lý các sự kiện khác nhau. Callback `connected` được gọi khi subscription sẵn sàng. Callback `received` xử lý dữ liệu broadcast từ server. Các phương thức tùy chỉnh như `sendMessage` có thể gọi các action trên server thông qua `this.perform`. ## Solid Cable trong Rails 8 Rails 8 giới thiệu Solid Cable như một adapter mặc định mới cho Action Cable, thay thế Redis trong nhiều trường hợp sử dụng. Solid Cable sử dụng cơ sở dữ liệu quan hệ để lưu trữ messages, mang lại sự đơn giản trong việc triển khai mà không cần phụ thuộc vào dịch vụ bên ngoài. ```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 ``` Cấu hình trên cho thấy Solid Cable có thể sử dụng một database riêng biệt cho messages, giữ cho database chính không bị ảnh hưởng bởi tải của WebSocket traffic. Tùy chọn `polling_interval` kiểm soát tần suất kiểm tra messages mới, và `message_retention` xác định thời gian lưu trữ messages. ```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 ``` Việc sử dụng multi-database cho phép tách biệt hoàn toàn giữa dữ liệu ứng dụng và dữ liệu Action Cable. Điều này cải thiện hiệu suất và giảm nguy cơ xung đột về tài nguyên database. Solid Cable phù hợp cho các ứng dụng vừa và nhỏ, hoặc trong môi trường không muốn phụ thuộc vào Redis. Tuy nhiên, với các ứng dụng có lưu lượng WebSocket rất cao, Redis vẫn là lựa chọn được khuyến nghị do hiệu suất vượt trội của in-memory data store. ## Các mẫu Broadcasting Broadcasting là cách server gửi dữ liệu đến tất cả subscribers của một channel hoặc stream cụ thể. Rails cung cấp nhiều cách tiếp cận linh hoạt cho việc broadcasting. ```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 ``` Ví dụ đầu tiên cho thấy broadcasting trực tiếp từ model callback, phù hợp cho các tác vụ nhẹ và tức thì. Ví dụ thứ hai sử dụng background job, phù hợp cho các tác vụ nặng như tính toán thống kê phức tạp. Việc sử dụng `after_create_commit` thay vì `after_create` đảm bảo rằng broadcast chỉ xảy ra sau khi transaction database hoàn tất thành công. Điều này tránh tình trạng gửi dữ liệu về records chưa thực sự được persist. ## Tích hợp Turbo Streams Turbo Streams là một phần của Hotwire, cung cấp cách tiếp cận khai báo để cập nhật DOM thông qua WebSockets. Kết hợp với Action Cable, Turbo Streams cho phép xây dựng các tính năng thời gian thực với code tối thiểu. ```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 %>