Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Master Rails Action Cable với cáp chắc chắn:Xây dựng ứng dụng thời gian thực mà không cần Redis

Các tính năng thời gian thực ngày càng trở nên quan trọng trong các ứng dụng web, nhưng không phải nhà phát triển Rails nào cũng quen thuộc với Action Cable, thư viện WebSocket tích hợp của framework.

Rails Action Cable đã hỗ trợ các ổ cắm web từ lâu nhưng có một số điểm phức tạp hơn. Rails 8 giới thiệu Cáp rắn , một bộ chuyển đổi dựa trên cơ sở dữ liệu mới dành cho Action Cable giúp loại bỏ nhu cầu sử dụng Redis. Trong hướng dẫn này, tôi sẽ hướng dẫn bạn qua Action Cable bằng Solid Cable và chỉ cho bạn cách xây dựng tính năng thời gian thực. Bạn sẽ thấy việc thêm chức năng thời gian thực vào ứng dụng Rails 8 mà không cần bận tâm đến Redis dễ dàng như thế nào.

Tôi khuyến khích bạn theo dõi và xây dựng ứng dụng cùng tôi nhưng bạn có thể xem dự án đã hoàn thành trên GitHub.

Tại sao nên sử dụng Rails Action Cable?

Các ứng dụng web hiện đại thường cần đẩy các bản cập nhật tới khách hàng theo thời gian thực. Một số ví dụ rõ ràng bao gồm tin nhắn trò chuyện xuất hiện ngay lập tức hoặc thông báo trực tiếp trên trang tổng quan. Cáp hành động là giải pháp tích hợp sẵn của Rails để tích hợp WebSockets vào ứng dụng của bạn, cho phép liên lạc hai chiều, liên tục giữa máy chủ và máy khách. Cá nhân tôi rất biết ơn Action Cable như một phần của khung Rails, vì nó hỗ trợ chủ đề tổng thể là cung cấp cho bạn mọi thứ bạn cần cho một ứng dụng web thực sự hữu ích. Sử dụng Action Cable có nghĩa là máy chủ có thể gửi dữ liệu đến trình duyệt mà không cần trình duyệt yêu cầu rõ ràng (không cần làm mới do người dùng nhắc!).

Với Action Cable và WebSockets, ứng dụng Rails của bạn có thể cung cấp các tính năng tương tác trực tiếp mà trước đây khó triển khai trong ứng dụng do máy chủ kết xuất. Một số trường hợp sử dụng hàng ngày cho việc này là:

  • Ứng dụng trò chuyện trực tiếp
  • Thông báo và nguồn cấp dữ liệu
  • Ứng dụng cộng tác có cập nhật trực tiếp
  • Thể thao trực tiếp hoặc cổ phiếu

Nói tóm lại, Action Cable thu hẹp khoảng cách giữa chu kỳ phản hồi yêu cầu truyền thống và các cập nhật theo sự kiện theo thời gian thực. Về phía khách hàng, Rails cung cấp cho người tiêu dùng JavaScript để đăng ký các kênh và nhận các chương trình phát sóng. Với tư cách là nhà phát triển, bạn tương tác với Action Cable bằng cách xác định các kênh phụ trợ (tương tự như bộ điều khiển, nhưng dành cho luồng thời gian thực) mà máy khách ngoại vi có thể đăng ký.

Vậy cáp Solid là gì?

Nếu bạn đã sử dụng Action Cable trong các phiên bản Rails trước đó, bạn có thể biết rằng trong quá trình sản xuất, nó thường dựa vào Redis (hoặc NOTIFY của PostgreSQL ) để quảng bá tin nhắn trên các quy trình máy chủ khác nhau. Dịch vụ pub/sub (thường là Redis) đảm bảo rằng thông báo từ một quy trình Rails sẽ được gửi đến tất cả các quy trình khác để chúng có thể chuyển tiếp nó đến các máy khách WebSocket được kết nối của mình. Cơ sở hạ tầng bổ sung này trước đây là yêu cầu bắt buộc để sử dụng Action Cable.

