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

Làm chủ Rails Action Cable với Solid Cable: Xây dựng Real-time Apps không cần Redis

Tính năng real-time ngày càng quan trọng trong web app, nhưng không phải Rails developer nào cũng quen Action Cable, thư viện WebSocket built-in của framework.

Rails Action Cable lâu nay hỗ trợ web sockets, nhưng kèm complexity thêm. Rails 8 giới thiệu Solid Cable, adapter database-backed mới cho Action Cable loại bỏ cần Redis. Trong hướng dẫn này, tôi sẽ walk you through Action Cable nhờ Solid Cable và chỉ cách build real-time feature. Bạn thấy dễ dàng thêm real-time functionality vào Rails 8 app mà không loay hoay với Redis.

Tôi khuyến khích bạn follow along build app cùng tôi, nhưng welcome check finished project trên GitHub.

Tại sao dùng Rails Action Cable?

Modern web apps thường cần push updates đến clients real-time. Ví dụ rõ ràng: tin nhắn chat xuất hiện ngay lập tức hoặc live dashboard notifications. Action Cable là giải pháp built-in Rails tích hợp WebSockets vào app, cho phép two-way, persistent communication giữa server và client. Tôi cá nhân biết ơn Action Cable như phần của Rails framework, vì nó support overall theme giving you everything cho genuinely useful web app. Dùng Action Cable nghĩa server gửi data đến browser mà browser không explicit request (không user-prompted refresh!).

Với Action Cable và WebSockets, Rails app cung cấp live interactive features trước đây khó implement trong server-rendered app. Một số use cases hàng ngày:

  • Live chat applications
  • Notifications và feeds
  • Collaborative apps với live updates
  • Live sports hoặc stock tickers

Tóm lại, Action Cable cầu nối gap giữa request-response cycle truyền thống và real-time event-driven updates. Phía client, Rails cung cấp JavaScript consumer subscribe channels nhận broadcasts. Là developer, bạn tương tác Action Cable bằng cách define backend channels (tương tự controllers, nhưng cho real-time streams) mà front-end clients subscribe.

Solid Cable là gì?

Nếu dùng Action Cable trong Rails version trước, có thể biết production thường rely trên Redis (hoặc PostgreSQL NOTIFY) broadcast messages across server processes. Pub/sub service (thường Redis) đảm bảo message từ một Rails process delivered đến all processes khác để forward đến WebSocket clients connected. Infrastructure thêm này historically là requirement dùng Action Cable.

Solid Cable, giới thiệu Rails 8, thay thế cần external pub/sub service như Redis bằng việc dùng database hiện có làm backend. Solid Cable là database-backed adapter cho Action Cable, giống Solid Queue cho Active Job và Solid Cache cho Active Cache. Mỗi incoming WebSocket message ghi vào database table, và tất cả Action Cable instances poll table đó cho message mới broadcast ra clients. Xảy ra rất nhanh (mặc định 100 milliseconds), cho near real-time performance. Messages lưu chỉ thời gian ngắn (24h mặc định) trước khi pruned, cho phép debug recent issues không lo database space.

Tóm lại, Solid Cable fit vào triết lý "Solid Trifecta" của Rails 8: bộ built-in, database-backed features cho caching, background jobs, real-time messaging. Với Solid Cable, bạn có piece cuối chạy jobs, caching, WebSockets tất cả qua database.

Xây dựng Rails 8 app với Solid Cable

Bạn chắc tò mò hands-on Solid Cable, hãy walk through add vào Rails 8 app và build minimal chat room nơi multiple users exchange messages real-time.

Tạo example app

Bạn học Action Cable fundamentals (channels, subscriptions, broadcasting) dùng Solid Cable backend. Dùng Rails 8, tạo app mới:

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

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

Vì dùng Rails 8, không cần add Solid Cable hoặc gems khác để rolling. Phần lớn config đã có. Sẽ walk through tất cả nếu bạn từ Rails version cũ.

Cấu hình Solid Cable

Bắt đầu run Solid Cable setup:

bin/rails solid_cable:install 

Generator này làm 2 việc chính. Tạo file config/cable.yml config Solid Cable làm cable adapter. Cũng tạo db/cable_schema.rb chứa database schema definition cho messages table của Solid Cable. Rails version gần cũng tạo files này tự động khi chạy rails new.

Tiếp theo, config database settings cho Solid Cable. Mặc định Rails dùng separate database cho Solid Cable isolate real-time messaging data từ data khác. Development, bạn dùng same database hoặc setup separate. Sẽ walk through dùng separate SQLite database cho Solid Cable trong development. Nghĩa là thêm "cable" database connection mới.

Thiết lập database cho Solid Cable

Mở config/database.yml. Trong development section, thêm database cable. Ví dụ dùng SQLite (Rails default cho dev):

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 

Lại nói, nếu Rails version đủ mới, config này đã có.

Mở config/cable.yml. Solid Cable đã là default adapter production. Muốn enable Solid Cable development để test chat localhost. Edit cable.yml dùng adapter solid_cable development và point đến database cable vừa config:

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, set development adapter solid_cable copy settings từ production. Setting connects_to bảo Action Cable dùng database cable (định nghĩa trong database.yml) lưu messages. Cần làm thay đổi này kể cả Rails version mới.

Cho app nhỏ, có thể dùng same primary database hold Solid Cable table (copy schema vào migration remove separate DB config). Nhưng khuyến khích separate database tránh performance interference với primary app data.

Cuối cùng, chạy rails db:prepare đảm bảo database ready. Cũng làm production nếu ship app.

Thiết lập Action Cable channel

Action Cable hoạt động qua channels, là Ruby classes handle streams of data. Tương tự controllers handle HTTP requests. Tạo channel cho chat feature. Gọi UserChatChannel. Dùng generator:

