Siete assetati di ulteriori approfondimenti da parte di esperti?

Iscriviti alla nostra newsletter Tea O'Clock!

Abbonati

Elaborazione dei dati in tempo reale su scala: Come garantire l'integrità dei dati

Albert De Watrigant
Pubblicato su
18/6/2024
Avete mai desiderato creare un'architettura cloud serverless in grado di trasmettere i vostri dati in streaming, solo per scoprire che c'erano dei vuoti di dati tra l'input e l'output della vostra architettura? Se è così, in questo articolo cercheremo di rispondere alla vostra domanda, presentando il nostro caso d'uso per un'architettura in grado di elaborare 3.000 richieste al secondo.

Mentre un gran numero di casi di utilizzo dei dati può essere risolto con una soluzione batch, alcune situazioni richiedono necessariamente una soluzione di streaming. Con Google Cloud, è possibile ottenere questo risultato con servizi serverless, che consentono di pagare solo per le risorse effettivamente utilizzate. In questo articolo vedremo come funziona un'architettura di Google Cloud: recupera le richieste HTTPS come input, le elabora non appena arrivano e scrive l'output di queste richieste in un data warehouse. Ci concentreremo sulla parte della nostra architettura che garantisce lo stesso numero di messaggi dell'output.

Per chiarire meglio le cose, ecco cosa fa la nostra architettura:

Con maggiori dettagli e strumenti GCP, ecco come si presenta:

Funzionamento generale

Nella nostra architettura, le richieste in arrivo vengono raccolte da una Cloud Function, che legge la richiesta, controlla se il formato è valido, quindi trasmette l'informazione tramite un messaggio inviato a un topic Pub/Sub. Abbiamo scelto di utilizzare una Cloud Function perché il codice da eseguire è molto semplice e il servizio è in grado di aumentare il numero di istanze in base all'evoluzione del traffico in entrata, senza che noi ce ne dobbiamo preoccupare.

Abbiamo quindi creato una sottoscrizione "Push" tra l'argomento Pub/Sub e il Cloud Run. Abbiamo scelto la configurazione "Push" per preservare la natura di streaming della nostra architettura. Il messaggio viene quindi elaborato dalla Cloud Run, che può richiedere un po' più di tempo rispetto alla Cloud Function, se necessario. L'esecuzione Cloud scrive quindi il risultato dell'elaborazione in una tabella BigQuery.

Punti di forza architettonici

Separando l'acquisizione dall'elaborazione, ci assicuriamo che la richiesta inviata come input dall'utente venga elaborata il più rapidamente possibile. L'elaborazione può quindi richiedere un po' più di tempo per svolgere il proprio lavoro, poiché la latenza è meno critica. Infatti, se il carico di lavoro diventa troppo pesante, la sottoscrizione Pub/Sub tratterrà i messaggi e li rimanderà indietro fino a quando il Cloud Run non li avrà elaborati.

In questo modo, possiamo garantire che l'utente non sperimenterà alcuna latenza particolare, anche durante i picchi di traffico. Possiamo anche essere sicuri che tutti i messaggi "validi" saranno elaborati dalla nostra architettura, poiché Pub/Sub garantisce che almeno una occorrenza di un messaggio sarà inviata e ricevuta dal destinatario (il Cloud Run nel nostro caso).

Problemi riscontrati

Dopo alcuni giorni di test, ci siamo resi conto che la nostra architettura stava effettivamente elaborando tutti i messaggi in arrivo, ma che alcuni messaggi erano presenti in più copie nella tabella BigQuery. Ciò significa che lo stesso messaggio veniva elaborato più volte dalla nostra esecuzione Cloud. Perché? Dopo una serie di indagini, abbiamo scoperto che la maggior parte dei duplicati arrivava nella tabella BigQuery nei momenti di picco del traffico. Durante questi picchi di richieste, il numero di istanze Cloud Run aumenta rapidamente. È quindi possibile che la sottoscrizione Pub/Sub invii lo stesso messaggio a diverse istanze, poiché questo servizio garantisce ALMENO una consegna di un messaggio. Se si desidera limitare Pub/Sub all'invio di un singolo messaggio, ciò è possibile, ma solo con una sottoscrizione "Pull", il che significa che ci si trova di fronte a un'architettura Batch.

Per risolvere il problema dei messaggi duplicati nel nostro database, avevamo due possibilità: o filtrare i messaggi durante l'elaborazione, eliminando quelli che erano già stati elaborati, oppure ripulire il database utilizzando una query SQL con una certa frequenza. Abbiamo deciso di implementare la prima soluzione. Questa scelta è dovuta al fatto che volevamo mantenere il più possibile un approccio in tempo reale. Tuttavia, con l'esecuzione di una query SQL, saremmo stati costretti a lavorare in batch. Inoltre, una normale query SQL su un grande volume di dati avrebbe potuto generare costi significativi.

