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.
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.