Elido
11 min di letturaIngegneria

Come funzionano gli URL shortener? La meccanica spiegata

Come funzionano gli URL shortener? Memorizzano un mapping slug-destinazione, cercano la chiave ad ogni clic, restituiscono un redirect HTTP. La meccanica, dall'inizio alla fine

Marius Voß
DevRel · edge infra
Pipeline dalla richiesta alla ricerca dello slug fino a un redirect 302 che arriva a un URL di destinazione, nella palette del brand Elido

Apri elido.me/abc123 e qualcosa deve trasformare quella stringa corta in un indirizzo web completo prima che il tuo browser possa caricare qualsiasi cosa. Il meccanismo è più semplice di quanto la maggior parte delle persone si aspetti. Un URL shortener memorizza un mapping da un codice breve a un lungo URL di destinazione. Quando clicchi il link corto, il servizio tratta il codice come una chiave di ricerca, trova la destinazione e restituisce un redirect HTTP che dice al tuo browser dove andare effettivamente. Una richiesta in entrata, un redirect in uscita.

Questa è l'idea completa. Tutto il resto è ingegneria attorno a tre pressioni: rendere la ricerca veloce, mantenere i codici brevi e univoci, e registrare il clic senza rallentare nessuno. Questo post illustra come funzionano gli URL shortener dall'inizio alla fine, usando l'architettura edge di Elido come esempio concreto mantenendo la spiegazione valida per gli shortener in generale. Copriremo il mapping slug-URL, come vengono generati i codici brevi, dove vivono i dati, la scelta redirect 301-versus-302 che crea confusione più di qualsiasi altra cosa, cosa sia effettivamente un redirect HTTP sul filo, perché il caching all'edge è importante, e come un clic viene contato in modo asincrono.

Come funzionano gli URL shortener: il mapping al centro#

Rimuovi l'infrastruttura e un URL shortener è un key-value store con un gestore di redirect attaccato. La chiave è lo slug, il codice breve dopo il dominio. Il valore è la destinazione, il lungo URL che hai originariamente incollato.

Quando crei un link corto, lo shortener scrive una riga: questo slug punta a quella destinazione. Quando qualcuno visita il link corto, lo shortener rilegge quella riga e agisce di conseguenza. Creare link è raro; leggerli è costante. Un singolo link di marketing potrebbe essere scritto una volta e poi letto qualche centinaio di migliaia di volte nel corso della sua vita. Quel rapporto read-heavy è il fatto singolarmente più importante sul workload, e modella ogni decisione di design che segue, in particolare il caching.

Il mapping stesso può contenere più di una destinazione. In Elido, uno slug può contenere regole di targeting, cosicché un link corto instrada verso destinazioni diverse per paese, dispositivo, lingua o orario. Questo è ciò che chiamiamo smart link, ed è ancora la stessa ricerca, solo con una piccola valutazione delle regole dopo la lettura. La relazione fondamentale non cambia mai: slug in entrata, destinazione in uscita.

Generare il codice breve#

Se lo slug è la chiave, da dove viene? Ci sono due approcci consolidati, e la maggior parte degli shortener ne usa uno o una combinazione di entrambi.

Il primo è la codifica base62 di un ID del database. Ogni nuovo link ottiene un ID intero auto-incrementante dal database. Quell'intero viene codificato in base62, che usa i 62 caratteri URL-safe a-z, A-Z e 0-9. L'ID 1 diventa b, l'ID 125 diventa un codice a due caratteri, e così via. Base62 è denso: tre caratteri coprono circa 238.000 link, cinque caratteri coprono circa 916 milioni, sei ne coprono circa 56 miliardi. I codici rimangono brevi e, poiché si mappano uno-a-uno su ID univoci, non entrano mai in collisione. Il compromesso è che gli ID sequenziali sono indovinabili, quindi molti sistemi mescolano o sfalsano lo spazio degli ID prima della codifica.

