# 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", `
${data.user}: ${data.body}
` ) }, 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 %>
<%= render @chat_room.messages %>
``` การใช้ `broadcasts_to` ใน Model ทำให้ Rails จัดการการ Broadcast อัตโนมัติเมื่อมีการสร้าง อัปเดต หรือลบ Record โดย Turbo Streams จะส่ง HTML Partial ที่พร้อมแทรกเข้าไปใน DOM ทันที ข้อดีของการใช้ Turbo Streams กับ Action Cable: - **Progressive Enhancement**: ทำงานร่วมกับ Server-side Rendering ได้ดี - **ลดการเขียน JavaScript**: ไม่ต้องจัดการ DOM Manipulation ด้วยตนเอง - **รูปแบบ Broadcasting ที่หลากหลาย**: รองรับ append, prepend, replace, update และ remove ## การ Scale ใน Production สำหรับแอปพลิเคชันที่ต้องรองรับผู้ใช้จำนวนมาก การใช้ Redis เป็น Adapter ยังคงเป็นมาตรฐานที่แนะนำ ```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 ``` ข้อพิจารณาสำคัญสำหรับการ Scale: - **Horizontal Scaling**: Redis ทำหน้าที่เป็น Message Broker ที่ช่วยให้ Application Server หลายตัวสามารถ Share State ของ WebSocket Subscriptions ได้ - **Connection Limits**: แต่ละ Process มีขีดจำกัดจำนวน Connection ที่รองรับได้ ควรวางแผนและทดสอบ Load ก่อน Deploy - **Channel Prefix**: การใช้ Prefix ช่วยป้องกันการชนกันของ Channel Name เมื่อใช้ Redis ร่วมกันหลายแอปพลิเคชัน - **Connection Pooling**: การตั้งค่า Pool Size ที่เหมาะสมช่วยเพิ่มประสิทธิภาพการเชื่อมต่อกับ Redis > **เคล็ดลับสำหรับการสัมภาษณ์** > > ในการสัมภาษณ์ ผู้สัมภาษณ์มักถามเกี่ยวกับการตัดสินใจเลือกระหว่าง Solid Cable และ Redis คำตอบที่ดีควรอธิบายถึง Trade-offs ระหว่างความเรียบง่ายกับความสามารถในการ Scale และแสดงให้เห็นว่าเข้าใจบริบทการใช้งานที่เหมาะสมของแต่ละตัวเลือก ## การทดสอบ Channels Rails มี Testing Framework ที่ครบครันสำหรับการทดสอบ Action Cable Channels ```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 ``` แนวทางการทดสอบที่ครอบคลุม: - **ทดสอบ Authorization**: ตรวจสอบว่าผู้ใช้ที่ไม่มีสิทธิ์ถูกปฏิเสธอย่างถูกต้อง - **ทดสอบ Stream Setup**: ยืนยันว่า Stream ถูกสร้างสำหรับ Object ที่ถูกต้อง - **ทดสอบ Broadcasting**: ตรวจสอบว่าข้อมูลถูก Broadcast ไปยัง Channel ที่ถูกต้องพร้อมรูปแบบข้อมูลที่คาดหวัง ## คำถามสัมภาษณ์ที่พบบ่อย การเตรียมตัวสำหรับคำถามเกี่ยวกับ Action Cable และ WebSockets ควรครอบคลุมหัวข้อต่อไปนี้: **1. อธิบายความแตกต่างระหว่าง HTTP Polling, Long Polling และ WebSocket** HTTP Polling คือการส่ง Request ซ้ำๆ ตามช่วงเวลาที่กำหนด ซึ่งสิ้นเปลืองทรัพยากรเมื่อไม่มีข้อมูลใหม่ Long Polling คือการค้าง Request ไว้จนกว่าจะมีข้อมูลใหม่หรือ Timeout แต่ยังต้องสร้าง Connection ใหม่ทุกครั้ง WebSocket คือ Persistent Connection ที่เปิดค้างไว้และทั้งสองฝ่ายส่งข้อมูลหากันได้ตลอดเวลา **2. Action Cable จัดการ Authentication อย่างไร** การ Authentication เกิดขึ้นใน Connection Class ระหว่าง WebSocket Handshake โดยทั่วไปใช้ Encrypted Cookies ที่ถูกตั้งค่าไว้ตอน HTTP Login เนื่องจาก Cookies ถูกส่งมาพร้อมกับ Handshake Request **3. เมื่อไหร่ควรใช้ Solid Cable และเมื่อไหร่ควรใช้ Redis** Solid Cable เหมาะสำหรับแอปพลิเคชันขนาดเล็กถึงกลางที่ต้องการความเรียบง่ายในการ Deploy และไม่ต้องการ Horizontal Scaling Redis เหมาะสำหรับแอปพลิเคชันที่มีผู้ใช้จำนวนมาก ต้องการ Low Latency สูงสุด หรือต้อง Scale แบบ Distributed **4. อธิบายการทำงานของ stream_from และ stream_for** `stream_from` รับชื่อ Stream เป็น String โดยตรง เหมาะสำหรับกรณีที่ต้องการควบคุมชื่อ Stream เอง `stream_for` รับ Object และสร้างชื่อ Stream อัตโนมัติจาก Class Name และ ID ทำให้การ Broadcast ไปยัง Object เฉพาะง่ายขึ้น **5. จะจัดการกับการ Reconnection อย่างไร** Action Cable มีกลไก Reconnection อัตโนมัติในฝั่ง Client แต่ควรออกแบบแอปพลิเคชันให้รองรับกรณีที่ผู้ใช้พลาดข้อความระหว่างที่ตัดการเชื่อมต่อ เช่น การโหลดข้อความล่าสุดเมื่อ Connect สำเร็จ > **ข้อควรระวัง** > > ในการสัมภาษณ์ ควรหลีกเลี่ยงการตอบแบบท่องจำ ผู้สัมภาษณ์มักต้องการเห็นความเข้าใจในหลักการและความสามารถในการประยุกต์ใช้กับสถานการณ์จริง การยกตัวอย่างจากประสบการณ์หรืออธิบาย Trade-offs ของแต่ละแนวทางจะช่วยสร้างความประทับใจได้ดีกว่า ## บทสรุป Action Cable และ WebSockets เป็นเทคโนโลยีที่สำคัญสำหรับการพัฒนาแอปพลิเคชัน Real-time ใน Rails ความเข้าใจอย่างถ่องแท้ในหัวข้อเหล่านี้จะช่วยให้ประสบความสำเร็จทั้งในการสัมภาษณ์งานและการพัฒนาแอปพลิเคชันจริง สิ่งสำคัญที่ควรจดจำ: - WebSocket ให้การสื่อสารแบบ Full-duplex ที่มี Latency ต่ำกว่า HTTP Polling - Action Cable แบ่งโครงสร้างออกเป็น Connections, Channels และ Subscriptions ที่ทำงานร่วมกัน - การ Authentication ต้องทำใน Connection Class ระหว่าง Handshake - Rails 8 เปิดตัว Solid Cable สำหรับการตั้งค่าที่ง่ายขึ้นโดยไม่ต้องพึ่ง Redis - Turbo Streams ช่วยลดความซับซ้อนในการอัปเดต DOM แบบ Real-time - การทดสอบ Channels ควรครอบคลุมทั้ง Authorization, Stream Setup และ Broadcasting - การเลือกระหว่าง Solid Cable และ Redis ขึ้นอยู่กับขนาดและความต้องการของแอปพลิเคชัน การฝึกฝนและทำความเข้าใจแนวคิดเหล่านี้จะช่วยเตรียมความพร้อมสำหรับการสัมภาษณ์งานในตำแหน่ง Rails Developer ได้เป็นอย่างดี [ฝึกฝนคำถามสัมภาษณ์ Action Cable & WebSockets](/th/technologies/ruby-on-rails/interview-questions/action-cable-websockets) --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/ruby-on-rails/action-cable-websockets-complete-guide