rails generate channel UserChat 

Mở app/channels/user_chat_channel.rb generated, update logic mới.

Khi client subscribe UserChatChannel (mở chat page), callback subscribed invoked. Muốn gọi stream_from "user_chat_channel" trong callback start streaming từ broadcast tên "user_chat_channel".

Về cơ bản: "listen bất kỳ data broadcast đến stream user_chat_channel pass đến clients của channel này." Tất cả users subscribed channel nhận message broadcasts đến "user_chat_channel".

Cũng define custom action, gọi talk(data). Bất kỳ public method nào trong channel invoked từ client side. Trong case này, khi client gọi perform("talk", { content: "Hello World" }), method talk execute server-side.

Implementation talk lấy message content client gửi dùng ActionCable.server.broadcast gửi đến mọi subscriber "user_chat_channel". Nghĩa là mọi subscriber (kể cả sender) nhận message data real-time. Broadcast hash chứa message text; có thể include info khác (username, timestamp) nếu cần. Lưu ý: Real app, có thể persist message database hoặc validate ở đây. Cho đơn giản, chỉ broadcast.

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 consumer của channel ở client

Đã build backend, cần hook front-end để user send/nhận messages qua WebSocket demo real-time functionality.

Rails 8 có Action Cable JavaScript stuff baked in. Generator tạo file app/javascript/channels/user_chat_channel.js. Implement client behavior ở đó.

Mở app/javascript/channels/user_chat_channel.js update:

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; 

Dùng consumer.subscriptions.create("UserChatChannel", {...}) tạo subscription đến UserChatChannel server. Trả về subscription object tương tác channel.

Callback connected() chạy khi connection established. Ở đây log console thấy hoạt động.

Callback disconnected() chạy nếu WebSocket disconnect.

Callback received(data) quan trọng! Callback này fire khi channel nhận broadcast từ server. Trong UserChatChannel#talk broadcast { content: message }. Argument data ở đây là hash y hệt. Cập nhật chat log instantly cho all connected clients khi message mới đến.

Define helper sendMessage(content) gọi userChatChannel.perform("talk", { content: ... }). Gửi request đến server-side talk action đã define, kèm message content user gõ.

Cần UI đơn giản user send/nhận messages. Tạo view basic cho điều này.

Xây dựng UI đơn giản cho example app

Đầu tiên, generate controller:

rails generate controller UserChat index 

Mở index view setup basic:

<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, setup root route trỏ route mới trong config/routes.rb:

root "user_chat#index" 

Demo cách mọi thứ hoạt động cùng nhau

Simple chat app sẵn sàng test! Chạy project bin/dev visit localhost:3000:

Làm chủ Rails Action Cable với Solid Cable: Xây dựng Real-time Apps không cần Redis

Demo real-time updates, mở app 2 tab browser khác nhau. Tab 1 nhập "Hello from tab number 1!"

Làm chủ Rails Action Cable với Solid Cable: Xây dựng Real-time Apps không cần Redis Gửi message từ tab 2, thấy xuất hiện tab 1!

Làm chủ Rails Action Cable với Solid Cable: Xây dựng Real-time Apps không cần Redis

Deploy Rails Action Cable lên production

Solid Cable lưu WebSocket messages trong database table, ví dụ trên dùng default cable database. Rails 8 default SQLite cho Solid Cable trong app mới, nhưng technical point đến bất kỳ Rails-supported database bằng cách thêm section cable trong config/database.yml.

Thực tế, dùng separate database cho Solid Cable khuyến khích production isolate real-time messaging load từ data khác. Ví dụ, provision dedicated app_production_cable database cho Solid Cable while primary app data ở app_production.

Separation ngăn chat/notification traffic tranh chấp main application queries. Tuy nhiên, app nhỏ, thường fine dùng single database cho app data và Cable messages.

Phần non-obvious: đảm bảo Solid Cable database included deployment setup. Nếu dùng separate database, nhớ chạy rails db:prepare hoặc rails db:migrate để Rails tạo table messages production.

Nhớ rằng mỗi WebSocket connection tiêu tốn server memory, đảm bảo server đủ resources handle connections cần.

Cấu hình polling intervals

Polling frequency Solid Cable configurable, cho phép balance latency với database load. Giảm interval → poll thường xuyên hơn → giảm time pick up new messages, nhưng cost more SELECT queries database.

Ngược lại, interval lâu hơn → nhẹ database usage nhưng delay broadcasts/updates. Thực tế, default 0.1s (10 polls/second) là starting point tốt cung cấp seemingly real-time updates không overwhelm hầu hết databases.

Solid Cable là trụ cột thiết yếu của Solid Trifecta

Bạn thấy Rails Action Cable mang WebSockets đến Rails cho real-time communication, Solid Cable làm possible không Redis. Bạn biết có hai "Solid" libraries khác trong Rails? Solid Cache cache dễ dàng không Redis, Solid Queue process background jobs không Redis.

Dùng "Solid Trifecta" cho framework remarkably functional build interactive applications với minimal infrastructure overhead.

Lợi ích chính Solid Cable và siblings: simplicity. Real-time functionality Rails app works out of the box chỉ với app database behind scenes. Deployment đơn giản (không Redis/additional services), và nhiều app performance more than sufficient.

Tất nhiên, chạy Rails app production, nên monitor issues users encounter. Không muốn biết khi Action Cable consumers/channels có vấn đề trước khi users?

Honeybadger lựa chọn tuyệt vời Rails error và performance monitoring, critical deploy real-time applications. Honeybadger alert instantly khi errors xảy ra anywhere app – backend và client side – pull in application logs và performance data rapid search, troubleshoot, resolution.

Đăng ký Honeybadger bắt đầu!