Il secondo approccio è la generazione casuale. Scegli una stringa casuale di lunghezza fissa dallo stesso alfabeto, poi controlla il database per confermare che non sia già presa. Se c'è una collisione, genera un'altra. Le collisioni sono rarissime a lunghezze ragionevoli, quindi il ciclo di retry quasi non gira mai. Gli slug casuali non sono enumerabili, il che è l'argomento di sicurezza a loro favore.

Gli slug personalizzati, quelli brandizzati come elido.me/spring-sale, si appoggiano su entrambi gli schemi. L'utente fornisce la stringa; lo shortener la valida per i caratteri consentiti e controlla l'unicità rispetto allo stesso indice prima di salvare. Che lo slug sia generato o scelto a mano, finisce nello stesso posto: una colonna univoca nel datastore.

Dove vivono i dati#

Il mapping slug-destinazione ha bisogno di un posto che possa rispondere a "dove punta questo slug" rapidamente e in modo coerente. Per la fonte di verità, quel posto è quasi sempre un database relazionale. Elido usa Postgres, con lo slug memorizzato come colonna indicizzata univoca affinché una ricerca sia una singola lettura per chiave piuttosto che una scansione della tabella. Postgres contiene il record canonico per ogni link, utente e workspace.

Ma accedere a Postgres ad ogni singolo clic sarebbe dispendioso dato il rapporto read-heavy. Una ricerca per chiave di slug in Postgres richiede tipicamente da uno a tre millisecondi, il che suona veloce finché non lo moltiplichi per il volume di clic di un link virale e ricordi che una connessione al database è una risorsa finita. Quindi gli shortener di produzione mettono una cache davanti al database. La cache è dove la maggior parte delle letture viene effettivamente servita; il database è il fallback per tutto ciò che la cache non ha ancora.

Diagramma di flusso: una GET del browser per elido.me/abc123 controlla la cache LRU L1 in-process, cade nella cache Redis L2 in caso di miss, poi in una chiamata gRPC all'origine verso api-core su cold miss, e restituisce un redirect 302 con la destinazione nell'header Location

Elido esegue una cache a due livelli davanti a Postgres. Il primo livello è una cache LRU in-process che vive nel binario di redirect stesso, che restituisce una destinazione in poche centinaia di nanosecondi senza alcun hop di rete. Il secondo livello è un cluster Redis nella stessa regione, che serve in meno di un millisecondo. Solo un cold miss, uno slug che nessuno dei due livelli ha visto di recente, cade in una chiamata gRPC all'origine verso api-core, che legge Postgres. Il tasso di hit combinato su entrambi i livelli di cache si attesta intorno al 99,4%, quindi il database viene toccato su circa una richiesta ogni 167. La descrizione completa di come si comporta quella cache, inclusa la sua politica di eviction e i failure mode che abbiamo incontrato, è nel nostro post sulla strategia di cache.

Cos'è effettivamente un redirect HTTP#

Una volta che lo shortener ha la destinazione, deve restituirla al browser. Lo fa con un redirect HTTP, che è solo un tipo specifico di risposta. Invece di restituire contenuto della pagina con un 200 OK, il server restituisce un codice di stato 3xx e un header Location che nomina l'URL reale. Sul filo la risposta è piccola:

HTTP/1.1 302 Found
Location: https://shop.example.com/spring-collection
Content-Length: 0

Il browser legge il codice di stato, vede un header Location, e immediatamente invia una nuova richiesta a quell'indirizzo. Per la persona che clicca, sembra una navigazione; sotto il cofano sono due richieste, con il link corto che funge da rapida directory lookup nel mezzo. La semantica di ogni codice 3xx è definita nell'RFC 7231, e la guida di Mozilla sui redirect HTTP è il riferimento pratico più chiaro su quale codice fa cosa.

Il corpo è vuoto perché non c'è nulla da renderizzare. L'intero payload è la riga di stato e l'header. Questo è in parte il motivo per cui i redirect sono economici da servire: non c'è template, nessun join al database per il contenuto, nessun markup. Risolvi lo slug, imposta un header, invia.

