# Action Cable และ WebSockets ใน Rails: คู่มือฉบับสมบูรณ์สำหรับการสัมภาษณ์งาน > เรียนรู้ Action Cable และ WebSockets ใน Rails อย่างละเอียดสำหรับการสัมภาษณ์งาน ครอบคลุม Architecture, Solid Cable, Turbo Streams และคำถามที่พบบ่อย - 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 --- การสื่อสารแบบ Real-time ถือเป็นหัวใจสำคัญของแอปพลิเคชันสมัยใหม่ ไม่ว่าจะเป็นระบบแชท การแจ้งเตือนแบบทันที หรือแดชบอร์ดที่อัปเดตข้อมูลอัตโนมัติ Action Cable เป็นเฟรมเวิร์กที่ Rails มอบให้สำหรับการจัดการการสื่อสารผ่าน WebSockets ได้อย่างมีประสิทธิภาพ บทความนี้จะอธิบายหลักการทำงานของ WebSockets และ Action Cable อย่างครบถ้วน พร้อมทั้งครอบคลุมเนื้อหาที่จำเป็นสำหรับการสัมภาษณ์งานในตำแหน่ง Rails Developer > **จุดสำคัญสำหรับการสัมภาษณ์** > > Action Cable ถูกรวมเข้ากับ Rails ตั้งแต่เวอร์ชัน 5.0 และมีการปรับปรุงอย่างต่อเนื่อง โดยเฉพาะใน Rails 8 ที่เปิดตัว Solid Cable ซึ่งเป็น Database-backed adapter ที่ช่วยลดความซับซ้อนในการตั้งค่าระบบ Real-time ## หลักการพื้นฐานของ WebSocket Protocol WebSocket เป็นโปรโตคอลการสื่อสารที่ทำงานบน TCP connection เดียว โดยมีจุดเด่นที่การสื่อสารแบบ Full-duplex ซึ่งหมายความว่าทั้ง Client และ Server สามารถส่งข้อมูลหากันได้พร้อมกันโดยไม่ต้องรอการร้องขอจากอีกฝ่าย การเชื่อมต่อ WebSocket เริ่มต้นจากกระบวนการ Handshake ผ่าน HTTP ปกติ โดย Client จะส่ง Request พร้อม Header พิเศษที่ระบุว่าต้องการอัปเกรดการเชื่อมต่อเป็น WebSocket หาก Server รองรับและยอมรับ การเชื่อมต่อจะถูกอัปเกรดและคงอยู่ตลอดจนกว่าฝ่ายใดฝ่ายหนึ่งจะปิดการเชื่อมต่อ ข้อแตกต่างที่สำคัญระหว่าง WebSocket และ HTTP แบบดั้งเดิมมีดังนี้: - **HTTP**: ทำงานแบบ Request-Response โดย Client ต้องส่ง Request ทุกครั้งที่ต้องการข้อมูล Server ไม่สามารถส่งข้อมูลไปยัง Client ได้เองโดยไม่มีการร้องขอ - **WebSocket**: เมื่อเชื่อมต่อแล้ว ทั้งสองฝ่ายสามารถส่งข้อมูลหากันได้ตลอดเวลา ลด Overhead จากการสร้าง Connection ใหม่ซ้ำแล้วซ้ำเล่า ข้อดีของ WebSocket ในบริบทของแอปพลิเคชัน Real-time ได้แก่ Latency ที่ต่ำกว่ามาก เนื่องจากไม่ต้องสร้าง Connection ใหม่สำหรับทุก Message และการใช้ทรัพยากร Network ที่มีประสิทธิภาพกว่าเทคนิค Polling แบบเดิม ## สถาปัตยกรรมของ Action Cable Action Cable ประกอบด้วยสามส่วนหลักที่ทำงานร่วมกัน ได้แก่ Connections, Channels และ Subscriptions แต่ละส่วนมีหน้าที่เฉพาะทางที่ช่วยให้การจัดการ WebSocket มีความเป็นระเบียบและปลอดภัย ### Connection Connection เป็นจุดเริ่มต้นของการเชื่อมต่อ WebSocket ทุกครั้ง ทำหน้าที่ยืนยันตัวตนของผู้ใช้และเก็บข้อมูลที่จำเป็นสำหรับการทำงานตลอดช่วงเวลาที่เชื่อมต่อ ```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 ``` การยืนยันตัวตนใน Connection นั้นสำคัญมาก เนื่องจาก WebSocket ไม่ได้ส่ง Session หรือ Authentication Token แบบอัตโนมัติเหมือน HTTP Request ทั่วไป การใช้ Encrypted Cookies เป็นวิธีที่นิยมและปลอดภัยในการระบุตัวตนผู้ใช้ ### Channel Channel เป็นหน่วยลอจิกที่จัดกลุ่มฟังก์ชันการทำงานที่เกี่ยวข้องกัน เปรียบเสมือน Controller ในรูปแบบ MVC แต่สำหรับการสื่อสาร WebSocket ```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 ``` สิ่งที่ควรสังเกตในโค้ดด้านบนคือ: - **Authorization ใน subscribed**: การตรวจสอบสิทธิ์ก่อนอนุญาตให้ Subscribe เป็นหลักปฏิบัติที่สำคัญด้านความปลอดภัย - **stream_for**: Method นี้สร้าง Stream ที่ผูกกับ Object เฉพาะ ทำให้การ Broadcast ไปยังห้องแชทที่ถูกต้องเป็นเรื่องง่าย - **Cleanup ใน unsubscribed**: การจัดการทรัพยากรเมื่อผู้ใช้ตัดการเชื่อมต่อช่วยป้องกันปัญหา Memory Leak และข้อมูลสถานะที่ไม่ถูกต้อง ## การ Subscribe จากฝั่ง Client ฝั่ง Client ใช้ JavaScript Consumer เพื่อสร้างและจัดการ Subscription ไปยัง Channel ต่างๆ ```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 }) } } ) ``` Lifecycle Callbacks ที่สำคัญประกอบด้วย: - **connected()**: ถูกเรียกเมื่อ Subscription พร้อมใช้งาน เหมาะสำหรับการโหลดข้อมูลเริ่มต้นหรือแสดงสถานะการเชื่อมต่อ - **disconnected()**: ถูกเรียกเมื่อการเชื่อมต่อถูกปิด สามารถใช้แสดง UI ที่บ่งบอกว่าระบบออฟไลน์ - **received(data)**: ถูกเรียกทุกครั้งที่มีข้อมูลถูก Broadcast มายัง Channel นี้ ## Solid Cable ใน Rails 8 Rails 8 เปิดตัว Solid Cable ซึ่งเป็น Database-backed Adapter สำหรับ Action Cable แทนที่จะต้องพึ่งพา Redis หรือ External Service อื่นๆ Solid Cable ใช้ฐานข้อมูลเป็นที่เก็บ Message Queue ```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 ``` สำหรับ Production Environment ที่ต้องการแยกฐานข้อมูลสำหรับ Cable ออกจากฐานข้อมูลหลัก สามารถตั้งค่าได้ดังนี้: ```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 ได้แก่: - **ลดความซับซ้อนในการ Deploy**: ไม่ต้องตั้งค่าและดูแล Redis Server แยกต่างหาก - **ความเรียบง่าย**: เหมาะสำหรับแอปพลิเคชันขนาดเล็กถึงกลางที่ไม่ต้องการ Scale แบบ Distributed - **การตั้งค่าที่ยืดหยุ่น**: สามารถกำหนด Polling Interval และ Message Retention ได้ตามต้องการ อย่างไรก็ตาม สำหรับแอปพลิเคชันที่ต้องรองรับผู้ใช้จำนวนมากหรือต้องการ Low Latency สูงสุด Redis ยังคงเป็นตัวเลือกที่เหมาะสมกว่า ## รูปแบบการ Broadcasting การ Broadcast ข้อมูลไปยัง Channel สามารถทำได้จากหลายจุดในแอปพลิเคชัน ขึ้นอยู่กับความต้องการและบริบทของการใช้งาน ```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 ``` แนวทางปฏิบัติที่ดีในการ Broadcasting: - **ใช้ after_create_commit แทน after_create**: การ Broadcast หลังจาก Transaction ถูก Commit แล้วช่วยให้มั่นใจว่าข้อมูลถูกบันทึกจริงก่อนส่งไปยัง Client - **ย้ายงานหนักไป Background Job**: การคำนวณที่ซับซ้อนควรทำใน Job แยกต่างหาก เพื่อไม่ให้บล็อก Main Thread - **ใช้ Channel Naming Convention ที่ชัดเจน**: การตั้งชื่อที่สื่อความหมายช่วยให้การดูแลรักษาโค้ดง่ายขึ้น ## การผสานกับ Turbo Streams Turbo Streams เป็นส่วนหนึ่งของ Hotwire ที่ทำงานร่วมกับ Action Cable ได้อย่างลงตัว ช่วยให้การอัปเดต DOM แบบ Real-time เป็นเรื่องง่ายโดยไม่ต้องเขียน JavaScript เอง ```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 %>