In diesem Tutorial werden wir einen skalierbaren URL-Verkürzungsdienst mit Node.js und Redis erstellen. Dieser Dienst wird verteiltes Caching nutzen, um mit hohem Traffic effizient umzugehen, die Latenz zu reduzieren und nahtlos zu skalieren. Wir werden Schlüsselkonzepte wie konsistentes Hashing, Cache-Invalidierungsstrategien und Sharding erkunden, um sicherzustellen, dass das System schnell und zuverlässig bleibt.

Am Ende dieses Leitfadens werden Sie einen voll funktionsfähigen URL-Verkürzungsdienst haben, der verteiltes Caching verwendet, um die Leistung zu optimieren. Wir werden auch eine interaktive Demo erstellen, in der Benutzer URLs eingeben und Echtzeitmetriken wie Cache-Treffer und -Fehler sehen können.

Das werden Sie lernen

  • Wie man einen URL-Verkürzungsdienst mit Node.js und Redis erstellt.

  • Wie man verteiltes Caching zur Leistungsoptimierung implementiert.

  • Verständnis von konsistentem Hashing und Cache-Invalidierungsstrategien.

  • Verwendung von Docker zur Simulation mehrerer Redis-Instanzen für Sharding und Skalierung.

Voraussetzungen

Vor dem Start stellen Sie sicher, dass Sie Folgendes installiert haben:

  • Node.js (v14 oder höher)

  • Redis

  • Docker

  • Grundkenntnisse in JavaScript, Node.js und Redis.

Inhaltsverzeichnis

Projektübersicht

Wir werden einen URL-Verkürzungsdienst erstellen, bei dem:

  1. Benutzer lange URLs verkürzen und die Original-URLs abrufen können.

  2. Der Dienst verwendet Redis-Caching zur Speicherung von Zuordnungen zwischen verkürzten URLs und Original-URLs.

  3. Der Cache ist auf mehrere Redis-Instanzen verteilt, um einen hohen Datenverkehr zu bewältigen.

  4. Das System wird Cache-Treffer und -Fehlschläge in Echtzeit demonstrieren.

Systemarchitektur

Um Skalierbarkeit und Leistung sicherzustellen, werden wir unseren Service in die folgenden Komponenten aufteilen:

  1. API-Server: Bearbeitet Anfragen zur Verkürzung und Abruf von URLs.

  2. Redis-Caching-Schicht: Verwendet mehrere Redis-Instanzen für verteiltes Caching.

  3. Docker: Simuliert eine verteilte Umgebung mit mehreren Redis-Containern.

Schritt 1: Projekt einrichten

Richten wir unser Projekt ein, indem wir eine Node.js-Anwendung initialisieren:

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

Installieren Sie nun die erforderlichen Abhängigkeiten:

npm install express redis shortid dotenv
  • express: Ein leichtgewichtiges Webserver-Framework.

  • redis: Zur Handhabung des Cachings.

  • shortid: Zur Generierung kurzer, eindeutiger IDs.

  • dotenv: Zur Verwaltung von Umgebungsvariablen.

Erstellen Sie eine .env-Datei im Stammverzeichnis Ihres Projekts:

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

Diese Variablen definieren die Redis-Hosts und -Ports, die wir verwenden werden.

Schritt 2: Einrichten von Redis-Instanzen

Wir werden Docker verwenden, um eine verteilte Umgebung mit mehreren Redis-Instanzen zu simulieren.

Führen Sie die folgenden Befehle aus, um drei Redis-Container zu starten:

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

Dadurch werden drei Redis-Instanzen eingerichtet, die auf verschiedenen Ports laufen. Wir werden diese Instanzen verwenden, um konsistentes Hashing und Sharding zu implementieren.

Schritt 3: Implementierung des URL-Verkürzungsdienstes

Erstellen wir unsere Hauptanwendungsdatei, 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 })
];

// Hashfunktion zur Verteilung von Schlüsseln auf Redis-Clients
function getRedisClient(key) {
  const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  return redisClients[hash % redisClients.length];
}

// Endpunkt zum Verkürzen einer 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}` });
});

// Endpunkt zum Abrufen der ursprünglichen 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}`);
});