Solid Cable, được giới thiệu trong Rails 8, thay thế nhu cầu về dịch vụ pub/sub bên ngoài như Redis bằng cách sử dụng cơ sở dữ liệu hiện có của bạn làm phụ trợ. Solid Cable là một bộ điều hợp dựa trên cơ sở dữ liệu dành cho Action Cable, giống như Solid Queue cho Active Job và Solid Cache cho Active Cache. Mỗi thông báo WebSocket đến được ghi vào một bảng cơ sở dữ liệu và tất cả các phiên bản Action Cable sẽ thăm dò bảng đó để phát các thông báo mới tới máy khách. Điều này xảy ra rất nhanh (theo mặc định là 100 mili giây một lần), mang lại hiệu suất gần như thời gian thực. Các tin nhắn chỉ được lưu trữ trong một thời gian ngắn (24 giờ theo mặc định) trước khi bị cắt bớt, do đó bạn có thể gỡ lỗi các sự cố gần đây mà không phải lo lắng về dung lượng cơ sở dữ liệu.

Nhìn chung, Solid Cable phù hợp với triết lý "Solid Trifecta" của Rails 8, là một bộ hoàn chỉnh các tính năng được tích hợp sẵn, hỗ trợ cơ sở dữ liệu để lưu vào bộ nhớ đệm, tác vụ nền và nhắn tin theo thời gian thực. Với Solid Cable, bạn có mảnh ghép cuối cùng để chạy các công việc, bộ nhớ đệm và WebSockets thông qua cơ sở dữ liệu của mình.

Xây dựng ứng dụng Rails 8 bằng Solid Cable

Có thể bạn đang tò mò muốn thực hành Solid Cable, vì vậy hãy cùng tìm hiểu cách thêm nó vào ứng dụng Rails 8 và xây dựng một phòng trò chuyện tối thiểu nơi nhiều người dùng có thể trao đổi tin nhắn trong thời gian thực.

Tạo ứng dụng mẫu

Bạn sẽ tìm hiểu các nguyên tắc cơ bản của Action Cable (kênh, đăng ký, phát sóng) trong khi sử dụng Solid Cable làm chương trình phụ trợ. Chúng tôi sẽ sử dụng Rails 8 cho ví dụ này, vì vậy hãy tiếp tục và tạo một ứng dụng Rails mới với:

rails _8.1.0_ new solid_cable_chat --database=sqlite3

Sau đó, cd vào thư mục Solid_cable_chat mới.

Vì bạn đã sử dụng Rails 8 nên bạn sẽ không cần thêm Solid Cable hoặc bất kỳ loại đá quý nào khác để bắt đầu. Hầu hết hoặc tất cả cấu hình sẽ có sẵn cho bạn. Tôi sẽ hướng dẫn bạn tất cả những điều đó trong trường hợp bạn đang sử dụng phiên bản Rails cũ hơn.

Định cấu hình cáp rắn

Chúng ta sẽ bắt đầu bằng cách chạy thiết lập Solid Cable:

bin/rails solid_cable:install

Máy phát điện này thực hiện hai việc chính. Nó tạo ra một config/cable.yml tệp cấu hình đặt Solid Cable làm bộ chuyển đổi cáp. Nó cũng tạo ra một db/cable_schema.rb tệp chứa định nghĩa lược đồ cơ sở dữ liệu cho bảng thông báo của Solid Cable. Các phiên bản gần đây của Rails cũng tự động tạo các tệp này khi chạy rails new .

Tiếp theo, chúng ta cần định cấu hình cài đặt cơ sở dữ liệu cho Solid Cable. Theo mặc định, Rails sử dụng cơ sở dữ liệu riêng cho Solid Cable để tách biệt dữ liệu nhắn tin theo thời gian thực khỏi phần dữ liệu còn lại của bạn. Trong quá trình phát triển, bạn có thể sử dụng cùng một cơ sở dữ liệu hoặc thiết lập một cơ sở dữ liệu riêng. Tôi sẽ hướng dẫn bạn cách sử dụng cơ sở dữ liệu SQLite riêng cho Solid Cable trong quá trình phát triển. Điều này có nghĩa là thêm một kết nối cơ sở dữ liệu "cáp" mới.