Soluzione

Per risolvere questo problema, abbiamo dovuto utilizzare nuovi servizi GCP, che potete vedere nel nostro diagramma di architettura.

Abbiamo creato un'istanza di Redis chiamata Memorystore su Google Cloud. Questo servizio viene utilizzato come cache: non appena un messaggio viene elaborato con successo, scriviamo l'ID del messaggio come chiave in Memorystore. Quindi, non appena arrivano nuovi messaggi, interroghiamo l'istanza di Memorystore per vedere se l'ID del messaggio è già presente nel database. Se lo è, il messaggio non viene elaborato, perché significa che è già stato elaborato da Cloud Run. Se l'ID del messaggio non è presente in Memorystore, elaboriamo il messaggio e scriviamo l'ID nell'istanza, dicendo così che il messaggio è stato appena elaborato.

Quando si scrive una chiave in Memorystore, è possibile assegnarle anche una data di scadenza. Nel nostro caso, abbiamo impostato questo valore a 15 minuti, poiché non era necessario conservare un ID di messaggio più a lungo.

Abbiamo anche utilizzato una VPC per garantire una connessione sicura tra il Cloud Run e l'istanza Memorystore. Sempre nell'ottica di rafforzare la sicurezza, abbiamo attivato il requisito di autenticazione dell'istanza Memorystore, il che significa che abbiamo bisogno di una chiave di sicurezza per poter comunicare con essa. Per memorizzare questa chiave in modo sicuro, l'abbiamo inserita nel Secret Manager di Google Cloud, che chiamiamo direttamente dal codice del Cloud Run.

Perché abbiamo usato Redis piuttosto che un altro database? Innanzitutto, volevamo un database "chiave:valore", che ci permettesse di recuperare una chiave molto rapidamente. Poi, sapendo che avevamo bisogno di queste chiavi solo per un certo intervallo di tempo, volevamo un database che ci permettesse di inserire una data di scadenza per le chiavi. Per questo abbiamo scelto il servizio Memorystore di Google Cloud. Attenzione: l'uso di Memorystore è stato utile nel nostro caso perché avevamo un grande volume di dati, ma è importante specificare che il suo utilizzo deve essere adattato al caso d'uso, perché nella sua configurazione minima, il servizio costa 70 dollari al mese.

Risultati

Dopo diverse settimane di test, la nostra architettura è riuscita a elaborare una media di 1.500 richieste al secondo, con alcuni picchi a 3.000. Abbiamo potuto osservare che il nostro sistema di controllo dei duplicati con Memorystore non aumentava affatto la latenza delle richieste elaborate da Cloud Run. Abbiamo anche scoperto che il nostro sistema ha rilevato in media tra i 5.000 e i 15.000 messaggi duplicati al giorno, con alcuni picchi di 300.000 al giorno, su un totale di circa 70 milioni di messaggi al giorno. Inoltre, tutti i messaggi vengono ora consegnati una sola volta al database.

Possibili miglioramenti

Per quanto riguarda la nostra architettura, diversi punti possono essere modificati o migliorati. 

Se la prima fase di raccolta richiede la regolazione di un gran numero di parametri (numero di richieste, numero di CPU e memoria per istanza, ecc.) e si desidera utilizzare Docker come strumento di distribuzione, allora la sostituzione della nostra Cloud Function con una Cloud Run potrebbe essere più interessante per il vostro caso d'uso.

Se volete che il vostro punto di raccolta recuperi richieste da utenti esterni geograficamente dispersi, prendete in considerazione la possibilità di impostare un Load Balancer tra gli utenti e il vostro punto di raccolta (Cloud Run o Cloud Function). Inoltre, con un Load Balancer si può facilmente integrare Cloud Armor (il WAF di Google Cloud) e gestire i nomi dei sottodomini.

Infine, se l'elaborazione dei dati è leggera o addirittura inesistente e non si vuole usare Docker per semplificare la distribuzione, si può sostituire la nostra esecuzione cloud con una funzione cloud.

Tutti gli articoli

Articoli correlati

Nessun articolo trovato.

Volete saperne di più? Iscrivetevi alla nostra newsletter mensile.

Scoprite tutte le ultime notizie, articoli, repliche di webinar e cinquantacinque eventi nella nostra newsletter mensile, Tea O'Clock.

Nome*
Cognome*
Azienda*
Lingua preferita*
Email*
 Grazie!

La tua richiesta di abbonamento è stata presa in considerazione con successo.
Oops! Qualcosa è andato storto durante l'invio del modulo.