In deze tutorial zullen we een schaalbare URL-verkortingsdienst bouwen met behulp van Node.js en Redis. Deze dienst zal gedistribueerde caching benutten om efficiënt met hoge verkeersbelasting om te gaan, de latentie te verminderen en naadloos op te schalen. We zullen belangrijke concepten verkennen zoals consistente hashing, cache-invalideringsstrategieën en sharding om ervoor te zorgen dat het systeem snel en betrouwbaar blijft.

Aan het einde van deze gids zul je een volledig functionele URL-verkorter hebben die gedistribueerde caching gebruikt om de prestaties te optimaliseren. We zullen ook een interactieve demo maken waar gebruikers URL’s kunnen invoeren en realtime statistieken kunnen zien zoals cachehits en missers.

Wat je zult leren

  • Hoe je een URL-verkortingsdienst bouwt met Node.js en Redis.

  • Hoe je gedistribueerde caching implementeert om de prestaties te optimaliseren.

  • Het begrijpen van consistent hashing en cache-invalideringsstrategieën.

  • Het gebruiken van Docker om meerdere Redis-instanties te simuleren voor sharding en schalen.

Vereisten

Voordat je begint, zorg ervoor dat je het volgende geïnstalleerd hebt:

  • Node.js (v14 of hoger)

  • Redis

  • Docker

  • Basiskennis van JavaScript, Node.js en Redis.

Inhoudsopgave

Projectoverzicht

We gaan een URL-verkorterservice bouwen waar:

  1. Gebruikers lange URL’s kunnen verkorten en de oorspronkelijke URL’s kunnen ophalen.

  2. De service maakt gebruik van Redis-caching om koppelingen tussen verkorte URL’s en oorspronkelijke URL’s op te slaan.

  3. De cache is verdeeld over meerdere Redis-instanties om het hoge verkeer aan te kunnen.

  4. Het systeem zal in real-time cache hits en misses demonstreren.

Systeemarchitectuur

Om schaalbaarheid en prestaties te garanderen, zullen we onze service verdelen in de volgende componenten:

  1. API-server: Verwerkt verzoeken voor het inkorten en ophalen van URL’s.

  2. Redis-cachelag: Gebruikt meerdere Redis-instanties voor gedistribueerde caching.

  3. Docker: Simuleert een gedistribueerde omgeving met meerdere Redis-containers.

Stap 1: Project instellen

Laten we ons project opzetten door een Node.js-toepassing te initialiseren:

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

Installeer nu de benodigde afhankelijkheden:

npm install express redis shortid dotenv
  • express: Een lichtgewicht webserverframework.

  • redis: Om caching te verwerken.

  • shortid: Voor het genereren van korte, unieke ID’s.

  • dotenv: Voor het beheren van omgevingsvariabelen.

Maak een .env bestand aan in de hoofdmap van je project:

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

Deze variabelen definiëren de Redis hosts en poorten die we zullen gebruiken.

Stap 2: Opzetten van Redis-instanties

We zullen Docker gebruiken om een gedistribueerde omgeving met meerdere Redis-instanties te simuleren.

Voer de volgende commando’s uit om drie Redis-containers te 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

Dit zal drie Redis-instanties op verschillende poorten opzetten. We zullen deze instanties gebruiken om consistent hashing en sharding te implementeren.

Stap 3: Implementeren van de URL-verkorter-service

Laten we ons hoofdapplicatiebestand, index.js, maken:

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-functie om sleutels te verdelen over Redis-clients
function getRedisClient(key) {
  const hash = key.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  return redisClients[hash % redisClients.length];
}

// Eindpunt om een URL te verkorten
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}` });
});

// Eindpunt om de oorspronkelijke URL op te halen
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}`);
});

Zoals je kunt zien in deze code, hebben we:

  1. Consistent Hashing:

    • We verdelen sleutels (verkorte URL’s) over meerdere Redis-clients met behulp van een eenvoudige hashfunctie.

    • De hashfunctie zorgt ervoor dat URL’s gelijkmatig over de Redis-instanties worden verdeeld.

  2. URL-verkorting:

    • De /shorten-eindpunt accepteert een lange URL en genereert een kort ID met behulp van de shortid-bibliotheek.

    • De verkorte URL wordt opgeslagen in een van de Redis-instanties met behulp van onze hashfunctie.

  3. URL-omleiding:

    • Het /:shortId-eindpunt haalt de oorspronkelijke URL op uit de cache en leidt de gebruiker om.

    • Als de URL niet in de cache wordt gevonden, wordt een 404-reactie geretourneerd.

Stap 4: Implementeren van Cache-invalidering

In een real-world toepassing kunnen URL’s verlopen of in de loop van de tijd veranderen. Om hiermee om te gaan, moeten we cache-invalidering implementeren.

Verloop toevoegen aan gecachte URL’s

Laten we ons index.js bestand aanpassen om een vervaltijd in te stellen voor elk gecached item:

// Eindpunt om een URL te verkorten met vervaldatum
app.post('/shorten', async (req, res) => {
  const { url, ttl } = req.body; // ttl (time-to-live) is optioneel
  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); // Standaard TTL van 1 uur
  res.json({ shortUrl: `http://localhost:${process.env.PORT}/${shortId}` });
});
  • TTL (Time-To-Live): We stellen een standaard vervaltijd van 1 uur in voor elke verkorte URL. U kunt de TTL aanpassen voor elke URL indien nodig.

  • Cache-invalidering: Wanneer de TTL afloopt, wordt het item automatisch verwijderd uit de cache.

Stap 5: Monitoren van Cache-metrics

Om cache-hits en missers te controleren, voegen we wat logging toe aan onze endpoints in 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);
  });
});

Hier is wat er gebeurt in deze code:

  • Cache Hits: Als een URL wordt gevonden in de cache, is het een cache-hit.

  • Cache Misses: Als een URL niet wordt gevonden, is het een cache-misser.

  • Deze logging zal u helpen bij het controleren van de prestaties van uw gedistribueerde cache.

Stap 6: Het testen van de Applicatie

  1. Start uw Redis-instanties:
docker start redis1 redis2 redis3
  1. Start de Node.js-server:
node index.js
  1. Test de eindpunten met behulp van curl of Postman:

    • Verkort een URL:

        POST http://localhost:3000/shorten
        Body: { "url": "https://example.com" }
      
    • Open de verkorte URL:

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

Conclusie: Wat je hebt geleerd

Gefeliciteerd! Je hebt succesvol een schaalbare URL-verkorter service gebouwd met gedistribueerde caching met behulp van Node.js en Redis. Gedurende deze tutorial heb je geleerd hoe je:

  1. Implementeer consistent hashing om cache-items over meerdere Redis-instanties te verdelen.

  2. Optimaliseer uw toepassing met cache-invalideringsstrategieën om gegevens up-to-date te houden.

  3. Gebruik Docker om een gedistribueerde omgeving met meerdere Redis-nodes te simuleren.

  4. Monitor cache-hits en misses om de prestaties te optimaliseren.

Volgende stappen:

  • Voeg een Database toe: Sla URL’s op in een database voor persistentie buiten de cache.

  • Implementeer Analytics: Houd klikaantallen en analyses bij voor verkorte URL’s.

  • Implementeer in de Cloud: Implementeer uw toepassing met Kubernetes voor automatisch schalen en veerkracht.

Veel programmeerplezier!