Come scalare da 1 a 100 utenti

Molte startup hanno affrontato questo problema: folle di nuovi utenti si registrano ogni giorno e il team di sviluppo fatica a mantenere attivo il servizio.

È un bel problema da avere, ma sul Web sono disponibili poche informazioni chiare su come ridimensionare attentamente un'applicazione Web da zero a centinaia di migliaia di utenti. In genere esistono soluzioni antincendio o soluzioni per colli di bottiglia (e spesso entrambe). Pertanto, le persone usano tecniche piuttosto cliché per trasformare il loro progetto amatoriale in qualcosa di veramente serio.

Proviamo a filtrare le informazioni e a scrivere la formula base. Scaleremo passo dopo passo il nostro nuovo sito di condivisione di foto Graminsta da 1 a 100 utenti.

Scriviamo quali azioni specifiche devono essere intraprese quando il pubblico aumenta a 10, 100, 1000, 10 e 000 persone.

1 utente: 1 macchina

Quasi ogni applicazione, sia essa un sito web o un'applicazione mobile, ha tre componenti chiave:

  • API
  • banca dati
  • client (applicazione mobile stessa o sito web)

Il database memorizza dati persistenti. L'API serve le richieste verso e intorno a questi dati. Il client trasmette i dati all'utente.

Sono giunto alla conclusione che è molto più semplice parlare di scalabilità di un'applicazione se, dal punto di vista architetturale, le entità client e API sono completamente separate.

Quando iniziamo a creare un'applicazione per la prima volta, tutti e tre i componenti possono essere eseguiti sullo stesso server. In un certo senso, è simile al nostro ambiente di sviluppo: un ingegnere esegue il database, l'API e il client sulla stessa macchina.

In teoria, potremmo distribuirlo nel cloud su una singola istanza DigitalOcean Droplet o AWS EC2, come mostrato di seguito:
Come scalare da 1 a 100 utenti
Detto questo, se ci sarà più di un utente su un sito, ha quasi sempre senso dedicare un livello di database.

10 utenti: spostamento del database a un livello separato

La suddivisione del database in servizi gestiti come Amazon RDS o Digital Ocean Managed Database ci servirà bene per molto tempo. È un po' più costoso del self-hosting su una singola macchina o su un'istanza EC2, ma con questi servizi ottieni molte estensioni utili pronte all'uso che ti torneranno utili in futuro: backup multiregione, repliche di lettura, backup e altro ancora.

Ecco come appare il sistema adesso:
Come scalare da 1 a 100 utenti

100 utenti: spostare il client su un livello separato

Fortunatamente, ai nostri primi utenti è piaciuta molto la nostra applicazione. Il traffico sta diventando più stabile, quindi è ora di spostare il client a un livello separato. Si dovrebbe notare che separazione entità è un aspetto chiave della creazione di un'applicazione scalabile. Poiché una parte del sistema riceve più traffico, possiamo partizionarla per controllare il modo in cui il servizio si adatta in base a modelli di traffico specifici.

Questo è il motivo per cui mi piace pensare al client come separato dall'API. Ciò rende molto facile pensare allo sviluppo per più piattaforme: web, web mobile, iOS, Android, applicazioni desktop, servizi di terze parti, ecc. Sono tutti semplicemente client che utilizzano la stessa API.

Ad esempio, ora i nostri utenti chiedono molto spesso di rilasciare un'applicazione mobile. Se separi le entità client e API, ciò diventa più semplice.

Ecco come si presenta un sistema del genere:

Come scalare da 1 a 100 utenti

1000 utenti: aggiungi il bilanciatore del carico

Le cose stanno migliorando. Gli utenti Graminsta caricano sempre più foto. In crescita anche il numero delle immatricolazioni. Il nostro unico server API ha difficoltà a tenere il passo con tutto il traffico. Serve più ferro!

