Rails 5 Action Cable

Để hiểu rõ về Action Cable trong Rails 5 chúng ta cần đi qua và hiểu về WebSocket là gì

I. WebSocket

  • WebSoket là công nghệ hỗ trợ giao tiếp hai chiều giữa client và server bằng cách sử dụng một TCP socket để tạo một kết nối hiệu quả và ít tốn kém. Mặc dù được thiết kế để chuyên sử dụng cho các ứng dụng web, lập trình viên vẫn có thể đưa chúng vào bất kì loại ứng dụng nào.
  • WebSockets cho phép các kênh giao tiếp song song hai chiều và hiện đã được hỗ trợ trong nhiều trình duyệt (Firefox, Google Chrome và Safari). Kết nối được mở thông qua một HTTP request (yêu cầu HTTP), được gọi là liên kết WebSockets với những header đặc biệt. Kết nối được duy trì để bạn có thể viết và nhận dữ liệu bằng JavaScript như khi bạn đang sử dụng một TCP socket đơn thuần.
  • Dữ liệu truyền tải thông qua giao thức HTTP (thường dùng với kĩ thuật Ajax) chứa nhiều dữ liệu không cần thiết trong phần header.

II. Action Cable

  • ActionCable là một bước đi mới quan trọng của Rails, nó cho phép developer chuyển cách request/response sang một cách mới mà ở đó việc kết nối giữa client và Rails server được duy trì. Đây là một kiểu kết nối stateful không giống như HTTP request. chi phí, độ trễ liên quan đến việc đẩy dữ liệu trong thời gian thực được giảm đáng kể so với HTTP.

  • Action Cable có thể chạy độc lập với server, hoặc chúng ta có thể cấu hình cho nó chạy chính process của nó bên trong ứng dụng chính của server.

  • ActionCable sử dụng Rack Socket Hijacking API để tiếp quản việc điều khiển kết nối từ ứng dụng server. ActionCable sau đó sẽ quản lý việc kết nối một cách riếng rẽ, đa luồng, nhiều kênh

  • Một instance của Action Cable được tạo sử dụng Rack để mở và duy trì việc kết nối, và sử dụng một kênh gắn kết trên một sub-URI trong ứng dụng để stream từ những phần nhất định trong ứng dụng và broadcaset tới những phần khác.

  • ActionCable cung cấp server-side code để broadcast nội dung nhất định ( new message hay notification) thông qua kênh "channel" tới một "subscriber". Subscriber này được khởi tạo từ phía clint-side với một hàm JS sử dụng JQuery để append nội dung vào DOM.

  • ActionCable sử dụng Redis để lưu trữ dữ liệu, đồng bộ nội dung thông qua các instances của ứng dụng

II.1 Pub & Sub

  • Đầy đủ là Publisher và Subscriber: Là việc dùng cơ chế hàng đợi gửi message từ một abstract class của subscriber mà không cần đến một bên nhận cụ thể. ActionCable dùng phương pháp này để giao tiếp giữa client và server.

II. 2 Server side components

II.2.1 Connection

  • Connection được hình thành thì mối quan hệ giữa client-server. Mỗi khi server chấp nhận websocket thì một đối tượng connection sẽ được khởi tạo. Đối tượng này trở thành cha của tất cả các channel subscription. Bản thân connection sẽ không thực hiện bất kỳ một logic nào khác ngoại trừ việc xác thực và ủy quyền. Client của Websocket connection được gọi là connection consumer.
  • Connection là những instances của ApplicationCable::Connection, ở đây việc chứng thực và xử lý thiết lập connect sẽ được thực hiện.

II.2.2 Channels

  • Một channel được gói gọi trong một đơn vị logic, Được setup giống như những controller thông thường trong mô hình MVC. Mặc định thì Rails sẽ tạo một class cha cho việc đóng gói chia sẻ logic giữa các channels ApplicationCable::Channel

# app/channels/application_cable/channel.rb
module ApplicationCable
  class Channel < ActionCable::Channel::Base
  end
end

II.3 Client side component

II.3.1 connection

  • Consumers sẽ yêu cầu một instance của connection phía client, điều này có thể được thực hiện bằng cách sử dụng Javascript, nó sẽ mặc định được tạo ra bởi Rails.

  • Connection consumers: Connect với server dựa vào /cable, Việc kết nối sẽ không được thiết lập cho tới khi bạn có ít nhất một subscription

