← l-artikli kollha

System Design

Nidissinja URL Shortener bħala Proġett ta' Tagħlim

URL shortener f'.NET 9, tliet kopji wara nginx, sostnuta minn Cassandra u cached b'Redis, mibnija biex verament nifhem video ta' system design minflok sempliċiment nararah.

Nidissinja URL Shortener bħala Proġett ta' Tagħlim

Rajt video ta' system design dwar kif tiddisinja URL shortener, u minflok sempliċiment nararah, iddeċidejt li verament nibnih: URL shortener reali f'.NET 9, sostnut minn Cassandra, cached b'Redis, jgħix wara load balancer nginx, b'biċċa jaħdem fuq Azure.

X'tagħmel verament

Żewġ endpoints. POST /shorten tieħu URL twil u tirritorna kodiċi qasir, xi ħaġa bħal jxs3y2C. GET /{code} tirredirezzjona għall-URL oriġinali. Dik hija s-superfiċje kollha. Kull ma huwa interessanti jinsab f'dak li jagħmilha malajra u kapaċi tlaħħaq ma' traffiku reali.

Tliet kopji, ebda memorja komuni

L-app taħdem bħala tliet kopji identiċi wara nginx, li jaqsam it-traffiku bejniethom. It-tliet kopji kollha jitkellmu mal-istess database Cassandra u l-istess cache Redis, u l-ebda waħda minnhom iżżomm xejn fil-memorja tagħha stess. Liema kopja twieġeb talba, tara l-istess dejta bħaż-żewġ oħrajn, u dak hu li jħallini nżid aktar kopji meta t-traffiku jikber minflok nerġa' nfassal xi ħaġa.

Endpoint żgħir /whoami jipprova li r-rotazzjoni verament qed isseħħ:

for i in {1..9}; do curl -s http://localhost:8080/whoami; echo; done
{"server":"a0988b503bdf"}   # l-ewwel kopja
{"server":"da9800fda566"}   # it-tieni kopja
{"server":"e6299466c10c"}   # it-tielet kopja
{"server":"a0988b503bdf"}   # l-ewwel kopja mill-ġdid...

Kodiċijiet qosra b'Base62

Il-kodiċi jrid ikun qasir u uniku, għalhekk jiġi ġġenerat minn alfabett ta' 62 karattru, ċifri flimkien ma' ittri żgħar u kbar:

private const string Alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const int CodeLength = 7;

public string Generate()
{
    var buffer = new char[CodeLength];
    for (int i = 0; i < CodeLength; i++)
    {
        buffer[i] = Alphabet[Random.Shared.Next(Alphabet.Length)];
    }
    return new string(buffer);
}

Seba' karattri minn dak l-alfabett ikopru madwar 3.5 triljun kodiċi possibbli.

Għaliex Cassandra

Shortener dejjem ifittex l-affarijiet b'mod wieħed biss: agħtini l-URL għal dan il-kodiċi. Cassandra hija mibnija eżatt għal dak it-tip ta' lookup, u tista' tifrex id-dejta fuq ħafna magni hekk kif tikber. Hija verament overkill għal proġett tant żgħir, database SQL waħda tagħmel ix-xogħol tajjeb, iżda l-punt li nibni dan kien li verament naħdem mad-database li jaqbel mal-verżjoni tal-problema fid-dinja reali.

Inżid cache mingħajr ma nmiss xejn ieħor

Kull redirect kien ikun imur direttament għad-database, minkejja li link popolari tagħti lura eżattament l-istess tweġiba kull darba. Redis iżomm il-links "sħan" fil-memorja biex klikkijiet ripetuti jiġu mwieġba minnufih u d-database tinżamm kwieta.

Il-parti li togħġobni l-aktar dwar kif spiċċat din: il-cache hija decorator madwar is-saff tad-database, mhux tfassil mill-ġdid tiegħu. CachedUrlStore timplimenta l-istess interface IUrlStore bħall-istore Cassandra sempliċi, tiċċekkja Redis l-ewwel, u taqa' lura fuq id-database biss meta jkun hemm miss:

public async Task<ShortenedUrl?> GetByCodeAsync(string code)
{
    var key = $"url:{code}";

    var cached = await _cache.StringGetAsync(key);
    if (cached.HasValue)
        return JsonSerializer.Deserialize<ShortenedUrl>(cached!);

    var entity = await _inner.GetByCodeAsync(code);
    if (entity is null)
        return null;

    await _cache.StringSetAsync(key, JsonSerializer.Serialize(entity), Ttl);
    return entity;
}

Li taqleb bejn "database biss" u "database u cache" hija bidla ta' linja waħda f'kif is-servizzi jiġu konnessi. Xejn li jsejjaħ IUrlStore m'għandu bżonn jaf jew jimpurtah ma' liema wieħed verament qed jitkellem.

301 vs 302, u għaliex jimporta hawn

Redirect 301 jiġi cached mill-browser, li huwa veloċi iżda jfisser li qatt ma nista' ngħodd klikkijiet mill-ġdid wara l-ewwel wieħed. 302 dejjem jerġa' jgħaddi mis-server tiegħi, ftit aktar bil-mod, iżda jfisser li nista' nsegwi klikkijiet u nbiddel id-destinazzjoni aktar tard jekk ikolli bżonn. Għażilt 302 biex inżomm l-analytics possibbli.

Nittrasferixxi l-cache lejn Azure

Il-cache Redis issa taħdem fuq Azure Cache for Redis minflok container lokali. Azure jieħu ħsieb il-manutenzjoni, u jien nikseb inqas kontroll u ftit spejjeż bi skambju. Tikkonnettja biss permezz ta' TLS, il-password tgħix f'secret store lokali minflok fir-repo, u l-aċċess pubbliku jrid jitqabbad deliberatament, li huwa d-default it-tajjeb.

X'qed nieħu minn dan

Li nibnih minflok sempliċiment nararah huwa dak li verament għamel il-biċċiet jibqgħu f'moħħi: kif jaħdmu l-kodiċijiet qosra Base62, kif Cassandra żżomm il-lookups malajrin fuq skala kbira, għaliex cache tmur quddiem database u mhux minflokha, u għaliex li żżomm l-app ħielsa mill-memorja tagħha stess huwa dak li jagħmel il-load balancing possibbli minn kollox. Docker Compose ibda s-sitt biċċiet kollha, tliet kopji tal-app, Cassandra, Redis, u nginx, b'kmand wieħed, u dan waħdu għallimni aktar dwar kif dawn il-biċċiet verament jgħaqqdu ma' xulxin milli kwalunkwe dijagramma.

Il-pass li jmiss huwa li nittrasferixxi d-database innifisha lejn il-cloud. Azure Cosmos DB titkellem il-protokoll ta' Cassandra, għalhekk dan għandu jfisser l-istess kodiċi ippuntat lejn indirizz differenti, u eventwalment li nospita l-app innifisha f'Azure Container Apps b'autoscaling reali minflok tliet kopji fissi fuq il-laptop tiegħi.