Action Cable in Rail 5

Realtime là một khái niệm được biết đến rộng rãi trong thời đại công nghệ hiện nay, được ứng dụng trong nhiều lĩnh vực từ image processing, IOT,.. và web cũng vậy, nó được sử dụng trong các chức năng tạo thông báo hay khi bạn chat, bạn nhận được những thông tin mới một cách rất nhanh chóng.

HTTP và Websockets

Đối với HTTP, kết nối giữa server và client thì khá là ngắn: đầu tiên client request một resource trên server thì một kết nối đến server sẽ được thiết lập và resource được yêu cầu(có thể là JSON, HTML, XML,…) sẽ được truyền về cho client như là một response. Sau đó, kết nối sẽ bị đóng. Vậy làm thế nào để client nhận biết được data trên server thay đổi, thông thường thì client sẽ request để nhận biết sự thay đổi đó trong một thời gian nhất định. Không giống như HTTP, WebSockets là giao thức cho phép các client và server luông kết nối liên tục với nhâu để truyền data. Các client đăng kí channel với server và khi có thay đổi data thì server sẽ broadcasts data đó đến tất cả các client đã đăng kí channel này. Bằng cách này, cả server và client đều cậ nhật trạng thái mới nhất của data và có thể dễ dàng đồng bộ hóa các xuất hiện các thay đổi. Các controller của Rails được xây dựng nhằm mục đích xử lý các request HTTP và Rails đã đưa ra một giải pháp để handle việc tích hợp xử lý Websockets. Rails 5 sẽ có thêm một thư mục mới bên trong thư mục app gọi là channels. Channels hoạt động như các controller xử lý các request Websockets bằng cách đóng gói các logic thành các đơn vị đặc thù, ví dụ như là chat messages hoặc notifications, các client đăng kí các channels để truyền tải dữ liệu.

  • Ưu điểm: -Đối với WebSockets việc cung cấp tới khả năng giao tiếp hai chiều rất mạnh mẽ, có độ trẽ thấp cũng như dẽ dàng xử lý khi phát sinh lỗi -Dung lượng của một kết nối bằng WebSocket sẽ ít hơn rất nhiều so với một HTTP Request.
  • Nhược điểm: -Do WebSocket là một tính năng đặc biệt của HTML5 chính vì thế nó vẫn chưa nhận được sự hỗ trợ của tất cả các trình duyệt.

Action Cable

Action Cable là một tính năng tích hợp WebSocket của Rails. Sử dụng Javascript ở phía Client và Ruby ở phía Server. để thực hiện việc real-time. Vậy là mình đã giới thiệu sơ qua về các khái niệm, bây giờ sẽ đến phần sử dụng trong ứng dụng thông báo.

Views

#app/views/notifications/_notification.html.erb
<li>
  <%= notification.event %>
  <span> <%= notification.created_at.strftime("%d %b. %Y") %></span>
</li>
#app/views/notifications/_notifications.html.erb
<ul class="notificaton">
  <div id="notificationContainer">
    <div id="notificationTitle">Notifications</div>
    <div id="notificationsBody" class="notifications">
      <ul id="notificationList">
        <%= render notifications %>
      </ul>
    </div>
    <div id="notificationFooter"></div>
  </div>
</ul>

2 view này sẽ hiện thị thông báo.

<%= render "notifications", notifications: @notifications %>

gọi render này đến những header hay những view nào bạn muốn hiển thị thông báo.

Notification

Server

tạo một kênh ứng với mục đích sử dụng của mình. Ở đây mình đặt tên là NotificationChannel.

#app/channels/notification_channel.rb
class NotificationChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notification_channel"
  end

  def unsubscribed; end
end

Sau khi yêu cầu từ Client đã được chấp nhận, thì Server sẽ thực hiện hàm subscriebed để thực hiện tạo một kênh riêng để trao đối dữ liệu với Client. Hàm unsubscribed để hủy bỏ kênh.

#model/notification.rb
after_create :push_notify

private

  def push_notify
    ActionCable.server.broadcast "notification_channel",
      notification: render_notification self
  end
  
  def render_notification notification
    ApplicationController.renderer.render partial: "notifications/notification",
      locals: {notification: notification}
  end

Ở có 2 dữ liệu cần chú ý là tên của kênh và dữ liệu truyền vào kênh. vậy là đã xong việc phần server.

Client

  • 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
// /cable.js
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
//= 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
$(document).ready(function() {
  (function() {
    App.notifications = App.cable.subscriptions.create({
      channel: 'NotificationChannel'
    },
    {
      connected: function() {},
      disconnected: function() {},
      received: function(data) {
        current_user = parseInt($('#current-user-id').val());
        $('.notificationList').prepend('' + data.notification);
        $counter = $('.counter-notification').text();
        val = parseInt($counter);
        val++;
        $('.counter-notification').text(val);
      },
    });
  }).call(this);
});

Tạo kết nối tới kênh mà ta muốn kết nối. Ở đây là NotificationChannel. Dữ liệu nhận về sẽ thông qua biến data ở hàm received.

Settings

Trong routes:

#routes.rb
mount ActionCable.server => "/cable"

Và cho js:

//= require cable

Vậy là đã xong công việc thao tác gửi nhận rồi 😄

Nguồn tham khảo

https://github.com/rails/rails/tree/master/actioncable http://guides.rubyonrails.org/action_cable_overview.html

All Rights Reserved