301 vs 302: la scelta che decide i tuoi analytics#

Ecco dove gli shortener prendono silenziosamente una decisione che la maggior parte degli utenti non vede mai ma che determina se il loro link è modificabile e tracciabile. Il codice di stato del redirect non è una formalità. Le due opzioni comuni si comportano in modo molto diverso.

Un 301 Moved Permanently dice al browser, e a ogni proxy e CDN tra te e il server, che questo link corto punterà sempre allo stesso posto. Quindi lo memorizzano nella cache. Un 301 viene archiviato aggressivamente. La prossima volta che il visitatore clicca quel link corto, il suo browser potrebbe risolverlo dalla cache e non contattare mai lo shortener. È ottimo per eliminare un round trip, ed è un disastro per uno strumento per link, perché si rompono due cose. Primo, gli analytics diventano ciechi: i clic serviti dalla cache del browser non raggiungono mai il server, quindi non vengono mai contati. Secondo, la destinazione è effettivamente bloccata. Se cambi dove punta il link, chiunque abbia già memorizzato il 301 nella cache continua ad atterrare sul vecchio indirizzo finché la loro cache non scade, il che non controlli tu.

Confronto affiancato di un redirect permanente 301 (memorizzato nella cache del browser, analytics ciechi, destinazione difficile da cambiare) contro un redirect temporaneo 302 (ri-richiesto ogni volta, analytics funzionanti, destinazione modificabile)

Un 302 Found (e il suo cugino più rigoroso 307 Temporary Redirect) dice al browser che è temporaneo: torna a chiedere la prossima volta. Il browser non memorizza il mapping nella cache, quindi ogni clic ri-richiede il link corto dal server. Quell'ulteriore round trip è esattamente ciò che uno strumento per link vuole. Ogni clic raggiunge la tua infrastruttura, quindi ogni clic è contabile, e poiché il server risolve la destinazione di fresco ogni volta, puoi cambiare dove punta un link e far sì che il nuovo target entri in vigore al clic successivo. Il costo è un round trip di rete per clic, che un edge ben costruito mantiene nei millisecondi a una cifra.

Questo è il motivo per cui Elido usa il 302 come default. Le destinazioni modificabili e i dati di clic accurati sono l'intero punto di un link gestito, e un 301 scambia entrambi per un'ottimizzazione della cache che di solito non vuoi. RFC 7231 specifica che un 301 è memorizzabile nella cache per default mentre un 302 non lo è a meno che gli header non lo dicano diversamente, che è esattamente il comportamento che i due casi d'uso richiedono. Ci sono casi limitati in cui un redirect permanente è corretto, una vera migrazione di dominio, per esempio, ma per link corti tracciabili e modificabili il redirect temporaneo è il default corretto.

Caching all'edge per bassa latenza#

Un redirect è sincrono e bloccante. Il browser del visitatore si blocca sul link corto finché il redirect non arriva, e solo allora può iniziare a caricare la pagina che conta davvero. Ogni millisecondo trascorso a risolvere lo slug è un millisecondo aggiunto all'attesa del visitatore. Ecco perché gli shortener seri spingono la ricerca il più vicino possibile al visitatore.

Elido esegue il gestore di redirect a punti di presenza edge a Francoforte, Ashburn e Singapore, con il traffico instradato al più vicino. Il gestore è scritto in Go su fasthttp, scelto perché il suo percorso di richiesta zero-allocation mantiene le pause di garbage collection prevedibili sotto carico sostenuto. Combinato con la cache in-memory, questo mantiene i redirect a un p95 inferiore a 15ms su un cache hit, misurato al POP: circa 4,8ms mediano a Francoforte, che sale a circa 14ms p95 a Singapore dove la geografia è più ampia. La maggior parte di quel budget è il transito fisico di rete, la distanza inevitabile tra il visitatore e il POP più vicino, che è la parte che non puoi ottimizzare via software. Abbiamo documentato il budget di latenza completo e come ogni regione si misura nel post p95-sotto-15ms.

