С Redis можно сделать так много, но где и когда его использовать, для некоторых может быть загадкой. Удаленный сервер словарей (да, это то, что означает слово Redis 😉) невероятно повысит производительность вашего приложения, сократив задержку операций чтения и записи вашего приложения до смешного менее миллисекунды за счет миллионов операций в секунду. — да, это не опечатка.

Redis имеет множество знакомых вам структур данных для удовлетворения потребностей вашего приложения. Он позволяет хранить данные вашего приложения в виде строки, списка, хеша, множества, JSON и т. д. И угадай что? Redis очень прост в использовании. С Redis вы пишете меньше строк кода для хранения, доступа и использования данных в своих приложениях. Кроме того, вы можете использовать Redis API, не беспокоясь об управлении отдельным кешем, базой данных или базовой инфраструктурой.

Но эй! Достаточно того, как Redis может помочь моему приложению. Я уже влюблен. Пожалуйста, скажите мне тогда, что такое Redis на самом деле? Расслабься…😉

Согласно официальной документации, Redis — это хранилище структуры данных в памяти [ключ-значение] с открытым исходным кодом, используемое в качестве базы данных, кеша, брокера сообщений и механизма потоковой передачи. Вы можете использовать Redis из большинства языков программирования, таких как JavaScript, Java, Python, C++, C#, Swift, PHP и т. д.

Psst: это всего лишь общий обзор Redis. Чтобы узнать больше, посетите официальный сайт.

Хорошо, теперь, пожалуйста, и, пожалуйста, скажите мне, что я могу сделать с Redis 🥺. Конечно, без проблем! ✌️Случаи использования Redis следующие:

  • Кэширование для уменьшения задержки доступа к данным, увеличения пропускной способности и снижения нагрузки на реляционную базу данных или базу данных NoSQL и приложение.
  • Чат, обмен сообщениями и очереди за счет использования различных структур данных, таких как списки, отсортированные наборы и хэши.
  • Хранилище сеансов для управления данными сеанса, такими как профили пользователей, учетные данные, состояние сеанса и индивидуальная персонализация пользователя.
  • Игровые списки лидеров, используя структуру данных Redis Sorted Set.
  • Управление геопространственными данными в режиме реального времени с помощью таких команд, как GEOADD, GEODIST, GEORADIUS и GEORADIUSBYMEMBER, для хранения, обработки и анализа геопространственных данных в режиме реального времени.
  • Быстрое хранилище данных в памяти для потоковой передачи в реальном времени.
  • Быстрое хранилище данных в памяти для быстрого создания, обучения и развертывания моделей машинного обучения.
  • Случаи использования аналитики в реальном времени, такие как аналитика социальных сетей, таргетинг рекламы, персонализация и IoT.

И многое другое!

Как реализовать получение ежедневных активных пользователей из метрик Prometheus, используя возможности функции кэширования Redis

Итак, теперь позвольте мне показать вам, как максимально использовать функцию кэширования Redis для решения бизнес-проблемы извлечения уникальных активных пользователей из реального приложения в режиме реального времени. Наверное, поэтому вы здесь, не так ли?

Проблема

Осветим проблему еще раз. Ожидается, что вы получите метрику из клиента Prometheus, интегрированного в ваше приложение Node.js. Этот показатель представляет собой количество активных пользователей в день, то есть каждого пользователя, который просматривал ваш сайт хотя бы один раз в день. Таким образом, независимо от того, открывает ли пользователь ваш сайт 1 или 1 миллион раз в день, его действия на сайте регистрируются как 1 за день.

План

Мы создадим счетчик Prometheus. Если уникальный пользователь открывает веб-сайт и использует защищенный маршрут, счетчик увеличивается на 1. Это приращение останется таким и не изменится для того же пользователя до конца этого дня. Этот механизм будет применяться к каждому уникальному пользователю, открывающему веб-сайт в течение дня. Таким образом, счетчик продолжает увеличиваться для каждого уникального пользователя в течение дня. Затем на следующий день счетчик будет увеличиваться на 1 за каждого такого же уникального пользователя и останется таким до конца дня. Короче говоря, счетчик будет увеличиваться на 1 за каждого уникального пользователя каждый день, когда пользователь посещает веб-сайт.