Thiết lập cơ sở dữ liệu cho Solid Cable

Mở config/database.yml tập tin. Trong phần phát triển, thêm cable cơ sở dữ liệu. Ví dụ:nếu bạn đang sử dụng SQLite (mặc định Rails cho nhà phát triển):

development:
 primary:
 <<: *default
 database: storage/development.sqlite3
 cable:
 <<: *default
 database: storage/development_cable.sqlite3
 migrations_paths: db/cable_migrate

production:
 primary:
 <<: *default
 database: storage/production.sqlite3
 cache:
 <<: *default
 database: storage/production_cache.sqlite3
 migrations_paths: db/cache_migrate
 queue:
 <<: *default
 database: storage/production_queue.sqlite3
 migrations_paths: db/queue_migrate
 cable:
 <<: *default
 database: storage/production_cable.sqlite3
 migrations_paths: db/cable_migrate

Xin nhắc lại, nếu bạn đang sử dụng phiên bản Rails đủ gần đây thì cấu hình này sẽ có sẵn ở đó.

Bây giờ hãy mở config/cable.yml . Solid Cable đã là bộ chuyển đổi mặc định trong sản xuất. Chúng tôi cũng muốn kích hoạt Solid Cable trong quá trình phát triển (để chúng tôi có thể kiểm tra cuộc trò chuyện của mình trong localhost). Chỉnh sửa cable.yml để sử dụng solid_cable bộ điều hợp đang được phát triển và trỏ nó tới cable cơ sở dữ liệu chúng ta vừa cấu hình:

development:
 adapter: solid_cable
 connects_to:
 database:
 writing: cable
 polling_interval: 0.1.seconds
 message_retention: 1.day

test:
 adapter: test

production:
 adapter: solid_cable
 connects_to:
 database:
 writing: cable
 polling_interval: 0.1.seconds
 message_retention: 1.day

Trong cable.yml ở trên , chúng tôi đặt bộ điều hợp phát triển thành solid_cable và sao chép cài đặt từ cài đặt sản xuất. connects_to cài đặt yêu cầu Action Cable sử dụng cáp cơ sở dữ liệu (như được định nghĩa trong database.yml ) để lưu trữ tin nhắn. Bạn sẽ cần thực hiện thay đổi này ngay cả trên phiên bản Rails gần đây.

Đối với các ứng dụng nhỏ, bạn có thể sử dụng cùng một cơ sở dữ liệu chính để giữ bảng của Solid Cable (bằng cách sao chép lược đồ vào quá trình di chuyển và xóa cấu hình DB riêng biệt). Tuy nhiên, bạn nên sử dụng cơ sở dữ liệu riêng để tránh mọi khả năng ảnh hưởng đến hiệu suất đối với dữ liệu ứng dụng chính của bạn.

Cuối cùng, chạy rails db:prepare để đảm bảo cơ sở dữ liệu đã sẵn sàng. Bạn cũng cần thực hiện việc này trong quá trình sản xuất nếu bạn vận chuyển ứng dụng của mình.

Thiết lập kênh Action Cable

Action Cable hoạt động thông qua các kênh, là các lớp Ruby xử lý các luồng dữ liệu. Điều này hơi giống với bộ điều khiển xử lý các yêu cầu HTTP. Hãy tạo một kênh cho tính năng trò chuyện của chúng tôi. Chúng ta sẽ gọi nó là UserChatChannel . Sử dụng trình tạo:

rails generate channel UserChat

Mở app/channels/user_chat_channel.rb đã tạo và cập nhật nó để chứa logic mới.