Il bilanciamento del carico è un concetto molto potente. L'idea chiave è mettere un bilanciatore del carico davanti all'API e distribuire il traffico alle singole istanze del servizio. Questo è il modo in cui scaliamo orizzontalmente, ovvero aggiungiamo più server con lo stesso codice, aumentando il numero di richieste che possiamo elaborare.

Posizioneremo bilanciatori del carico separati davanti al client Web e davanti all'API. Ciò significa che puoi eseguire più istanze che eseguono codice API e codice client Web. Il sistema di bilanciamento del carico indirizzerà le richieste al server meno caricato.

Qui otteniamo un altro importante vantaggio: la ridondanza. Quando un'istanza fallisce (magari è sovraccarica o si è bloccata), ne restano altre che continuano a rispondere alle richieste in arrivo. Se solo un'istanza funzionasse, in caso di guasto l'intero sistema si bloccherebbe.

Il sistema di bilanciamento del carico fornisce anche il ridimensionamento automatico. Possiamo configurarlo per aumentare il numero di istanze prima del picco di carico e diminuirlo quando tutti gli utenti dormono.

Con un bilanciatore del carico, il livello API può essere scalato quasi all'infinito, semplicemente aggiungendo nuove istanze all'aumentare del numero di richieste.

Come scalare da 1 a 100 utenti

Nota. Al momento il nostro sistema è molto simile a quello che le aziende PaaS come Heroku o Elastic Beanstalk su AWS offrono immediatamente (motivo per cui sono così popolari). Heroku inserisce il database su un host separato, gestisce un bilanciatore del carico con scalabilità automatica e consente di ospitare il client Web separatamente dall'API. Questo è un ottimo motivo per utilizzare Heroku per progetti in fase iniziale o startup: ottieni tutti i servizi di base pronti all'uso.

10 utenti: CDN

Forse avremmo dovuto farlo fin dall’inizio. L'elaborazione delle richieste e l'accettazione di nuove foto stanno iniziando a mettere a dura prova i nostri server.

In questa fase, è necessario utilizzare un servizio cloud per archiviare contenuti statici: immagini, video e molto altro (AWS S3 o Digital Ocean Spaces). In generale, la nostra API dovrebbe evitare di gestire cose come la fornitura di immagini e il caricamento di immagini sul server.

Un altro vantaggio del cloud hosting è il CDN (AWS chiama questo componente aggiuntivo Cloudfront, ma molti provider di cloud storage lo offrono immediatamente). La CDN memorizza automaticamente nella cache le nostre immagini in vari data center in tutto il mondo.

Sebbene il nostro data center principale possa trovarsi in Ohio, se qualcuno richiede un'immagine dal Giappone, il fornitore di servizi cloud ne farà una copia e la memorizzerà nel proprio data center giapponese. La prossima persona che richiederà questa immagine in Giappone la riceverà molto più velocemente. Questo è importante quando lavoriamo con file di grandi dimensioni, come foto o video, che impiegano molto tempo per essere scaricati e trasmessi in tutto il pianeta.

Come scalare da 1 a 100 utenti

100 utenti: scalare il livello dati

La CDN ha aiutato molto: il traffico cresce a pieno ritmo. Il famoso video blogger Mavid Mobrick si è appena registrato con noi e ha pubblicato la sua “storia”, come si suol dire. Grazie al bilanciamento del carico, l'utilizzo della CPU e della memoria sui server API è mantenuto basso (dieci istanze API in esecuzione), ma stiamo iniziando a ricevere molti timeout sulle richieste... da dove vengono questi ritardi?

Analizzando un po' i parametri, vediamo che la CPU sul server del database è caricata all'80-90%. Siamo al limite.

Il ridimensionamento del livello dati è probabilmente la parte più difficile dell'equazione. I server API servono richieste stateless, quindi aggiungiamo semplicemente più istanze API. Naso a maggioranza i database non possono farlo. Parleremo dei più diffusi sistemi di gestione di database relazionali (PostgreSQL, MySQL, ecc.).

Caching