// app/assets/javascripts/cable.js
//= require action_cable
//= require_self
//= require_tree ./channels

(function() {
  this.App || (this.App = {});

  App.cable = ActionCable.createConsumer();
}).call(this);
  • Một consumer trở thành một subscriber bằng cách đăng ký vào một channel, Một consumer có thể hành xử như một subscriber để đăng ký nhiều channel. Ví dụ một consumer có thể subscriber tới nhiều phòng chat khác nhau tại cùng một thời điểm

II.4. Tương tác giữa client side và server side

actioncable_flow1.png

II.4.1 Stream

  • Stream cung cấp cơ chế định tuyến channel để pulished nội dung tới subscribers.
class ChatChannel < ApplicationCable::Channel
  def subscribed
    stream_from "chat_#{params[:room]}"
  end
end

  • Nếu Stream liên quan đến model thì việc sử dụng broadcast có thể được tạo ra từ model và channel.

II.4.2 Broadcast

  • Broadcast là một link pub/sub ở đó tất cả mọi thứ truyền bởi một publisher định tuyến trực tiếp tới một những channel subscribers. Một channel có thể không stream tới broadcast hoặc nhiều broadcast.
  • Broadcast hoàn toàn là một hàng đợi và phụ thuộc vào thời gian. Nếu một consumer không đăng ký một channel chúng sẽ không được broadcast.
class MessageBroadcastJob < ApplicationJob
  queue_as :default

  def perform(message)
    ActionCable.server.broadcast 'room_channel', message: render_message(message)
  end

  private
  def render_message(message)
    ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
  end
end

III. Tạo chatapp

III.1 Cài đặt môi trường

III.2 Cột

  • Đầu tiên bạn cần phải enable cable bằng cách update config/routes.rb và javascript/cable.coffee file và update action_cable_meta_tag cho layout
    #routes.rb
        mount ActionCable.server => '/cable
    //cable.coffee file
        #= require action_cable
        #= require_self
        #= require_tree ./channels

        @App ||= {}
        App.cable = ActionCable.createConsumer()
    //layouts/application.html.erb
    // bên trong thẻ head
        <%= action_cable_meta_tag %>
  • Tạo channel room
        rails g channel room speak
  • Tạo message thông qua ActionCable client side
     //app/assets/javascripts/channels/room.coffee
        speak: (message) ->
            @perform 'speak', message: message
Gửi message JSON object tới method `speak` RoomChannel server-side
 RoomChanel server-side
     class RoomChannel < ApplicationCable::Channel
          def subscribed
            # stream_from "some_channel"
             stream_from "room_channel"
          end
          def speak(data)
            Message.create! content: data['message']
          end
        end
  • Về cơ bản thì RoomChannel là môi trường để ActionCable lấy data từ client, nhận data từ subscribed từ hàm recieve
      received: (data) ->
        $('#messages').append data['message']

      $(document).on 'keypress', '[data-behavior~=room_speaker]', (event) ->
      if event.keyCode is 13
        App.room.speak event.target.value
        event.target.value = ''
        event.preventDefault()
  • broadcast cho chat message
    #models/message.rb
    class Message < ApplicationRecord
      after_create_commit { MessageBroadcastJob.perform_later self }
    end
`after_create_commit` message sẽ được broadcast khi message được lưu vào trong db.
  • Tạo MessageBroadcastJob
        class MessageBroadcastJob < ApplicationJob
          queue_as :default

          def perform(message)
            ActionCable.server.broadcast 'room_channel', message: render_message(message)
          end

          private
          def render_message(message)
            ApplicationController.renderer.render(partial: 'messages/message', locals: { message: message })
          end
        end
`perform` method sẽ nhận message và render ra view

source code https://github.com/khanhhd/chatapp

IV. Nguồn tham khảo

http://tutorials.pluralsight.com/ruby-ruby-on-rails/creating-a-chat-using-rails-action-cable

https://www.sitepoint.com/action-cable-and-websockets-an-in-depth-tutorial/

https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable#building-a-real-time-chat-app-with-action-cable


All Rights Reserved