Блокировщик

Чтобы распознать, что пользователь уже открывал сайт хотя бы один раз в течение дня, и, таким образом, не увеличивать счетчик в следующий раз, когда он рано или поздно в тот же день открывает другой защищенный маршрут, мы должны хранить информацию о пользователе, которую мы можем использовать. для проверки в следующий раз, когда наше приложение получит другой запрос на защищенный маршрут. Мы не хотим хранить эту информацию в нашей базе данных. Ни за что!🙅‍♂️ Нам нужна система кеширования.👍

Редис спешит на помощь!

Redis — идеальное решение. Используя кэширование Redis, мы будем делать следующее на защищенных маршрутах:

  • Используйте идентификатор пользователя в качестве ключа.
  • Проверьте, есть ли кешированные данные на основе ключа.
  • Если данных нет, это должен быть первый раз, когда пользователь открыл веб-сайт за день. Таким образом, сохранить идентификатор пользователя вместе с результатом…
  • вычисление, сколько времени в секундах осталось до конца дня с момента открытия сайта и использование его в качестве времени истечения срока действия для Redis.
  • Затем увеличьте счетчик ежедневных активных пользователей на 1.

Предпосылки

  • Prometheus: Поскольку речь идет о Redis, я предполагаю, что вы уже знаете хотя бы основы Prometheus. Если нет, то рекомендую сначала пройти ускоренный курс здесь.
  • Grafana: Идеально иметь небольшое знание Grafana. Но если вы не знаете Grafana, загляните в их документацию. Это довольно просто.
  • Redis: вы должны знать некоторые основные концепции Redis. Но это не проблема — с некоторыми из них я уже познакомил вас в самом начале. Кроме того, я проведу вас через установку и некоторые основные команды Redis с помощью redis-cli.
  • Существующий проект Node.js: в этом тексте я буду делиться только фрагментами кода и скриншотами реализаций Redis, Prometheus и Grafana для достижения основной цели, которая заключается в привлечении активных пользователей ежедневно.

Ладно, начнем! ✌️

Установка Redis

Способ установки Redis зависит от вашей операционной системы. Смотрите руководство здесь, которое лучше всего соответствует вашим потребностям.

Давайте изучим Redis с помощью CLI

После успешной установки Redis откройте терминал и выполните следующую команду, чтобы запустить сервер Redis.

❯ redis-server

Это должно привести к выводу, который выглядит следующим образом:

Если вы получаете сообщение об ошибке, возможно, у вас не установлен Redis или, для пользователей Windows, вы не используете терминал WSL. Обязательно установите Redis, следуя инструкции по установке здесь. А если вы работаете в Windows, обязательно откройте терминал WSL. Затем повторите команду.

Обратите внимание, что часть информации, напечатанной на вашем терминале, говорит о том, что Redis работает на порту 6379 на вашем локальном компьютере. Это номер порта по умолчанию для сервера Redis.

Теперь, когда сервер Redis работает, откройте новый терминал и выполните следующую команду, чтобы запустить интерфейс командной строки и получить доступ к Redis.

❯ redis-cli

Это должно открыть командную строку, указывающую на 127.0.0.1:6379.

Теперь давайте поиграем с некоторыми командами Redis. Введите и выполните следующую команду, чтобы получить все ключи, соответствующие шаблону.

127.0.0.1:6379 ❯ KEYS *

Redis сопоставляет данные с ключами, указанными вами в точке хранения. Думайте о ключах как об идентификаторах ваших данных. Чтобы получить сохраненные данные, вы должны передать key. В приведенной выше команде, если шаблон, указанный после ключевого слова KEYS, соответствует ключу, она возвращает ключ или ключи, если шаблон равен *, что означает «все ключи».

Приведенная выше команда должна вернуть сообщение empty list or set… если только у вас нет каких-либо существующих данных, сохраненных в прошлом. Итак, давайте создадим новый. Выполните следующую команду:

127.0.0.1:6379 ❯ SET name Kenneth

SET — это ключевое слово для установки новых данных. Для этого требуется ключ name и значение Kenneth. Обратите внимание, что данные, хранящиеся таким образом, хранятся в виде строк. Теперь проверьте, существует ли ваш ключ name.