Khi khách hàng đăng ký UserChatChannel (bằng cách mở trang trò chuyện), subscribed gọi lại được gọi. Chúng tôi muốn gọi stream_from "user_chat_channel" trong cuộc gọi lại này để bắt đầu phát trực tuyến từ chương trình phát sóng có tên "user_chat_channel" .

Về cơ bản, chúng tôi đang nói "lắng nghe mọi dữ liệu được phát tới user_chat_channel truyền phát và chuyển nó tới khách hàng của kênh này." Tất cả người dùng đã đăng ký kênh này sẽ nhận được tin nhắn phát tới "user_chat_channel" .

Chúng tôi cũng muốn xác định một hành động tùy chỉnh, chúng tôi sẽ gọi nó là talk(data) . Bất kỳ phương thức công khai nào trong kênh đều có thể được gọi từ phía máy khách. Trong trường hợp này, khi client gọi perform("talk", { content: "Hello World" }) , talk phương thức thực thi trên máy chủ.

Việc triển khai talk của chúng tôi lấy nội dung tin nhắn do khách hàng gửi và sử dụng ActionCable.server.broadcast để gửi nó tới những người đã đăng ký "user_chat_channel" . Điều này có nghĩa là mọi người đăng ký (bao gồm cả người gửi) sẽ nhận được dữ liệu tin nhắn theo thời gian thực. Chúng tôi chỉ đơn giản phát đi một hàm băm chứa văn bản tin nhắn; bạn có thể bao gồm thông tin khác (như tên người dùng hoặc dấu thời gian) nếu cần. Lưu ý: Trong ứng dụng thực, bạn cũng có thể lưu thông báo vào cơ sở dữ liệu hoặc thực hiện xác thực tại đây. Để đơn giản, chúng tôi chỉ phát sóng nó.

class UserChatChannel < ApplicationCable::Channel
 def subscribed
 stream_from "user_chat_channel"
 end

 def unsubscribed
 # Any cleanup needed when unsubscribing from the channel
 end

 def talk(data)
 message = data["content"]
 ActionCable.server.broadcast("user_chat_channel", { content: message })
 end
end

Xây dựng khách hàng cho kênh của chúng tôi trong ứng dụng khách

Bây giờ chúng ta đã xây dựng xong phần phụ trợ, chúng ta cần kết nối giao diện người dùng để người dùng có thể gửi và nhận tin nhắn thông qua WebSocket nhằm minh họa chức năng thời gian thực.

Rails 8 đi kèm với nội dung JavaScript của Action Cable được tích hợp sẵn. Trình tạo đã tạo app/javascript/channels/user_chat_channel.js tập tin cho chúng tôi. Tiếp theo, chúng tôi sẽ triển khai hành vi của khách hàng.

Mở app/javascript/channels/user_chat_channel.js và cập nhật nó thành:

import consumer from "channels/consumer";

const userChatChannel = consumer.subscriptions.create("UserChatChannel", {
 connected() {
 console.log("Connected to UserChatChannel.");
 },

 disconnected() {
 console.log("Disconnected from UserChatChannel.");
 },

 received(data) {
 const messagesDiv = document.getElementById("messages");
 if (messagesDiv && data.content) {
 const messageElement = document.createElement("p");
 messageElement.textContent = data.content;
 messagesDiv.appendChild(messageElement);
 }
 }
});

function sendMessage(content) {
 userChatChannel.perform("talk", { content: content });
}

export { sendMessage };
window.sendMessage = sendMessage;

Ở đây chúng tôi sử dụng consumer.subscriptions.create("UserChatChannel", {...}) để tạo đăng ký cho UserChatChannel của chúng tôi trên máy chủ. Điều này trả về một đối tượng đăng ký mà chúng ta có thể sử dụng để tương tác với kênh.

connected() cuộc gọi lại sẽ chạy khi kết nối được thiết lập. Ở đây chúng ta chỉ cần đăng nhập vào bảng điều khiển để có thể thấy nó hoạt động.

disconnected() cuộc gọi lại sẽ chạy nếu WebSocket ngắt kết nối.

