Việc xây dựng các tính năng thời gian thực trong Rails đã trở nên dễ dàng hơn rất nhiều với các thư viện như Action Cable. Trong tập này của Học viện AppSignal, chúng ta sẽ đi sâu vào cập nhật theo thời gian thực và chơi đùa với việc xây dựng một máy chủ WebSocket tối thiểu để xem nó hoạt động như thế nào.
Chúng tôi sẽ xây dựng một ứng dụng đẩy dữ liệu và sử dụng Pub / Sub qua WebSocket . Trước khi bắt đầu viết mã, trước tiên chúng ta hãy dành một chút thời gian để trình bày ý nghĩa của ba khái niệm đó:
-
Đẩy đề cập đến việc đẩy dữ liệu đến người nhận thay vì yêu cầu người nhận thăm dò dữ liệu đó. A phải có trong các bản cập nhật theo thời gian thực như giá cổ phiếu, ứng dụng trò chuyện hoặc bảng điều khiển hoạt động.
-
Pub / Sub hoặc xuất bản và đăng ký là một mô hình tương tác để đẩy dữ liệu được TIBCO phổ biến trên Phố Wall vào những năm 1990. Người nhận đăng ký một chủ đề và đợi nhà xuất bản đẩy dữ liệu đến chủ đề đó. Thông thường bao gồm đối sánh mẫu ký tự đại diện để khớp thông báo đã xuất bản với người nghe, mặc dù một số triển khai đơn giản hơn chỉ sử dụng các kênh được đặt tên thay vì ký tự đại diện trong chủ đề. Tôi đã bắt đầu làm việc tại TIBCO trong những ngày đầu tiên đó nên tôi thích sự linh hoạt của khớp mẫu ký tự đại diện.
-
WebSocket là một giao thức để trao đổi dữ liệu - thường là giữa trình duyệt web và ứng dụng. Kết nối HTTP được nâng cấp thành kết nối WebSocket và sau đó dữ liệu có thể được gửi theo cả hai cách giữa hai điểm cuối. WebSockets có thể đẩy dữ liệu từ ứng dụng sang trình duyệt. Nó cũng cung cấp một cơ chế khác ngoài POST hoặc PUT để gửi dữ liệu từ mã JavaScript của bạn trong trình duyệt trở lại ứng dụng. Khá đẹp phải không?
Nâng cao
Hãy xem ví dụ về máy chủ WebSocket có thể hoạt động như thế nào. Từ trình duyệt, máy khách cố gắng tạo kết nối WebSocket với máy chủ có mã JavaScript.
var sock = new WebSocket("ws://" + document.URL.split("/")[2] + "/upgrade");
Máy chủ nhận được một yêu cầu HTTP với chỉ báo rằng một bản nâng cấp đã được yêu cầu. Nói chung, máy chủ cho phép ứng dụng quyết định có nâng cấp hay không. Nó hoạt động như thế nào phụ thuộc vào API được cung cấp cho ứng dụng. Máy chủ hỗ trợ Rack cung cấp một tùy chọn để chiếm quyền điều khiển socket và cho phép nhà phát triển xử lý tất cả các chi tiết về giao thức hoặc theo PR đề xuất, phản hồi để nâng cấp là đủ.
Nâng cấp là một tập hợp các trao đổi giữa máy chủ và máy khách. Tất cả các trình duyệt và một số máy chủ gems đều ẩn những chi tiết này. Sau khi kết nối được thiết lập, các tin nhắn có thể được trao đổi theo giao thức WebSocket.
Phép thuật bên dưới xử lý mã hóa, giải mã và giao thức trao đổi tin nhắn. Thông báo là cấu trúc nhị phân, chiều rộng cố định với trọng tải theo sau, được mã hóa bằng SHA1. Giao thức WebSocket bao gồm một số loại tin nhắn và trao đổi, như nhịp tim ping / pong và mở và đóng các cuộc trao đổi tin nhắn. Đó là điều kỳ diệu mà các máy chủ thực hiện bằng cách không sử dụng phương pháp tấn công kết nối.
Đang lặn
Chúng tôi sẽ sử dụng ví dụ về một chuỗi đồng hồ được bắt đầu để xuất bản thời gian hiện tại cho tất cả các khách hàng đang lắng nghe. Chúng tôi sẽ sử dụng Agoo để xây dựng máy chủ của mình vì nó nhanh và giữ mức độ phức tạp ở mức tối thiểu.
Chúng tôi sẽ bắt đầu với một số JavaScript với tư cách là một ứng dụng khách bằng cách hiển thị thời gian hiện tại trên một trang HTML. Sau khi tạo một WebSocket
mới một onopen
callback được thiết lập để thay đổi phần tử HTML trạng thái. onmessage
gọi lại cập nhật message
Phần tử HTML. Gọi lại là một mẫu thiết kế phổ biến khi làm việc với các lệnh gọi không đồng bộ, chẳng hạn như xuất bản và đăng ký trao đổi.
<!-- websocket.html -->
<html>
<body>
<p id="status">...</p>
<p id="message">... waiting ...</p>
<script type="text/javascript">
var sock = new WebSocket(
"ws://" + document.URL.split("/")[2] + "/upgrade"
);
sock.onopen = function () {
document.getElementById("status").textContent = "connected";
};
sock.onmessage = function (msg) {
document.getElementById("message").textContent = msg.data;
};
</script>
</body>
</html>
Với ứng dụng khách của chúng ta đã hoàn tất, hãy triển khai máy chủ, đây là một ứng dụng Ruby sử dụng API Rack. Clock
chính lớp sẽ là một trình xử lý cho tất cả các yêu cầu HTTP trên /upgrade
đường dẫn. Nếu yêu cầu nâng cấp, chúng tôi chỉ trả về Thành công với mã trạng thái HTTP là 200, nếu không, chúng tôi trả về 404 cho Không tìm thấy trang. Bước khác duy nhất trong #call
phương thức là sự chỉ định của trình xử lý WebSocket.
class Clock
def self.call(env)
unless env['rack.upgrade?'].nil?
env['rack.upgrade'] = Clock
[ 200, { }, [ ] ]
else
[ 404, { }, [ ] ]
end
end
end
API dựa trên các lệnh gọi lại. Lệnh gọi lại duy nhất mà chúng tôi quan tâm đối với máy chủ của mình là #on_open
gọi lại cho phép chúng tôi tạo đăng ký cho chủ đề "thời gian". Tin nhắn được trao đổi qua các kênh được xác định theo chủ đề hoặc chủ đề. #on_open
được gọi khi kết nối ổ cắm web được thiết lập.
class Clock
# ...
def self.on_open(client)
client.subscribe('time')
end
end
Bây giờ, hãy bắt đầu xuất bản với một chủ đề xuất bản thời gian sau mỗi giây. Cuộc gọi đến Agoo.publish
gửi tin nhắn về chủ đề "thời gian", sau đó tất cả các thuê bao đều nhận được tin nhắn. Máy chủ theo dõi các đăng ký và kết nối và gửi thông báo đến máy khách JavaScript để cập nhật phần tử HTML.
require 'agoo'
Thread.new {
loop do
now = Time.now
Agoo.publish('time', "%02d:%02d:%02d" % [now.hour, now.min, now.sec])
sleep(1)
end
}
Mã khác duy nhất cần thiết là mã khởi tạo và khởi động máy chủ. Lệnh gọi đến Agoo::Server.handle(:GET, '/upgrade', Clock)
yêu cầu máy chủ lắng nghe các yêu cầu HTTP GET trên /upgrade
Đường dẫn URL và để chuyển các yêu cầu đó đến Clock
lớp. Điều này cho phép việc định tuyến diễn ra bên ngoài Ruby để cải thiện hiệu suất và tính linh hoạt.
Agoo::Server.init(6464, '.', thread_count: 0)
Agoo::Server.handle(:GET, '/upgrade', Clock)
Agoo::Server.start
Chúng tôi gần như ở đó. Chạy máy chủ bằng lệnh này.
$ ruby pubsub.rb
Một mục nhật ký sẽ xuất hiện hiển thị một cái gì đó giống như sau, cho biết rằng máy chủ đang chạy và lắng nghe trên cổng 6464.
I 2018/08/14 19:49:45.170618000 INFO: Agoo 2.5.0 with pid 40366 is listening on https://:6464.
Đã đến lúc xem nó có hoạt động không
Hãy mở https:// localhost:6464 / websocket.html. Sau khi nhấp nháy ban đầu khi kết nối được thiết lập, trạng thái kết nối và thời gian sẽ được hiển thị. Thời gian sẽ tăng lên mỗi giây khi đồng hồ tích tắc.
connected
19:50:12
Chúc mừng bạn đã xuất bản và đăng ký ứng dụng web;-)
Trong tập hôm nay, chúng ta đã xem xét việc sử dụng WebSocket. Sự kiện phía máy chủ (SSE) cung cấp một tùy chọn khác để thực hiện tương tự và chúng tôi đã đưa SSE vào ví dụ mã nguồn đầy đủ. Nếu bạn muốn tìm hiểu thêm, hãy xem máy chủ Agoo mà chúng tôi đã sử dụng hoặc Máy chủ Iodine WebSocket.
Nếu bạn có bất kỳ câu hỏi hoặc nhận xét nào, đừng ngần ngại gửi cho chúng tôi một dòng @AppSignal.