127.0.0.1:6379 ❯ KEYS name

Вы должны увидеть список ключей, включая ваш новый ключ name. Но что, если меня интересует value? Я хочу увидеть значение, хранящееся с ключом. Команда для этого:

127.0.0.1:6379 ❯ GET name

GET — это ключевое слово для получения данных. Для этого требуется ключ, в данном случае это name. Это выведет значение, сохраненное этим ключом. Итак, вы должны увидеть Kenneth.

Наш ключ (и значение) будут храниться вечно, потому что мы не указали время истечения срока действия при создании. Однако мы можем удалить его с помощью этой команды:

127.0.0.1:6379 ❯ DEL name

Это должно вернуть (integer) 1, означающее, что он был успешно удален. Но что, если мы хотим установить срок действия для новых данных при создании? Синтаксис для этого:

127.0.0.1:6379 ❯ SETEX name 15 Kenneth

SETEX устанавливает новый ключ и значение со сроком действия в качестве аргумента перед значением. Время истечения должно быть в секундах. Таким образом, приведенная выше команда установит новый ключ с именем name и присвоит ему значение с именем Kenneth и запустит на нем таймер на 15 секунд. Чтобы получить информацию о том, сколько времени осталось до истечения срока действия, используется следующий синтаксис:

127.0.0.1:6379 ❯ TTL name

TTL означает «Время жить». Ему требуется имя ключа, по которому он должен выяснить, сколько времени он будет жить. Выполнение приведенного выше кода снова и снова приведет к разным результатам, поскольку время жизни для ключа (имя) отсчитывается. Как только он доходит до -2, это означает, что ключ пропал.

##Прометей

Как упоминалось ранее, я сосредоточусь только на той части кода Prometheus, где реализована метрическая логика. При реализации Prometheus я обычно создаю файл metrics.js, в котором определяю свои показатели. Скопируйте и вставьте следующий фрагмент кода в файл метрик:

import client from 'prom-client';
 export const dailyActiveUsersGauge = new client.Gauge({
  name: 'my_daily_active_users',
  help: 'Active users metrics on a daily basis.',
  labelNames: ['user_id', 'year', 'month', 'day'],
 });

Обычно у вас есть промежуточное ПО, которое проверяет и проверяет токен jwt запроса. Для этого я бы создал файл промежуточного программного обеспечения protect.js. Он будет содержать мою логику проверки jwt. Если проверка пройдена, я получу id пользователя (который будет использоваться в качестве идентификатора, необходимого при выдаче токена jwt) из декодированного токена. Затем я находил пользователя, чье id соответствовало декодированному токену id, и прикреплял данные пользователя к новому свойству объекта запроса с именем user (то есть req.user). Наконец, вызывается промежуточное ПО next() в стеке.

Ладно, это только мое мнение. Но как бы вы ни разработали свою логику аутентификации, пока вы можете получить id пользователя из объекта запроса, смонтируйте следующий фрагмент:

import { dailyActiveUsersGauge } from 'path/to/metrics.js';
 
 dailyActiveUsersGauge.inc(
    {
       user_id: currentUser.uid,
       year,
       month,
       day,
    },
    1,
 );

Это создаст новую метрику датчика с именем my_daily_active_users и некоторыми метками (year, month и day) с совокупным значением выборки, равным 1.

##Редис

Мы не можем полагаться на нашу текущую реализацию без помощи Redis, потому что выборочное значение будет увеличиваться каждый раз, когда один и тот же пользователь использует защищенный маршрут и вызывается метрика dailyActiveUsersGauge. Это приведет к неточному результату в конце дня. Итак, давайте используем Redis для кэширования пользовательского id.

Давайте установим пакет redis npm

❯ npm i redis

Создайте файл в папке вашего проекта (я бы назвал его redisCacheHandler.js) и вставьте в него следующий код:

