Action Cable และ WebSockets ใน Rails: คู่มือฉบับสมบูรณ์สำหรับการสัมภาษณ์งาน

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

Action Cable และ WebSockets ใน Rails สำหรับการสัมภาษณ์งาน

การสื่อสารแบบ 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 ต่างๆ

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 })
    }
  }
)

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

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 %>
<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 ยังคงเป็นมาตรฐานที่แนะนำ

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

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

แท็ก

#action-cable
#websockets
#rails
#real-time
#solid-cable

แชร์

บทความที่เกี่ยวข้อง

Rails API Mode RESTful Serialization

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 พร้อมระบบประมวลผล job และ caching แบบ database-backed

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: ฟีเจอร์ใหม่และคู่มือการอัปเกรดฉบับสมบูรณ์ 2026

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