Karn Tirasoontorn

May 29, 2021

สร้าง WebSocket Channels ร่วมกับ Connection ของ Turbo

การสร้าง Real-time Web Application คงหลีกเลี่ยงไม่ได้กับการใช้ WebSocket เพื่อให้เกิดการสื่อสารแบบสองทาง (Bi-direction Communication) เพื่อให้ฝั่ง Backend สามารถอัพเดตข้อมูลให้กับ Frontend ได้ สำหรับ Rails ก็มี ActionCable เพื่อจัดการในส่วนของ WebSocket ให้กับเรา

ที่นี้โจทย์เรามีอยู่ว่าในกรณีที่เรามีการใช้ turbo_stream_from จาก Turbo ซึ่งจะมีการสร้าง WebSocket Connection ขึ้นมาอยู่แล้ว 1 ช่อง และถ้าเราอยากจะให้เพิ่มช่องทางให้ในการส่งข้อมูลอื่นๆ ยกตัวอย่างเช่น ข้อมูล chat, ข้อมูลสถานะ เราจะสร้าง Connection ใหม่ขึ้นมา

// chat_controller.js
import { Controller } from "stimulus"
import consumer from '../channels/consumer'
export default class extends Controller {
  connect() {
    this.channel = consumer.subscriptions.create(      
      { channel: 'ChatChannel', room: 'Funny' },      
      {        
        connected: this.cableConnected.bind(this),       
        disconnected: this.cableDisconnected.bind(this),
        received: this.cableReceived.bind(this)      
      }    
  )
  ...
}
 
# chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_#{params[:room].split.join.underscore}"
  end
end

เมื่อเรารันโปรแกรม และเปิดดูใน Inspector ในส่วนของ Network และกรองดูเฉพาะ WS ก็จะพบว่า WebSocket Connection ที่ถูกสร้างขึ้นมีตั้ง 2 ช่องด้วยกัน


  1. ช่องทางที่สร้างจาก Turbo
  2. ช่องทางที่สร้างจาก ActionCable

แน่นอนว่าเว็บแอพพลิเคชันของเราจะต้องเปิด Connection ถึง 2 ช่อง ทำให้ Server ก็ต้องแบกรับภาระมากขึ้น รวมถึงข้อจำกัดจำนวน Connection ที่ Server ตัวหนึ่งๆ จะรองรับ ทำให้ต้องเกิดการขยับขยายขึ้น เพื่อเป็นการเพิ่มประสิทธิภาพให้กับ Server อย่างน้อยเราก็ขอพยายาม Optimize Connection กันหน่อย โดยพยายามรวบ Connection ให้เหลือช่องทางเดียว เพียงเราเปลี่ยนจากการสร้าง Channel จาก ActionCable มาเป็นการสร้าง Channel จาก Turbo 

import { Controller } from "stimulus"
import { subscribeTo } from '@hotwired/turbo-rails/app/javascript/turbo/cable'
export default class extends Controller { async connect() { await delay(200) this.channel = subscribeTo( { channel: "ChatChannel", room: "Best Room" }, { connected: this.cableConnected.bind(this), disconnected: this.cableDisconnected.bind(this), received: this.cableReceived.bind(this) } ) ) }

จากโค้ดข้างต้นจะพบว่าเราจะเรียกใช้งาน subscribeTo ซึ่งเป็นฟังก์ชันที่ใช้สำหรับสร้าง Channel ของ Turbo โดยใช้ Connection เดิมที่ Turbo ได้สร้างสขึ้นมา เพียงเท่านี้เราก็ Optimize ให้ Server ของเราได้แล้ว

collaborating_connection.png