received(data) gọi lại là quan trọng! Cuộc gọi lại này kích hoạt bất cứ khi nào kênh của chúng tôi nhận được thông báo từ máy chủ. Trong UserChatChannel#talk chúng tôi phát sóng { content: message } . data đối số ở đây sẽ là hàm băm tương tự. Điều này sẽ cập nhật nhật ký trò chuyện của chúng tôi ngay lập tức cho tất cả khách hàng được kết nối khi có tin nhắn mới.

Chúng tôi cũng xác định một trình trợ giúp sendMessage(content) gọi userChatChannel.perform("talk", { content: ... }) . Thao tác này sẽ gửi yêu cầu tới talk phía máy chủ hành động mà chúng tôi đã xác định, bao gồm cả nội dung tin nhắn mà người dùng đã nhập.

Bây giờ chúng tôi cần một giao diện người dùng đơn giản để người dùng gửi và nhận tin nhắn. Hãy tạo một cái nhìn rất cơ bản cho việc này.

Xây dựng giao diện người dùng đơn giản cho ứng dụng mẫu của chúng tôi

Đầu tiên, tạo bộ điều khiển:

rails generate controller UserChat index

Tiếp theo, mở chế độ xem chỉ mục và thiết lập cơ bản cho nó:

<h1>Chats from Users</h1>

<div id="messages" style="border: 1px solid #ccc; padding: 1em; height: 200px; overflow-y: auto; margin-bottom: 1em;">
 <!-- Messages will appear here -->
</div>

<form id="chat-form" onsubmit="event.preventDefault(); sendMessage(document.getElementById('chat-input').value); document.getElementById('chat-input').value = '';">
 <input type="text" id="chat-input" placeholder="Type a message..." autocomplete="off" style="width: 80%;" />
 <button type="submit">Send</button>
</form>

Cuối cùng, thiết lập tuyến gốc để trỏ tới tuyến mới này trong config/routes.rb :

root "user_chat#index"

Thể hiện cách tất cả phối hợp với nhau

Ứng dụng trò chuyện đơn giản của chúng tôi đã sẵn sàng để thử nghiệm! Chạy dự án với bin/dev và truy cập localhost:3000 :

Master Rails Action Cable với cáp chắc chắn:Xây dựng ứng dụng thời gian thực mà không cần Redis

Để hiển thị các bản cập nhật theo thời gian thực, hãy mở ứng dụng trong hai tab trình duyệt khác nhau. Trong một tab, nhập thông báo như "Xin chào từ tab số 1!"

Master Rails Action Cable với cáp chắc chắn:Xây dựng ứng dụng thời gian thực mà không cần Redis Nếu gửi tin nhắn từ tab thứ hai, bạn sẽ thấy tin nhắn đó xuất hiện trong tab đầu tiên!

Master Rails Action Cable với cáp chắc chắn:Xây dựng ứng dụng thời gian thực mà không cần Redis

Triển khai Rails Action Cable vào sản xuất

Solid Cable lưu trữ các thông báo WebSocket trong một bảng cơ sở dữ liệu và ví dụ của chúng tôi ở trên đã sử dụng cable mặc định cơ sở dữ liệu. Rails 8 cũng mặc định sử dụng SQLite cho Solid Cable trong các ứng dụng mới, nhưng về mặt kỹ thuật, bạn có thể trỏ nó tới bất kỳ cơ sở dữ liệu nào được Rails hỗ trợ bằng cách thêm cable phần trong config/database.yml .

Trên thực tế, nên sử dụng cơ sở dữ liệu riêng cho Solid Cable trong quá trình sản xuất để tách tải tin nhắn theo thời gian thực khỏi phần còn lại của dữ liệu. Ví dụ:bạn có thể cung cấp app_production_cable chuyên dụng cơ sở dữ liệu cho Solid Cable trong khi dữ liệu ứng dụng chính của bạn vẫn ở dạng app_production .

