Создание масштабируемого сокращателя URL-адресов с помощью Redis

В этом руководстве мы создадим масштабируемый сервис сокращения URL-адресов с помощью Node.js и Redis. Этот сервис будет использовать распределенное кэширование для эффективной обработки большого трафика, сокращения задержек и плавного масштабирования. Мы рассмотрим ключевые концепции, такие как согласованное хэширование, стратегии аннулирования кэша и сегментирование, чтобы гарантировать, что система остается быстрой и надежной.

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

Перед началом работы убедитесь, что у вас установлено следующее:

  • Node.js (версия 14 или выше)
  • Redis
  • Docker

Обзор проекта

Мы создадим сервис для сокращения URL-адресов, в котором:

  1. Пользователи могут сокращать длинные URL-адреса и извлекать исходные URL-адреса. Служба использует кэширование Redis для сохранения сопоставлений между сокращенными URL-адресами и исходными URL-адресами. Кэш распределяется по нескольким экземплярам Redis для обработки большого трафика. Система будет демонстрировать попадания в кэш и промахи в режиме реального времени.

Системная архитектура

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

  1. Сервер API: Обрабатывает запросы на сокращение и получение URL-адресов. Уровень кэширования Redis: Использует несколько экземпляров Redis для распределенного кэширования. Docker: Имитирует распределенную среду с несколькими контейнерами Redis.

Шаг 1: Настройка проекта

Давайте создадим наш проект, инициализация приложения Node.js :

mkdir scalable-url-shortener
cd scalable-url-shortener
npm init -y

Теперь установите необходимые зависимости:

npm install express redis shortid dotenv
  • express - это облегченная платформа для веб-сервера. redis - Для обработки кэширования. shortid - Для создания коротких уникальных идентификаторов. dotenv - Для управления переменными среды.

Создайте env-файл в корневом каталоге вашего проекта:

PORT=3000
REDIS_HOST_1=localhost
REDIS_PORT_1=6379
REDIS_HOST_2=localhost
REDIS_PORT_2=6380
REDIS_HOST_3=localhost
REDIS_PORT_3=6381

Эти переменные определяют хосты и порты Redis, которые мы будем использовать.

Шаг 2: Настройка экземпляров Redis

Мы будем использовать Docker для моделирования распределенной среды с несколькими экземплярами Redis.

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

docker run -p 6379:6379 --name redis1 -d redis
docker run -p 6380:6379 --name redis2 -d redis
docker run -p 6381:6379 --name redis3 -d redis

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

Шаг 3: Внедрение службы сокращения URL-адресов

Давайте создадим наш основной файл приложения, index.js:

require('dotenv').config();
const express = require('express');
const redis = require('redis');
const shortid = require('shortid');

const app = express();
app.use(express.json());

const redisClients = [
  redis.createClient({ host: process.env.REDIS_HOST_1, port: process.env.REDIS_PORT_1 }),
  redis.createClient({ host: process.env.REDIS_HOST_2, port: process.env.REDIS_PORT_2 }),
  redis.createClient({ host: process.env.REDIS_HOST_3, port: process.env.REDIS_PORT_3 })
];

// Hash function to distribute keys among Redis clients
function getRedisClient(key) {
  const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  return redisClients[hash % redisClients.length];
}

// Endpoint to shorten a URL
app.post('/shorten', async (req, res) => {
  const { url } = req.body;
  if (!url) return res.status(400).send('URL is required');

  const shortId = shortid.generate();
  const redisClient = getRedisClient(shortId);

  await redisClient.set(shortId, url);
  res.json({ shortUrl: `http://localhost:${process.env.PORT}/${shortId}` });
});

// Endpoint to retrieve the original URL
app.get('/:shortId', async (req, res) => {
  const { shortId } = req.params;
  const redisClient = getRedisClient(shortId);

  redisClient.get(shortId, (err, url) => {
    if (err || !url) {
      return res.status(404).send('URL not found');
    }
    res.redirect(url);
  });
});

app.listen(process.env.PORT, () => {
  console.log(`Server running on port ${process.env.PORT}`);
});

