Giới thiệu
Nếu bạn từng muốn ứng dụng Rails của mình cảm thấy sống động, bạn nên xem qua Action Cable. Đây là cách tích hợp sẵn của Rails để sử dụng WebSockets, điều này có nghĩa là máy chủ và trình duyệt có thể kết nối và trò chuyện trong thời gian thực. Không còn phải làm mới trang, không còn việc polling cồng kềnh — các cập nhật sẽ xuất hiện ngay lập tức.
Trong hướng dẫn này, chúng ta sẽ xây dựng một bản demo thú vị: một bộ đếm lưu lượng trực tiếp. Khi ai đó truy cập vào trang web của bạn, chúng ta sẽ ghi lại lượt truy cập đó trong cơ sở dữ liệu, tăng một số bộ đếm trong bộ nhớ, và phát sóng các số liệu đã cập nhật đến tất cả mọi người đang trực tuyến. Đóng tab? Số người dùng trực tuyến sẽ giảm ngay lập tức. Nó đơn giản, nhưng cho thấy phép màu thời gian thực mà Action Cable mang lại cho Rails 7.
Bước 1: Mô hình Lượt truy cập
Đầu tiên, hãy tạo một mô hình để ghi lại lượt truy cập.
bin/rails g model Visit session_id:string ip:string user_agent:string
bin/rails db:migrate
# app/models/visit.rb
class Visit < ApplicationRecord
end
Bước 2: Tạo một Kết nối Tùy chỉnh
Rails đã cung cấp cho bạn hai lớp cơ bản trong app/channels/application_cable/:
- channel.rb → lớp cơ bản cho tất cả các kênh của bạn.
- connection.rb → lớp cơ bản cho tất cả các kết nối WebSocket của bạn.
Chúng ta thường không chỉnh sửa những cái đó. Thay vào đó, chúng ta mở rộng chúng bằng cách tạo lớp kết nối của riêng mình bên trong app/channels/. Điều này giữ cho các mặc định sạch sẽ và đặt logic của chúng ta ở một nơi rõ ràng.
# app/channels/traffic_connection.rb
module ApplicationCable
class TrafficConnection < Connection
identified_by :session_id, :ip, :user_agent
def connect
self.session_id = request.session.id
self.ip = request.ip
self.user_agent = request.user_agent
end
end
end
Bước 3: Bộ đếm trong Bộ nhớ
Chúng ta sẽ sử dụng các bộ đếm nguyên tử để làm cho mọi thứ nhanh chóng. Khi khởi động, chúng ta lấy tổng số lượt truy cập từ DB, và bắt đầu số người dùng trực tuyến từ không.
# config/initializers/traffic_counter.rb
$online_users = Concurrent::AtomicFixnum.new(0)
$total_visits = Concurrent::AtomicFixnum.new(Visit.count)
Bước 4: Kênh
Đây là nơi mọi thứ trở nên thú vị — ghi lại lượt truy cập, cập nhật các bộ đếm và phát sóng.
# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
def subscribed
# ghi lại lượt truy cập
Visit.create!(session_id: connection.session_id,
ip: connection.ip,
user_agent: connection.user_agent)
# cập nhật các bộ đếm
$online_users.increment
$total_visits.increment
stream_from "traffic_channel"
broadcast_counts
end
def unsubscribed
$online_users.decrement
broadcast_counts
end
private
def broadcast_counts
ActionCable.server.broadcast("traffic_channel", {
online: $online_users.value,
total: $total_visits.value
})
end
end
Bước 5: Phía JavaScript
Khi bạn chạy bin/rails generate channel Traffic, Rails sẽ tạo ra hai tệp: kênh Ruby và tệp đăng ký JavaScript. Theo mặc định, tệp JS sẽ nhập consumer.js, mà Rails cũng đã thiết lập cho bạn khi ứng dụng được tạo ra.
// app/javascript/channels/traffic_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("TrafficChannel", {
received(data) {
document.querySelector("#online-count").textContent = data.online
document.querySelector("#total-count").textContent = data.total
}
})
Tệp consumer.js được tạo ra bởi Rails khi bạn tạo một ứng dụng mới. Nó thiết lập kết nối WebSocket toàn cầu đến /cable và trông như thế này:
// app/javascript/channels/consumer.js
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
Bước 6: Giao diện
<h1>🚦 Lưu lượng Trực tiếp</h1>
<p>Hiện tại trực tuyến: <span id="online-count">0</span></p>
<p>Tổng số lượt truy cập: <span id="total-count"><%= Visit.count %></span></p>
Bước 7: Đường dẫn
# config/routes.rb
Rails.application.routes.draw do
root "home#index"
mount ActionCable.server => "/cable"
end
Bước 8: Thử nghiệm
Chạy ứng dụng của bạn với bin/dev. Mở trang web của bạn trong vài tab. Số người dùng trực tuyến sẽ tăng lên với mỗi tab, và giảm khi bạn đóng chúng. Tổng số lượt truy cập chỉ tiếp tục tăng. Thật thỏa mãn khi xem!
Ghi chú cho Sản xuất
- Nhiều worker Puma? Sử dụng Redis cho các bộ đếm — bộ nhớ sẽ không đồng bộ giữa các tiến trình.
- Muốn theo dõi người dùng thực thay vì phiên? Thêm current_user vào kết nối.
- Lưu lượt truy cập trong DB có nghĩa là bạn có thể chạy phân tích sau này, như lưu lượng hàng ngày hoặc phân tích trình duyệt.
Và đó là tất cả — một bộ đếm lưu lượng trực tiếp thời gian thực trong Rails 7 với Action Cable. Chỉ trong chưa đầy 100 dòng mã, bạn đã có các cập nhật ngay lập tức và một cái nhìn về những gì WebSockets có thể làm. Cài đặt này cũng hoạt động cho thông báo, bảng điều khiển và tất cả các loại tính năng thời gian thực. Khi bạn đã thử, bạn sẽ muốn rải nó khắp nơi.