Uno dei modi più semplici per aumentare le prestazioni del nostro database è introdurre un nuovo componente: il livello cache. Il metodo di memorizzazione nella cache più comune è un archivio di record di valori-chiave in memoria, come Redis o Memcached. La maggior parte dei cloud dispone di una versione gestita di questi servizi: Elasticache su AWS e Memorystore su Google Cloud.

Una cache è utile quando un servizio effettua molte chiamate ripetute al database per recuperare le stesse informazioni. In sostanza, accediamo al database solo una volta, memorizziamo le informazioni nella cache e non le tocchiamo più.

Ad esempio, nel nostro servizio Graminsta, ogni volta che qualcuno accede alla pagina del profilo della star Mobrik, il server API interroga il database per ottenere informazioni dal suo profilo. Questo accade ancora e ancora. Poiché le informazioni del profilo di Mobrik non cambiano ad ogni richiesta, è eccellente per la memorizzazione nella cache.

Metteremo nella cache i risultati dal database in Redis per chiave user:id con un periodo di validità di 30 secondi. Ora, quando qualcuno accede al profilo di Mobrik, controlliamo prima Redis e, se i dati sono presenti, li trasferiamo semplicemente direttamente da Redis. Ora le richieste al profilo più popolare sul sito praticamente non caricano il nostro database.

Un altro vantaggio della maggior parte dei servizi di memorizzazione nella cache è che sono più facili da scalare rispetto ai server di database. Redis dispone di una modalità Cluster Redis integrata. Simile a un bilanciatore del carico1, ti consente di distribuire la cache Redis su più macchine (su migliaia di server, se necessario).

Quasi tutte le applicazioni su larga scala utilizzano la memorizzazione nella cache; è una parte assolutamente integrante di un'API veloce. Un'elaborazione delle query più rapida e un codice più produttivo sono tutti importanti, ma senza una cache è quasi impossibile adattare un servizio a milioni di utenti.

Leggi le repliche

Quando il numero di query al database aumenta notevolmente, un'altra cosa che possiamo fare è aggiungere repliche di lettura nel sistema di gestione del database. Con i servizi gestiti sopra descritti, questo può essere fatto con un clic. La replica di lettura rimarrà aggiornata nel database principale ed è disponibile per le istruzioni SELECT.

Ecco il nostro sistema ora:

Come scalare da 1 a 100 utenti

Passi successivi

Man mano che l'applicazione continua a crescere, continueremo a separare i servizi per scalarli in modo indipendente. Ad esempio, se iniziamo a utilizzare Websocket, è logico inserire il codice di elaborazione Websocket in un servizio separato. Possiamo posizionarlo su nuove istanze dietro il nostro sistema di bilanciamento del carico, che può scalare su e giù in base alle connessioni Websocket aperte e indipendentemente dal numero di richieste HTTP.

Continueremo inoltre a combattere le restrizioni a livello di database. È in questa fase che è il momento di studiare il partizionamento e lo sharding del database. Entrambi gli approcci richiedono un sovraccarico aggiuntivo, ma consentono di ridimensionare il database quasi all'infinito.

Vogliamo anche installare un servizio di monitoraggio e analisi come New Relic o Datadog. Ciò ti aiuterà a identificare le query lente e a capire dove è necessario migliorare. Man mano che cresciamo, vogliamo concentrarci sull'individuazione dei colli di bottiglia e sulla loro eliminazione, spesso utilizzando alcune delle idee delle sezioni precedenti.

fonti

Questo post è ispirato a uno di i miei post preferiti sull'elevata scalabilità. Volevo rendere l'articolo un po' più specifico per le fasi iniziali dei progetti e slegarlo da un unico fornitore. Assicurati di leggere se sei interessato a questo argomento.

note

  1. Sebbene simile in termini di distribuzione del carico su più istanze, l'implementazione sottostante di un cluster Redis è molto diversa da un bilanciatore del carico. [ritorno]

Come scalare da 1 a 100 utenti

Fonte: habr.com

Aggiungi un commento