Rails 5 Action Cable
Bài đăng này đã không được cập nhật trong 3 năm
Để 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
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
- Cài đặt Rails 5 và RVM latest => http://railsapps.github.io/installrubyonrails-ubuntu.html
- Chú ý câu lệnh
sudo apt-get update
phải success không gây ra lỗi. Nếu lỗi tham khảo => http://askubuntu.com/questions/65911/how-can-i-fix-a-404-error-when-using-a-ppa-or-updating-my-package-lists
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/
All rights reserved