Wie Sie in diesem Code sehen können, haben wir:

  1. Konsistentes Hashing:

    • Wir verteilen Schlüssel (verkürzte URLs) über mehrere Redis-Clients mithilfe einer einfachen Hashfunktion.

    • Die Hashfunktion stellt sicher, dass URLs gleichmäßig über die Redis-Instanzen verteilt werden.

  2. URL-Verkürzung:

    • Der /verkürzen-Endpunkt akzeptiert eine lange URL und generiert eine kurze ID mithilfe der shortid-Bibliothek.

    • Die verkürzte URL wird in einer der Redis-Instanzen mithilfe unserer Hash-Funktion gespeichert.

  3. URL-Weiterleitung:

    • Der /:kurzId-Endpunkt ruft die originale URL aus dem Cache ab und leitet den Benutzer weiter.

    • Wenn die URL im Cache nicht gefunden wird, wird eine 404-Antwort zurückgegeben.

Schritt 4: Implementierung der Cache-Invalidation

In einer realen Anwendung können URLs ablaufen oder sich im Laufe der Zeit ändern. Um dies zu handhaben, müssen wir eine Cache-Invalidation implementieren.

Hinzufügen einer Ablaufzeit für gecachte URLs

Ändern wir unsere index.js-Datei, um für jeden gecachten Eintrag eine Ablaufzeit festzulegen:

// Endpunkt zum Kürzen einer URL mit Ablaufzeit
app.post('/shorten', async (req, res) => {
  const { url, ttl } = req.body; // TTL (Time-To-Live) ist optional
  if (!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); // Standard-TTL von 1 Stunde
  res.json({ shortUrl: `http://localhost:${process.env.PORT}/${shortId}` });
});
  • TTL (Time-To-Live): Wir setzen eine Standard-Ablaufzeit von 1 Stunde für jede gekürzte URL. Bei Bedarf können Sie die TTL für jede URL anpassen.

  • Cache-Invalidation: Wenn die TTL abläuft, wird der Eintrag automatisch aus dem Cache entfernt.

Schritt 5: Überwachung von Cache-Metriken

Um Cache-Treffer und Fehlschläge zu überwachen, fügen wir unserem Endpoint in index.js einige Protokollierungen hinzu:

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);
  });
});

Hier ist, was in diesem Code passiert:

  • Cache-Treffer: Wenn eine URL im Cache gefunden wird, handelt es sich um einen Cache-Treffer.

  • Cache-Fehlschläge: Wenn eine URL nicht gefunden wird, handelt es sich um einen Cache-Fehlschlag.

  • Diese Protokollierung hilft Ihnen, die Leistung Ihres verteilten Caches zu überwachen.

Schritt 6: Testen der Anwendung

  1. Starten Sie Ihre Redis-Instanzen:
docker start redis1 redis2 redis3
  1. Führen Sie den Node.js-Server aus:
node index.js
  1. Testen Sie die Endpunkte mit curl oder Postman:

    • Verkürzen Sie eine URL:

        POST http://localhost:3000/shorten
        Body: { "url": "https://example.com" }
      
    • Rufen Sie die verkürzte URL auf:

        GET http://localhost:3000/{shortId}
      

Zusammenfassung: Was Sie gelernt haben

Herzlichen Glückwunsch! Sie haben erfolgreich einen skalierbaren URL-Verkürzungsdienst mit verteilter Zwischenspeicherung unter Verwendung von Node.js und Redis erstellt. Im Verlauf dieses Tutorials haben Sie gelernt, wie man:

  1. Implementiere konsistentes Hashing, um Cache-Einträge über mehrere Redis-Instanzen zu verteilen.

  2. Optimiere deine Anwendung mit Cache-Invalidierungsstrategien, um Daten aktuell zu halten.

  3. Verwende Docker, um eine verteilte Umgebung mit mehreren Redis-Knoten zu simulieren.

  4. Überwache Cache-Treffer und -Fehlschläge, um die Leistung zu optimieren.

Nächste Schritte:

  • Füge eine Datenbank hinzu: Speichere URLs in einer Datenbank für Persistenz über den Cache hinaus.

  • Implementiere Analytics: Verfolge Klickzahlen und Analysen für verkürzte URLs.

  • Veröffentliche in der Cloud: Veröffentliche deine Anwendung unter Verwendung von Kubernetes für automatisches Skalieren und Robustheit.

Viel Spaß beim Coden!