Введение
Если вы когда-либо хотели, чтобы ваше приложение на Rails выглядело живым, вам стоит обратить внимание на Action Cable. Это встроенный способ Rails работы с WebSockets, что в основном означает, что ваш сервер и браузер могут оставаться подключенными и общаться в реальном времени. Больше никаких обновлений страниц, никакого громоздкого опроса — обновления появляются мгновенно.
В этом руководстве мы создадим забавную небольшую демонстрацию: счетчик трафика в реальном времени. Когда кто-то заходит на ваш сайт, мы будем регистрировать этот визит в базе данных, увеличивать некоторые счетчики в памяти и транслировать обновленные числа всем онлайн. Закрыли вкладку? Количество онлайн-пользователей сразу же уменьшается. Это просто, но показывает магию реального времени, которую Action Cable приносит в Rails 7.
Шаг 1: Модель визита
Сначала давайте создадим модель для регистрации визитов.
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
Шаг 2: Создание пользовательского подключения
Rails уже предоставляет вам два базовых класса в app/channels/application_cable/:
- channel.rb → базовый класс для всех ваших каналов.
- connection.rb → базовый класс для всех ваших WebSocket подключений.
Мы обычно не редактируем их. Вместо этого мы расширяем их, создавая свой собственный класс подключения внутри app/channels/. Это позволяет сохранить стандартные настройки чистыми и разместить нашу логику в понятном месте.
# 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
Шаг 3: Счетчики в памяти
Мы будем использовать атомарные счетчики, чтобы сделать все быстро. При запуске мы получаем общее количество визитов из БД и начинаем с нуля для онлайн-пользователей.
# config/initializers/traffic_counter.rb
$online_users = Concurrent::AtomicFixnum.new(0)
$total_visits = Concurrent::AtomicFixnum.new(Visit.count)
Шаг 4: Канал
Вот где начинается веселье — регистрируем визит, обновляем счетчики и транслируем.
# app/channels/traffic_channel.rb
class TrafficChannel < ApplicationCable::Channel
def subscribed
# регистрируем визит
Visit.create!(session_id: connection.session_id,
ip: connection.ip,
user_agent: connection.user_agent)
# обновляем счетчики
$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
Шаг 5: Сторона JavaScript
Когда вы запускаете bin/rails generate channel Traffic, Rails создает два файла: Ruby-канал и файл подписки JavaScript. По умолчанию JS-файл импортирует consumer.js, который Rails также настроил для вас при создании приложения.
// 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
}
})
Файл consumer.js создается Rails, когда вы создаете новое приложение. Он настраивает глобальное WebSocket подключение к /cable и выглядит так:
// app/javascript/channels/consumer.js
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
Шаг 6: Вид
<h1>🚦 Счетчик трафика в реальном времени</h1>
<p>В настоящее время онлайн: <span id="online-count">0</span></p>
<p>Всего визитов: <span id="total-count"><%= Visit.count %></span></p>
Шаг 7: Маршруты
# config/routes.rb
Rails.application.routes.draw do
root "home#index"
mount ActionCable.server => "/cable"
end
Шаг 8: Попробуйте
Запустите ваше приложение с помощью bin/dev. Откройте ваш сайт в нескольких вкладках. Счетчик онлайн-пользователей увеличивается с каждой вкладкой и уменьшается, когда вы их закрываете. Общее количество визитов просто продолжает расти. Супер приятно наблюдать!
Заметки для продакшена
- Несколько рабочих процессов Puma? Используйте Redis для счетчиков — память не будет синхронизироваться между процессами.
- Хотите отслеживать фактических пользователей вместо сессий? Добавьте current_user в подключение.
- Хранение визитов в БД означает, что вы можете проводить аналитику позже, например, ежедневный трафик или разбивку по браузерам.
И это всё — счетчик трафика в реальном времени в Rails 7 с Action Cable. Менее чем на 100 строках кода вы получили мгновенные обновления и представление о том, что могут сделать WebSockets. Эта же настройка работает для уведомлений, панелей управления и всех видов функций в реальном времени. Как только вы попробуете это, вам захочется использовать это повсюду.