+1

WebSockets trong Rails 5

Rails 5 là ON THE HORIZON (một chân trời mới). Có rất nhiều tính năng mới trong phiên bản mới này, nhưng tôi ấn tượng nhất là về sự hỗ trợ tích hợp WebSockets.

Giới thiệu về WebSockets

Trong trường hợp bạn đang còn lạ lẫm với Websockets, hãy để tôi giải thích cho bạn hiểu. Trong một ứng dụng Web truyền thống, trình duyệt sẽ gửi một yêu cầu từ một trang web và máy chủ sẽ trả về một trang web mới. Khi trang web được load lại, kết nối sẽ bị đóng. Điều này sẽ rất tuyệt vời nếu các trang không bao giờ thay đổi. Nhưng lại có vấn đề khi có rất nhiều cập nhật mới lên máy chủ. Chỉ có một cách để trình duyệt nhận những thay đổi này là gửi những yêu cầu khác nhau và nhận về những trả lời khác nhau. WebSockets giải quyết được vấn đề này. Trong một ứng dụng WebSockets, thay vì tạo ra một yêu cầu, thì trình duyệt sẽ tạo ra một kết nối đến máy chủ. Kết nối này là liên tục và nó có tính chất 2 chiều. Tại bất kỳ thời điểm nào trong kết nối, máy chủ có thể gửi dữ liệu đến trình duyệt và ngược lại trình duyệt có thể gửi dữ liệu đến máy chủ.

Action cable

WebSockets đã có từ vài năm trở lại đây và có rất nhiều ứng dụng đã sử dụng WebSockets. Thực tế, bạn có thể sử dụng WebSockets trong Rails 4 thông qua một vài gem khác nhau. Còn trong rails 5 cung cấp built-in support thông qua Action Cable. Yêu cầu một vài initial setup cần được thực hiện và bạn có thể tìm thấy thông tin chi tiết tại đây. Tôi sẽ không nói đến các setup ở bài viết này, bởi vì nếu làm vậy thì nội dung bài viết sẽ thay đổi và đây cũng không phải những gì mà tôi đang muốn nói. Những gì mà tôi đang muốn nói là làm thế nào Action Cable có thể sử dụng để thêm vào thời gian thực cho ứng dụng Rails. Nhìn tổng quan, Action Cable được chia làm 2 thành phần chính. Một là phần Back-end, chỉ là một class của Ruby được kế thừa từ ApplicationCable::Chanel. Phần còn lại là Font-end là một đối tượng javascript với các chức năng cụ thể được xác định.

Tạo một ứng dụng dog barking (chó sủa)

Trong ví dụ này, tôi muốn tạo một ứng dụng để cho phép những con chó có thể phát sóng tiếng sủa của nó để bất kỳ ai cũng có thể nghe được. Điều đầu tiên chúng ta cần làm là thiết lập một chanel. Để làm được điều này, chúng ta cần tạo ra một class trong Ruby và kế thừa từ class ApplicationCable::Channel. Chúng ta sẽ đặt tên cho chanel là BarkChannel:

class BarkChannel < ApplicationCable::Channel
  def subscribed
    stream_from "barks"
  end
end

Việc tiếp theo chúng ta cần làm là thiết lập phương thức subscribed của chúng ta. Phương thức sẽ được gọi khi một kết nối được thực hiện. Bên trong phương thức, chúng ta sẽ gọi stream_from "barks". Để nói với back-end rằng kết nối này sẽ nhận được tất cả các thông báo trên chanel "barks". Tiếp theo chúng ta phải thiết lập các font-ed để tạo ra kết nối đến máy chủ. Điều này được thực hiện trong CoffeeScript sau đây:

App.dogs = App.cable.subscriptions.create "BarkChannel",
connected: ->;

disconnected: ->;

Phần quan trọng của đoạn code này là App.cable.subscriptions.create "BarkChannel". Khai báo này tạo ra kết nối đến máy chủ, thông qua Action Cable channel DogChannel. Các hàm connecteddisconnected sẽ được gọi đến khi mà chúng ta bắt đầu và kết thúc quá trình kết nối đến máy chủ. Tiếp theo, chúng ta sẽ cần giao diện người dùng cho phép dog có thể nhập vào tiếng sủa. Nhưng tôi sẽ bỏ qua phần này bởi vì nó quá đơn giản. Chúng ta sẽ gọi đến hàm bark trên đối tượng App.dogs:

App.dogs = App.cable.subscriptions.create "BarkChannel",
connected: ->;

disconnected: ->;

bark: ->;
var myDogsId = 1;
@perform("bark", {id: myDogsId})

Trong hàm bark, lần đầu sẽ được set một id cho dog của chúng ta. Trong ứng dụng thực tế, giá trị có thể được truyền lên từ back-end khi kết nối lần đầu tiên được thiết lập. Nhưng để đơn giản, tôi viết mã code luôn ở đây. Sau đó, chúng ta sẽ gọi đến @perform, nó như là một magic của câu lệnh Action Cable, để nói rằng nó sẽ gửi data ({id: myDogsId}) đến phía back-end của chanel, sử dụng phương thức bark. Do đó chúng ta sẽ thiết lập method bark như sau:

class BarkChannel < ApplicationCable::Channel
def subscribed
  stream_from "barks"
end

def bark data 
  my_dog = Dog.find_by id: data[:id]
  my_dog.bark
end

Ở đây chúng ta đang tìm kiếm Dog với id cụ thể. Sau đó, chúng ta sẽ gọi hàm bark để xử lý logic. Tại thời điểm này, chúng ta sẽ tạo ra kết nối đầy đủ từ phía font-end đến back-end và có thể làm bất kỳ điều gì. Trong ứng dụng của chúng ta, chúng ta muốn phát sóng sự kiện bark đến tất cả các dog khác đang lắng nghe. Sử dụng phương thức bark chúng ta sẽ làm được điều này như sau:

class Dog
  def bark
    ActionCable.server.broadcast "barks", {id: id}
  end
end

ActionCable.server.broadcast là phương thức sử dụng để gửi một thông điệp đến tất cả các khách hàng trên chanel barks. Chúng ta cũng gửi một hash có chứa id của dog gửi đi bark. Việc cuối cùng chúng ta cần làm là viết code trên font-end để xử lý thông điệp phát sóng. Để làm được điều này, chúng ta sử dụng function received:

App.dogs = App.cable.subscriptions.create "BarkChannel",
connected: ->;

disconnected: ->;

received: (data) ->;
# Handle the bark

bark: ->;
var myDogsId = 1
@perform("bark", {id: myDogsId})

Kết luận

Cuối cùng, chúng ta đã hoàn thành request cycle, từ font-end đến back-end và ngược lại. Tuy nhiên để ró ràng hơn, việc giao tiếp không cần phải khởi tạo bên client. Thực sự đây là những điều làm cho WebSockets trở nên tuyệt vời đến vậy, server có thể gửi thông điệp đến client bất kỳ lúc nào. Do đó, một trang web truyền thông xã hội có thể ngay lập tức thông báo cho những người xử dụng những tin tức mới. Hoặc một ứng dụng email trên web có thể thông báo cho người sử dụng email mà ko cần refresh lại trang.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.