import { createClient } from 'redis';
 
 let client;
 
 if (process.env.NODE_ENV === 'production') {
  client = createClient({
    url: `redis://${process.env.REDIS_USER}:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOSTNAME}:${process.env.REDIS_PORT}`,
   });
 } else {
  // You should install redis and run the service command ----> redis-server
   client = createClient();
 }
 
 client.on('error', (err) => console.log('Redis Client Error', err));
 
 async function connectRedis() {
   await client.connect();
 }
 
 connectRedis();
 
 const defaultExpirationTime = 60 * 10; // 60 seconds times 10 -> 10 minutes
 
 const getOrSetCache = (cb) => cb();
 
 export const getCache = (key) =>
   new Promise((resolve, reject) => {
     try {
       getOrSetCache(async () => {
        const data = await client.get(key);
        if (data) {
          console.log('SENDING CACHE...🍫');
        }
        resolve(JSON.parse(data));
      });
    } catch (error) {
      reject(error);
    }
 });
 
 export const setCache = (key, data, expTime) => {
  if (typeof expTime === 'undefined') {
    expTime = defaultExpirationTime;
 }
 
  return new Promise((resolve, reject) => {
    try {
      getOrSetCache(async () => {
        const isOk = await client.set(key, JSON.stringify(data), {
          EX: expTime,
        });
        if (isOk) {
          console.log('CACHED! ✅');
        }
        resolve();
      });
    } catch (error) {
      reject(error);
    }
  });
 };

Кратко поясню код:

  • Импортируйте библиотеку Redis.
  • Проверьте, в какой среде работает приложение. Если в разработке, используйте локальный сервер redis (убедитесь, что он запущен). Если в продакшене, подключитесь к Redis Cloud.
  • Экспортируйте две функции:
  • getCache: проверяет сохраненные данные с помощью данного ключа. Возвращает обещание данных (может быть неопределенным).
  • setCache: задает новые данные типа "ключ-значение" и возвращает обещание. Ожидает ключ, данные и время истечения срока действия в секундах. Если время истечения не указано, используйте время истечения по умолчанию, которое составляет 10 минут.

Теперь вернитесь туда, где мы реализовали функцию dailyActiveUsersGauge, и измените свой код следующим образом:

import { dailyActiveUsersGauge } from 'path/to/metrics.js';
import { getCache, setCache } from 'path/to/redisCacheHandler.js';
 
// .. ..
  const cacheKey = currentUser.uid;
    const date = new Date();
 
    const year = date.getFullYear();
    const month = date.getMonth();
    const day = date.getDate();
 
    const now = Date.now();
    const endOfDay = date.setUTCHours(23, 59, 59, 999);
 
    const secondsLeftTillEndOfDay = Math.floor((endOfDay - now) / 1000);
 
    const cachedData = await getCache(cacheKey);
 
    if (!cachedData) {
      dailyActiveUsersGauge.inc(
        {
          user_id: currentUser.uid,
          year,
          month,
          day,
        },
        1,
      );
 
      // cache
      await setCache(cacheKey, '_', secondsLeftTillEndOfDay);
   }
 
// .. ..

Кратко поясню код:

  • Импортируйте наши пользовательские модули Redis. Получите день, месяц и год и передайте их функции dailyActiveUsersGauge. Эта информация, особенно «день», нам понадобится для группировки наших метрик с Prometheus. Именно так мы и собираемся в итоге получить дневной счет.
  • Используйте идентификатор пользователя в качестве ключа
  • Проверьте, есть ли кешированные данные на основе ключа
  • Если данных нет, это должен быть первый раз, когда пользователь открывает веб-сайт за день. Таким образом, сохранить идентификатор пользователя вместе с результатом…
  • Расчет того, сколько времени осталось до конца дня с момента открытия сайта в секундах как время истечения
  • Затем увеличьте ежедневную шкалу активных пользователей на 1.

Приведенная выше реализация гарантирует, что у нас будет точное ежедневное количество пользователей, поскольку счетчик будет увеличиваться только в том случае, если благодаря Redis появится новый пользователь или новый ключ.

Графана

На панели инструментов Grafana создайте новую панель и структурируйте браузер метрик Prometheus следующим образом:

Заключение

Redis — это мощный инструмент, который можно использовать практически в любом месте вашего веб-приложения. Надеюсь, этот текст был полезен и, что более важно, открыл глаза на мощь Redis.

Этот пост создан в сотрудничестве с Redis.

Чтобы узнать больше, изучите следующие справочные ссылки:

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Посетите наш Community Discord и присоединитесь к нашему Коллективу талантов.