Sự tách biệt này ngăn cản lưu lượng trò chuyện hoặc thông báo cạnh tranh với các truy vấn ứng dụng chính của bạn. Điều đó có nghĩa là đối với các ứng dụng nhỏ hơn, việc sử dụng một cơ sở dữ liệu duy nhất cho cả dữ liệu ứng dụng và tin nhắn Cáp là điều bình thường.

Phần không rõ ràng là đảm bảo cơ sở dữ liệu Solid Cable được đưa vào thiết lập triển khai của bạn. Nếu bạn sử dụng cơ sở dữ liệu riêng biệt, hãy nhớ chạy rails db:prepare hoặc rails db:migrate để Rails tạo messages bàn đang được sản xuất.

Hãy nhớ rằng mỗi kết nối WebSocket tiêu tốn bộ nhớ máy chủ, vì vậy hãy đảm bảo máy chủ của bạn có đủ tài nguyên để xử lý số lượng kết nối bạn cần.

Định cấu hình khoảng thời gian bỏ phiếu

Tần số thăm dò cho Solid Cable có thể cấu hình được, cho phép bạn cân bằng độ trễ với tải cơ sở dữ liệu. Việc giảm khoảng thời gian sẽ dẫn đến việc bỏ phiếu thường xuyên hơn, giúp giảm thời gian nhận thư mới nhưng phải trả thêm nhiều truy vấn CHỌN hơn trên cơ sở dữ liệu của bạn.

Ngược lại, khoảng thời gian dài hơn sẽ làm giảm mức sử dụng cơ sở dữ liệu nhưng gây ra nhiều độ trễ hơn trong việc phát sóng và cập nhật. Trong thực tế, 0,1 giây mặc định (10 cuộc thăm dò mỗi giây) là điểm khởi đầu tốt để cung cấp các cập nhật dường như theo thời gian thực mà không làm quá tải hầu hết các cơ sở dữ liệu.

Cáp rắn là trụ cột thiết yếu của Solid Trifecta

Bạn đã thấy cách Rails Action Cable đưa WebSockets vào Rails để giao tiếp trong thời gian thực và cách Solid Cable có thể thực hiện được điều đó mà không cần Redis. Bạn có biết có hai thư viện "Solid" khác trong Rails ? Solid Cache giúp dễ dàng lưu vào bộ nhớ đệm mà không cần Redis và Solid Queue cho phép bạn xử lý các tác vụ nền mà không cần Redis.

Việc sử dụng "Solid Trifecta" mang lại cho bạn một khung chức năng vượt trội để xây dựng các ứng dụng tương tác với chi phí cơ sở hạ tầng tối thiểu.

Ưu điểm chính của Solid Cable và các anh chị em của nó là sự đơn giản. Chức năng thời gian thực của ứng dụng Rails của chúng tôi hoạt động hoàn hảo chỉ với cơ sở dữ liệu của ứng dụng. Việc triển khai đơn giản hơn (không cần Redis hoặc các dịch vụ bổ sung) và đối với nhiều ứng dụng, hiệu suất là quá đủ.

Tất nhiên, khi chạy bất kỳ ứng dụng Rails nào trong môi trường sản xuất, bạn nên theo dõi nó để phát hiện các vấn đề mà người dùng của bạn có thể gặp phải. Bạn có muốn biết khi nào có sự cố xảy ra với các thiết bị tiêu thụ và kênh Action Cable của mình trước khi người dùng của bạn biết ?

Honeybadger là một lựa chọn tuyệt vời để theo dõi hiệu suất và lỗi Rails, vốn rất quan trọng để triển khai các ứng dụng thời gian thực. Honeybadger sẽ thông báo cho bạn ngay lập tức khi có lỗi xảy ra ở bất kỳ đâu trong ứng dụng của bạn—ở phần phụ trợ và phía máy khách—đồng thời lấy nhật ký ứng dụng và dữ liệu hiệu suất của bạn để tìm kiếm, khắc phục sự cố và giải quyết nhanh chóng.

Hãy đăng ký Honeybadger để bắt đầu!