Как вы можете видеть в этом коде, у нас есть:

  1. Последовательное хэширование: Мы распределяем ключи (сокращенные URL-адреса) между несколькими клиентами Redis с помощью простой хэш-функции. Хэш-функция обеспечивает равномерное распределение URL-адресов по экземплярам Redis. Сокращение URL-адресов: Конечная точка /shorten принимает длинный URL и генерирует короткий идентификатор, используя библиотеку shortid. Сокращенный URL сохраняется в одном из экземпляров Redis с помощью нашей хэш-функции. Перенаправление URL: Конечная точка /:shortId извлекает исходный URL-адрес из кэша и перенаправляет пользователя. Если URL-адрес не найден в кэше, возвращается ответ 404.

Шаг 4: Реализация аннулирования кэша

В реальном приложении срок действия URL-адресов может истечь или они могут изменяться с течением времени. Чтобы справиться с этим, нам нужно реализовать аннулирование кэша.

Добавление срока действия к кэшированным URL-адресам

Давайте изменим наш файл index.js, чтобы установить время истечения срока действия для каждой кэшированной записи:

// Endpoint to shorten a URL with expiration
app.post('/shorten', async (req, res) => {
  const { url, ttl } = req.body; // ttl (time-to-live) is optionalif (!url) return res.status(400).send('URL is required');

  const shortId = shortid.generate();
  const redisClient = getRedisClient(shortId);

  await redisClient.set(shortId, url, 'EX', ttl || 3600); // Default TTL of 1 hour
  res.json({ shortUrl: `http://localhost:${process.env.PORT}/${shortId}` });
});
  • Срок действия (Time-To-Live): По умолчанию мы устанавливаем срок действия в 1 час для каждого сокращенного URL-адреса. При необходимости вы можете настроить срок действия для каждого URL-адреса. Аннулирование кэша: По истечении срока действия запись автоматически удаляется из кэша.

Шаг 5: Мониторинг показателей кэша

Чтобы отслеживать попадания и промахи в кэш, мы добавим некоторые протоколы в наши конечные точки в index.js:

app.get('/:shortId', async (req, res) => {
  const { shortId } = req.params;
  const redisClient = getRedisClient(shortId);

  redisClient.get(shortId, (err, url) => {
    if (err || !url) {
      console.log(`Cache miss for key: ${shortId}`);
      return res.status(404).send('URL not found');
    }
    console.log(`Cache hit for key: ${shortId}`);
    res.redirect(url);
  });
});

Вот что происходит в этом коде:

  • Попадание в кэш: Если URL-адрес найден в кэше, это означает попадание в кэш. Пропуски в кэше: Если URL-адрес не найден, это означает, что он не найден в кэше. Это ведение журнала поможет вам отслеживать производительность вашего распределенного кэша.

Шаг 6: Тестирование приложения

  1. Запустите свои экземпляры Redis:
docker start redis1 redis2 redis3
  1. Запустите Node.js сервер:
node index.js
  1. Протестируйте конечные точки с помощью curl или Postman: Сократить URL-адрес: СООБЩЕНИЕ http://localhost:3000/shorten Тело: { "url": "https://example.com " } Получите доступ к сокращенному URL-адресу: ПОЛУЧИТЬ http://localhost:3000 /{Короткий идентификатор}

Заключение: Чему вы научились

Поздравляю! Вы успешно создали масштабируемый сервис сокращения URL-адресов с распределенным кэшированием, используя Node.js и Redis. В этом руководстве вы узнали, как:

  1. Реализуйте согласованное хэширование для распределения записей кэша по нескольким экземплярам Redis. Оптимизируйте свое приложение с помощью стратегий аннулирования кэша, чтобы поддерживать актуальность данных. Используйте Docker для моделирования распределенной среды с несколькими узлами Redis. Отслеживайте попадания и промахи в кэш для оптимизации производительности.

следующие шаги:

  • Добавьте базу данных: Сохраняйте URL-адреса в базе данных для сохранения за пределами кэша.
  • Внедрите аналитику: Отслеживайте количество кликов и анализируйте сокращенные URL-адреса.
  • Развертывание в облаке: Разверните свое приложение с помощью Kubernetes для автоматического масштабирования и повышения устойчивости.

Счастливого кодирования!