Action Cable และ WebSockets ใน Rails: คู่มือฉบับสมบูรณ์สำหรับการสัมภาษณ์งาน
เรียนรู้ Action Cable และ WebSockets ใน Rails อย่างละเอียดสำหรับการสัมภาษณ์งาน ครอบคลุม Architecture, Solid Cable, Turbo Streams และคำถามที่พบบ่อย

การสื่อสารแบบ 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 ทุกครั้ง ทำหน้าที่ยืนยันตัวตนของผู้ใช้และเก็บข้อมูลที่จำเป็นสำหรับการทำงานตลอดช่วงเวลาที่เชื่อมต่อ
# 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
# 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 ต่างๆ
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 })
}
}
)Lifecycle Callbacks ที่สำคัญประกอบด้วย:
- connected(): ถูกเรียกเมื่อ Subscription พร้อมใช้งาน เหมาะสำหรับการโหลดข้อมูลเริ่มต้นหรือแสดงสถานะการเชื่อมต่อ
- disconnected(): ถูกเรียกเมื่อการเชื่อมต่อถูกปิด สามารถใช้แสดง UI ที่บ่งบอกว่าระบบออฟไลน์
- received(data): ถูกเรียกทุกครั้งที่มีข้อมูลถูก Broadcast มายัง Channel นี้
พร้อมที่จะพิชิตการสัมภาษณ์ Ruby on Rails แล้วหรือยังครับ?
ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ
Solid Cable ใน Rails 8
Rails 8 เปิดตัว Solid Cable ซึ่งเป็น Database-backed Adapter สำหรับ Action Cable แทนที่จะต้องพึ่งพา Redis หรือ External Service อื่นๆ Solid Cable ใช้ฐานข้อมูลเป็นที่เก็บ Message Queue
# 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 ออกจากฐานข้อมูลหลัก สามารถตั้งค่าได้ดังนี้:
# 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 สามารถทำได้จากหลายจุดในแอปพลิเคชัน ขึ้นอยู่กับความต้องการและบริบทของการใช้งาน
# 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 เอง
# 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>การใช้ 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 ยังคงเป็นมาตรฐานที่แนะนำ
# 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
# 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
เริ่มฝึกซ้อมเลย!
ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ
แท็ก
แชร์
บทความที่เกี่ยวข้อง

Rails API Mode ปี 2026: สร้าง RESTful API ด้วย Serialization, Authentication และคำถามสัมภาษณ์งาน
เจาะลึก Rails 8 API Mode สร้าง RESTful API ด้วย Alba, JWT Authentication และ RSpec พร้อมคำถามสัมภาษณ์ปี 2026

Solid Queue และ Solid Cache ใน Rails 8: คู่มือสมบูรณ์สำหรับเตรียมสัมภาษณ์งาน 2026
เจาะลึก Solid Queue และ Solid Cache ระบบ database-backed ที่เป็นค่าเริ่มต้นใน Rails 8 ครอบคลุมสถาปัตยกรรม การตั้งค่า concurrency controls และความรู้ที่จำเป็นสำหรับการสัมภาษณ์งานด้านเทคนิคปี 2026

Ruby on Rails 8: ฟีเจอร์ใหม่และคู่มือการอัปเกรดฉบับสมบูรณ์ 2026
คู่มือฉบับสมบูรณ์สำหรับ Ruby on Rails 8 ครอบคลุมฟีเจอร์ใหม่ทั้งหมด ได้แก่ Solid Queue, Solid Cache, ระบบ Authentication ในตัว, Propshaft, Kamal 2 พร้อมขั้นตอนการอัปเกรดจาก Rails 7 อย่างละเอียด