Mettere la ricerca all'edge piuttosto che su un singolo server centrale è la differenza tra un redirect che sembra istantaneo e uno che aggiunge uno stallo visibile. È anche il motivo per cui il routing edge anycast batte una configurazione solo-DNS per questo workload, un confronto che approfondiamo in edge POP vs routing solo-DNS. La versione breve: la rete fa la geografia, e la cache fa la velocità.

Contare il clic senza rallentare il redirect#

Uno shortener che solo reindirizzasse sarebbe un servizio di redirect. Ciò che lo rende uno strumento di gestione dei link è che conta ogni clic e ti dice chi ha cliccato, da dove, su quale dispositivo. La parte difficile è farlo senza far aspettare il visitatore.

La risposta è disaccoppiare completamente i due. Quando il gestore di redirect risolve uno slug, invia immediatamente la risposta 302. La registrazione del clic avviene dopo, come lavoro fire-and-forget. Il gestore aggiunge un evento di clic, slug, timestamp, un IP troncato, un hash dello user-agent, a una coda di messaggi e va avanti. Non aspetta che la scrittura venga confermata. Se la coda è temporaneamente non disponibile, il clic viene perso piuttosto che il redirect essere ritardato; abbiamo fatto la scelta deliberata che perdere un clic in caso di guasto dell'infrastruttura è accettabile e far fallire un redirect non lo è.

Elido usa Redpanda come quella coda. Un consumer separato, il click ingester, legge gli eventi dalla coda e li scrive in ClickHouse, un database colonnare costruito per il tipo di workload ad alto volume di append-and-aggregate che è l'analytics dei clic. Il redirect del visitatore è già stato completato millisecondi prima; la riga di analytics arriva pochi secondi dopo, completamente fuori dal percorso caldo. Spieghiamo il design della coda in ingestione di clic fire-and-forget e perché un datastore colonnare batte Postgres per questo in perché usiamo ClickHouse per gli analytics dei clic.

Questo disaccoppiamento è il motivo per cui i tuoi analytics possono essere dettagliati senza essere lenti. Il percorso di redirect rimane snello perché nessun conteggio, scoring o aggregazione avviene mentre il visitatore aspetta.

Mettere tutto insieme#

Un URL shortener, dall'inizio alla fine, è una sequenza breve. Crei un link, e lo shortener memorizza un mapping slug-destinazione in Postgres, generando o validando lo slug come chiave univoca. Un visitatore clicca, la richiesta atterra al POP edge più vicino, e il gestore risolve lo slug da una cache in-memory, cadendo in Redis e poi nel database solo in caso di miss. Restituisce un 302 con la destinazione nell'header Location, così il clic è contabile e la destinazione rimane modificabile. Poi invia l'evento di clic in una coda perché un consumer separato lo memorizzi, senza far aspettare nessuno.

Ogni parte è individualmente semplice. L'ingegneria è nei rapporti e nei budget: un workload read-heavy che vuole una cache, un tetto di latenza che vuole l'edge, e un requisito di analytics che vuole stare fuori dal percorso caldo. Se vuoi costruire su questo direttamente, la funzionalità Smart Links espone il livello delle regole, le API e SDK ti permettono di coniare link dal codice, e la pagina delle soluzioni developer e i documenti di architettura edge-redirect approfondiscono i meccanismi. Se stai valutando uno strumento piuttosto che costruirne uno, i nostri articoli su cos'è un URL shortener e se gli URL shortener sono sicuri coprono il terreno dal lato dell'utente, e la pagina dei prezzi spiega dove finisce il piano gratuito.

Correlati nel blog#

Prova Elido

Accorciatore di URL ospitato nell'UE: domini personalizzati, analisi approfondite e API aperta. Piano gratuito - senza carta di credito.

Tag
how do url shorteners work
url shortener mechanics
301 vs 302 redirect
url shortener database
short link lookup
url shortener architecture

Continua a leggere