Какое количество пользователей отслеживало приложение в вашем проекте?

Ответ

В моем последнем коммерческом проекте — платформе для онлайн-ивентов на Node.js — мы отслеживали активных пользователей в реальном времени. Пиковая нагрузка во время крупных трансляций достигала ~50 000 одновременных подключений через WebSocket.

Для подсчета и хранения этой метрики мы использовали комбинацию Redis (для скорости) и PostgreSQL (для персистентности и аналитики).

Пример реализации счетчика активных пользователей на Node.js с Socket.io и Redis:

const { createClient } = require('redis');
const { Server } = require('socket.io');

const redisClient = createClient();
await redisClient.connect();

const io = new Server(server);

io.on('connection', async (socket) => {
  const userId = socket.handshake.auth.userId;
  // Добавляем пользователя в Redis Set с ключом, имеющим TTL (например, на 5 минут)
  await redisClient.sAdd('active_users:session', userId);
  await redisClient.expire('active_users:session', 300);

  // Получаем и эмитируем общее количество
  const count = await redisClient.sCard('active_users:session');
  io.emit('activeUsersCount', count);

  socket.on('disconnect', async () => {
    await redisClient.sRem('active_users:session', userId);
    const newCount = await redisClient.sCard('active_users:session');
    io.emit('activeUsersCount', newCount);
  });
});

// Эндпоинт для получения текущего количества
app.get('/metrics/active-users', async (req, res) => {
  const count = await redisClient.sCard('active_users:session');
  res.json({ activeUsers: count });
});

Итоговые цифры по DAU/MAU мы агрегировали в PostgreSQL и визуализировали в Grafana, подключенной к этой БД.

Ответ 18+ 🔞

А, вспомнил! Был у меня один проект, ёпта, платформа для онлайн-ивентов. Сидим, значит, пилим на Node.js, всё вроде норм. А потом бац — ивент крупный, трансляция, народ повалил. И тут выясняется, что наш счётчик активных юзеров, который мы на коленке слепили, просто накрылся медным тазом. Под нагрузкой в ~50 000 одновременных сокетов он начал бздеть так, что мама не горюй. Каркалыга полная.

Пришлось, блядь, э бошка думай. Нужно было считать в реальном времени, чтобы на дашборде цифра прыгала, а потом ещё и статистику по DAU/MAU для аналитиков гонять. Доверия ебать ноль было к чистому PostgreSQL на такие пики, поэтому родили гибридную схему.

Суть в чём: всё, что горячее и должно быть быстрее пули, — в Redis. Он у нас как кэш-счётчик работал. А уже потом, по расписанию или событиям, данные агрегировались и на постоянное хранение в PostgreSQL сваливались. А из Постгре уже Grafana всё это дело красиво рисовала. Без неё — пизда рулю, бизнес ничего не понимает.

Вот, смотри, как примерно выглядел костяк этого цирка на Socket.io:

const { createClient } = require('redis');
const { Server } = require('socket.io');

const redisClient = createClient();
await redisClient.connect();

const io = new Server(server);

io.on('connection', async (socket) => {
  const userId = socket.handshake.auth.userId;
  // Добавляем пользователя в Redis Set с ключом, имеющим TTL (например, на 5 минут)
  await redisClient.sAdd('active_users:session', userId);
  await redisClient.expire('active_users:session', 300);

  // Получаем и эмитируем общее количество
  const count = await redisClient.sCard('active_users:session');
  io.emit('activeUsersCount', count);

  socket.on('disconnect', async () => {
    await redisClient.sRem('active_users:session', userId);
    const newCount = await redisClient.sCard('active_users:session');
    io.emit('activeUsersCount', newCount);
  });
});

// Эндпоинт для получения текущего количества
app.get('/metrics/active-users', async (req, res) => {
  const count = await redisClient.sCard('active_users:session');
  res.json({ activeUsers: count });
});

Фишка в том, что мы использовали Redis Set. Каждый новый коннект — добавляем айдишник в сет. На дисконнекте — удаляем. А чтобы сет не рос до бесконечности от забытых сессий, вешали TTL на ключ, скажем, на 5 минут. Это, конечно, упрощённый вариант, но ни хуя себе, как хорошо зашло. Главное — скорость, все операции атомарные.

А потом уже ночной джобой эти "активные сессии" из Redis перемалывались, считались уникальные юзеры за день/месяц и пихались в PostgreSQL. И всё это, блядь, светилось в Grafana. Бизнес смотрел на графики и думал, что мы ебушки-воробушки. А мы-то знаем, какой это был пиздопроёбищный геморрой всё отладить, чтобы под нагрузкой не посыпалось.