hadoop ecosystem - crs4dassia.crs4.it/.../2014/11/dassia4_hadoopecosystem.pdf · 2015-05-04 ·...
TRANSCRIPT
Hadoop Ecosystem
Hadoop Ecosystem
Richiamo concetti precedenti
Hadoop Ecosystem
The Four V
Hadoop Ecosystem
HDFS: a distributed filesystem
Hadoop Ecosystem
MapReduce: a dataflow programming framework
Hadoop Ecosystem
MapReduce: a “simplified” data processing language
Hadoop Ecosystem
Hadoop Scripting
Pig
Hadoop Ecosystem
NoSQL
Hadoop Ecosystem
Dunque...
● sappiamo cosa si intende per BigData;
● sappiamo dove memorizzare questi dati;
● sappiamo come elaborarli in modalità batch con MapReduce;
● possiamo usare linguaggi di scripting come Pig e Hive, che ci tengono al riparo
dalla complessità del MapReduce;
● sappiamo anche come memorizzare i risultati di queste elaborazioni su sistemi
NoSQL che ci garantiscono una elevata scalabilità orizzontale nell'accesso ai dati;
Ma ...
● come raccogliamo i dati ?
● come li trasferiamo su HDFS ?
● come creiamo un dataflow ?
Hadoop Ecosystem
Data Acquisition
Data Acquisition
Sorgenti distribuite ed eterogenee
BigData System
externaldata
services
LANWAN
Data Acquisition
Producer-Consumer Pattern
Le sorgenti di dati di BigData System sono variegate, distribuite e tipicamente non
sono sotto il controllo degli sviluppatori.
Tuttavia, essendo l'approvvigionamento dei dati un problema comune, è naturale che
nell'ecosistema Hadoop si siano sviluppate delle tecnologie che facilitano l'acquisizione
e l'accentramento di dati provenienti da sorgenti esterne.
E poiché la maggior parte di esse si basa sul modello produttore-consumatore è utile
introdurre il modello generale in modo da poterci poi concentrare solo sugli aspetti
particolari che caratterizzano ciascuna tecnologia.
Data Acquisition
Producer-Consumer Pattern
Il modello produttore-consumatore è nato per risolvere un problema di
sincronizzazione tra processi :
un processo che produce dei dati
● un processo che elabora quei dati
Tipicamente questi processi sono implementati come dei loop e, altrettanto tipicamente,
la velocità con cui i dati sono prodotti è diversa da quella con cui vengono elaborati.
Questo impone un qualche meccanismo di sincronizzazione.
Channelproducer consumer
Data Acquisition
Producer-Consumer Pattern
La strategia generale è quella di implementare il Channel come una coda in cui i dati
prodotti dal Produttore possono essere accumulati in attesa che il Consumatore li
elabori.
La coda ha, per definizione, una capacità limitata per cui :
● se il problema che si sta trattando consente di interrompere la generazione dei dati,
quando la coda è piena il produttore può fermarsi ed attendere che il consumatore
svuoti la coda;
● se questo non è possibile (ad esempio perché produttore e consumatore sono due
applicazioni indipendenti che nulla sanno l'uno dell'altro) allora in caso di coda
piena i nuovi dati verranno scartati fino a che non si libererà dello spazio nella
coda.
Data Acquisition
Producer-Consumer Pattern
Effettuare un opportuno dimensionamento della coda diventa un passo fondamentale
per ridurre al minimo la probabilità che parte dei dati vada perduta.
Una generalizzazione del modello prevede la presenza di più produttori che immettono
dati su una coda e di più consumatori che elaborano i dati presenti nella coda.
Si possono avere così casi in cui
● ogni messaggio viene elaborato da un solo consumatore, e dunque più
consumatori collaborano per evitare che la coda saturi
● ogni messaggio viene elaborato da tutti i consumatori, creando in tal modo una
biforcazione nella pipeline (o nel dataflow)
Data Acquisition
Producer-Consumer Pattern
Questo secondo caso pone però nuovi problemi, ad esempio
come garantisco che il messaggio venga rimosso dalla coda solo dopo esser stato correttamente elaborato da tutti i consumatori ?
I vari tool che vedremo implementano strategie diverse per cercare di rispondere in
maniera efficace a queste ed altre problematiche.
Data Acqusition
Flume
Data Acquisition
Data are business
L'idea alla base del mondo BigData è che
i dati sono una ricchezza
tutti i dati sono una ricchezza
dunque
essere in grado di raccogliere, collezionare ed elaborare dati
dei tipi più svariati e in qualunque quantità
significa
avere la possibilità di creare molti nuovi business.
Data Acquisition
Gestione Manuale
Dovendo gestire manualmente l'acquisizione e l'accentramento dei dati, si dovrebbero
realizzare degli script o dei servizi dedicati da installare nelle diverse macchine da
monitorare. Questo approccio presenta diversi problemi:
● manutenzione onerosa, in quanto gli script o i servizi andrebbero scritti e modificati
di volta in volta ed adattati continuamente ad ogni modifica sulla macchina;
● difficoltà nella gestione
● monitoraggio dell'esecuzione
● compressione dei dati
● uniformità nel formato dei dati prodotti
● probabilità di avere delay elevati
● onere e complessità della scalabilità completamente a nostro carico
Flume nasce per risolvere questi problemi.
Flume
Architettura
L'architettura di Flume si basa sul concetto di Agent che, come si può vedere
dall'illustrazione, è un'implementazione del modello produttore consumatore.
Nelle prossime slide illustreremo i concetti fondamentali
dell'architettura.
FLUME AGENT
CHANNEL
SOURCE SINKRiceve EVENTI
Destinazione, es HDFS
Inoltra EVENTI
WEB
Client
Flume
Eventi
In Flume
un evento è un “dato” prodotto da una sorgente esterna
ed inoltrato alla destinazione finale
materialmente
è un bytearray composto da un header e un payload
FLUME AGENT
CHANNEL
SOURCE SINKRiceve EVENTI
Destinazione, es HDFS
Inoltra EVENTI
WEB
Client
Event
Il “dato esterno” viene inserito nel payload e rimane opaco per Flume.
Tutte le informazioni necessaria alla gestione ed elaborazione degli eventi vengono
accumulate nell'header sotto forma di coppie chiave-valore.
PAYLOADbyte[]
HEADERMap< String, String>
EVENT
Flume
Client
Può essere un processo indipendente che colleziona dati prodotti da applicazioni esterne secondo
formati standard (es. Apache, Log4j, ...) oppure, qualora non sia possibile usare uno dei client
disponibili, può essere sviluppato ad hoc o integrato in una propria applicazione.
Flume espone un interfaccia RPC basata su Apache Thrift per cui il client può esser scritto nella
maggior parte dei linguaggi di programmazione.
FLUME AGENT
CHANNEL
SOURCE SINKRiceve EVENTI
Destinazione, es HDFS
Inoltra EVENTI
Client
Un Client è
un'entità che raccoglie i dati, li incapsula in eventi
e li invia ad uno o più Agenti
Flume
Agent
I componenti fondamentali di un Agent sono tre
● il source (produttore)
● il channel (coda)
● il sink (consumatore)
Tuttavia il concetto di Agent è un modello teorico, per cui un singolo processo che opera
come Agent può gestire più source, più channel e più sink contemporaneamente.
FLUME AGENT
CHANNEL
SOURCE SINKRiceve EVENTI
Destinazione, es HDFS
Inoltra EVENTI
WEB
Client
L'Agent è il cuore di Flume, e si occupa di
ricevere gli eventi generati da un client esterno,
accumularli in una coda interna, e inviarli ad una o più
destinazioni finali
Flume
Agent
Per ragioni di ottimizzazione il Source ha una coda interna in cui raccoglie gli eventi
prima di inviarli al canale in gruppi di dimensioni prefissate (configurabili).
Il Canale è un componente passivo che si limita a
● conservare quanto ricevuto dal Source
● inviare quanto richiesto dal Sink
Il Sink invia delle richieste periodiche al Channel e quando ci sono sufficienti dati
disponibili richiede un blocco di eventi di dimensioni prefissate (configurabili) che poi
inoltra ad una destinazione remota.
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Flume
Agent : Source
Un Source deve essere collegato ad almeno un canale.
Esistono tre tipologie principali di Source
● standard : ricevono gli eventi dall'esterno via HTTP o RPC (Avro, Thrift)
● exec: eseguono un comando via shell che genera i dati sullo STDOUT
● monitor: monitora una cartella locale e ogni qual volta viene aggiunto un file genera
un evento che contiene il file stesso
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Il Source è il componente interno all'Agent
che si occupa di ricevere gli eventi esterni
e memorizzarli in uno o più canali.
Flume
Agent : Source - Internals
All'interno di un Source troviamo tre
componenti:
● un Channel Processor che coordina tutte le
attività interne di elaborazione degli
eventi;
● un insieme di Interceptor che operano
come dei filtri ed aggiungono dei tag
all'header dell'evento per caratterizzane il
contenuto;
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Channel Processor Interceptor 1
Interceptor 2Interceptor 3
Channel SelectorInterceptor N
SOURCE
● esistono degli interceptor predefiniti (che aggiungono ad esempio un timestamp,
l'hostname, e simili) ma è anche possibile di definirne dei propri che aggiungono
tag in base al contenuto del payload
Flume
Agent : Source - Internals
● un Channel Selector che in base ai dati
contenuti nell'header identifica i canali su
cui andrà inoltrato l'evento.
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Channel Processor Interceptor 1
Interceptor 2Interceptor 3
Channel SelectorInterceptor N
SOURCE
Flume
Agent : Source – Event Lifecycle
Flume
Agent : Channel
● un Channel può ricevere dati da più Source e può fornire dati a più Sink
● esistono tre principali tipologie di Channel che si differenziano per il modello di
persistenza:
● MemoryChannel (volatile): conserva i dati in RAM col rischio che vadano
perduti in cado di riavvio del processo;
● FileChannel (durable): conserva i dati su file
● JDBCChannel (durable): conserva i dati su una tabella di un DB
● I Channel sono “transazionali” ovvero garantiscono che tutto ciò che viene scritto
dal Source venga poi letto dal Sink
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Il Channel è un componente passivo che opera
come buffer tra il Source e il Sink.
Flume
Agent : Channel - Transactions
A: garanzia di scrittura dei dati del Source
● una singola transazione può scrivere più di un evento
● se la transazione fallisce tutti gli eventi della transazione vengono riscritti sul channel
La ripetizione della transazione può portare alla creazione di duplicati sul canale.
Flume non gestisce la rimozione di eventi duplicati, sarà dunque cura dell'utilizzatore
finale rimuoverli. E' quindi prassi comune inserire nell'header, attraverso un custom
interceptor, un ID che identifichi univocamente ogni evento.
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Il Channel non è fully transactional, ma solo le
operazioni di scrittura e lettura (singolarmente)
sono racchiuse in una transazione.
Flume
Agent : Channel - Transactions
B: garanzia di lettura dei dati da parte del Sink
● la transazione garantisce che ogni evento venga letto dal Sink e scritto sulla
destinazione esterna
La transazione si considera completata con successo solo dopo aver ricevuto la
conferma che la scrittura sulla destinazione esterna sia avvenuta con successo.
In questo modo si ha la garanzia che nessun dato venga perso nella pipeline gestita da
Flume stesso.
SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Il Channel non è fully transactional, ma solo le
operazioni di scrittura e lettura (singolarmente)
sono racchiuse in una transazione.
Flume
Agent : Sink
● esistono Sink predefiniti per una gran varietà di destinazioni differenti
● HDFS, Hbase, Solr, ElasticSearch
● File, Logger
● Flume Agent (Avro, Thrift)
● è possibile creare dei Sink Personalizzati implementando l'interfaccia CustomSink
Il SinkProcessor decide, in base al contenuto dell'header, se un particolare evento
debba esser inoltrato o meno su una certa destinazione esterna.
Il Sink recupera gli eventi conservati sul Channel
e li inoltra verso una destinazione esterna SourceRiceve
eventiChannel
PushSink
Inoltra
eventi
Read
Poll
Agent
Flume
Agent Pipelines
Qualora si debbano trasferire grandi quantità di eventi attraverso network differenti (ad
esempio attraverso una WAN con banda limitata e latenza elevata) è possibile creare
delle pipeline per avere diversi livelli di aggregazione ed avere maggiore flessibilità
nella configurazione del sistema.
E' possibile costruire delle pipelines di Agent
facendo si che un Sink invii gli eventi ad un altro Agent
SOURCE
SINKChannel
SOURCE
SINKChannel
Flume
Agent Pipelines
Un eccessivo ritardo nella scrittura sulla destinazione finale porterebbe alla saturazione
del canale più a valle nella Pipeline.
A cascata questo si ripercuoterebbe nel tier precedente della pipeline ma consentirebbe
di ridurre il rischio di perdita di dati.
Non appena la scrittura sulla destinazione finale riprende entrambe le code iniziano
nuovamente a svuotarsi.
SOURCE
SINKChannel
SOURCE
SINKChannel
E' importante dimensionare correttamente la capacità
del Channel
Flume
Complex Flows
Flume
Complex Flows
Flume
Complex Flows
Flume
Complex Flows
Realizzare un flusso di dati convergente verso
la destinazione.
● Il traffico dei dati viene aggregato da Tier-2 e Tier-3 prima di inserirlo nella detinazione;
● Più un Tier è vicino alla destinazione e maggiore è la dimensione dei dati in ogni operazione batch.
Tier-1 Tier-2 Tier-3
Messaging System
Kafka
Kafka
A high throughput distributed messaging system
Kafka, come Flume, si basa sul modello produttore-consumatore e per tale ragione ha
un'architettura generale simile.
Tuttavia il suo posizionamento in un BigData System è leggermente diverso ed infatti gli
stessi sviluppatori non lo definiscono come un sistema per l'acquisizione dei dati ma
un sistema distribuito per la gestione dei messaggi ad altissime prestazioni.
Infatti l'obiettivo principale di Flume è quello di raccogliere dati da macchine remote,
aggregarli e memorizzarli in maniera persistente (e indipendente ..quasi batch) sul cluster
per future elaborazioni.
Kafka si propone invece come sistema per lo scambio in real-time di messaggi (e dati sotto
forma di messaggi) tra applicazioni distribuite sul cluster.
Kafka
Requirements
L'obiettivo degli sviluppatori di Kafka era quello di costruire una piattaforma unificata
per la gestione di tutti i flussi di dati e di messaggi di una grande azienda.
Questo richiedeva il soddisfacimento di alcuni requisiti fondamentali:
● avere la capacità di gestire velocemente ed in maniera affidabile un elevatissimo
numero di eventi organizzati in “stream” differenti;
● avere la capacità di gestire archivi di grandi dimensioni in modo da poter consentire
anche l'import periodico di grandi archivi provenienti da sistemi offline o batch;
● garantire la possibilità di elaborazione in real-time degli eventi e la produzione di
nuovi stream di eventi;
● avere un'intrinseco supporto alla fault-tolerance in modo da garantire il
funzionamento anche in caso di guasto su qualunque macchina del cluster
Kafka
Architettura
Come risultato di quei requisiti un cluster Kafka è un sistema distribuito per la gestione
ottimizzata di un elevato numero di catene produttore consumatore:
● ogni catena rappresenta uno stream di messaggi e viene detto Topic (o feed);
● ogni Topic è gestito da un certo numero di macchine del cluster (broker) e può
avere associati un numero arbitrario di Producer e Consumer.
Kafka
Topic
L'elemento fondamentale in Kafka è il Topic e rappresenta un flusso (stream) di
messaggi; può anche esser visto come un “argomento” che caratterizza un insieme di
messaggi. Tutti i messaggi pubblicati su un Topic sono salvati su un file detto log-file.
Per aumentare le performance del sistema ogni Topic può essere suddiviso in diverse
partizioni ed ogni partizione costituisce una sequenza ordinata ed immutabile di messaggi.
Un messaggio può essere inserito in
una sola partizione e riceve un ID
numerico (offset) che lo identifica
univocamente nella partizione.
Kafka
Topic
Le partizioni sono il meccanismo con cui Kafka costruisce la scalabilità:
● ad ogni partizione viene associato un file
● tutti i messaggi di una partizione devono essere contenuti su una singola macchina
● dati di partizioni diverse possono stare su macchine diverse
In questo modo un singolo log-file può essere suddiviso in più file fisici e distribuito nel
cluster e, teoricamente, può raggiungere dimensioni arbitrarie.
Inoltre ad ogni Topic è associato un replication-factor, che impone che i dati di una
partizione siano replicati su più macchine distinte in modo da garantire che il sistema
continui a funzionare anche in caso di guasto su una macchina
Kafka
Topic
Ad ogni partizione è associato un nodo, detto leader, che si occupa di gestire tutte le
interazioni (letture e scritture) con la partizione stessa.
In caso di replica-factor>1 alla partizione sono associati anche N-1 nodi follower
che si occupano di replicare sugli altri nodi quanto avviene sul nodo leader.
I nodi follower si comportano come qualunque altro consumer associato alla partizione
e hanno il compito di mantenere una replica aggiornata del log della partizione in modo
da poter sostituire il leader in caso di guasto. Tuttavia non è possibile interagire
direttamente con essi.
Ogni leader conserva un elenco aggiornato dei follower che sono in sync, ovvero che
sono attivi e riescono a “restare al passo” con quanto accade sul nodo leader.
Solo un follower che sia in sync può prendere il posto del leader.
Kafka
Topic e performance
In Kafka ogni messaggio viene scritto su file non appena ricevuto.
Questa scelta sorprendente è in realtà la chiave delle prestazioni di Kafka.
Infatti non avendo una cache in RAM dei messaggi
● si occupa meno RAM, lasciandone di più a disposizione degli altri processi e del
Sistema Operativo;
● si riduce il peso della Java Virtual Machine e del Garbage Collector;
● si delega al file system del Sistema Operativo sottostante l'onere di gestire il
caching verso il disco, che essendo ottimizzato per questo scopo riesce a farlo con
tempi prossimi a quelli di un accesso sequenziale e quindi molto elevati.
Kafka
Produttore
Un produttore è un qualunque processo che “pubblica” dei messaggi.
E' compito del produttore
● indicare il Topic su cui pubblicare il messaggio;
● effettuare un Load Balancing per distribuire i messaggi in maniera uniforme
sulle varie partizioni;
● il load balancing può esser fatto in maniera casuale o attraverso un hash su un
campo chiave per ottenere una sorta di “partizionamento semantico”
I messaggi vengono inviati in maniera asincrona.
Kafka
Performance
Nello sviluppo di Kafka è stata posta grande cura nell'ottimizzazione del trasferimento
dei messaggi dal produttore al Topic e dal Topic al consumatore.
Gli aspetti che si è cercato di gestire con maggior cura sono
● un accesso efficiente al disco (risolto con le scelte di persistenza legate al Topic)
● evitare un numero eccessivo di piccole operazioni di I/O
● evitare un numero eccessivo di operazioni di byte-copying
Kafka
Small I/O minimization
In un sistema di messaging è probabile che nelle operazioni tra client e server e in
quelle di persistenza del server stesso si generi un elevato numero di messaggi di
piccole dimensioni (scambio di messaggi ).
Il rischio è quello di saturare la rete e limitare le performance generali del sistema.
Per evitare questo è stato sviluppato un protocollo di comunicazione interna basato sui
concetti di Invio Asincrono e Message Set.
In questo modo chiunque debba inviare dei messaggi tende ad accumulare dei dati in
memoria per poter inviare più messaggi con una sola richiesta (batch send).
Kafka
Small I/O minimization
Questo processo è configurabile
● ponendo un limite massimo alla quantità di dati da accumulare (es. 64k)
● ponendo un limite massimo di tempo di attesa (es. 10ms)
● ponendoli entrambe (in tal caso la richiesta verrà inviata al raggiungimento del
primo limite)
In questo modo è possibile trovare il miglior compromesso tra un po di latenza
addizionale e un miglior througput.
Kafka
Byte-Copying reduction
Un altro problema tipico dei Message System è l'eccessivo numero di copie effettuate
su ciascun messaggio nel passaggio da un componente all'altro del sistema.
Cosa che ha un impatto ancor più rilevante su sistemi scritti in Java per via del
Garbage Collector.
Per ridurre questo problema in Kafka è stato definito un formato binario standard,
condiviso da tutti i componenti, che ad un messaggio di “attraversare” l'intero sistema
passando da un componente all'altro senza subire copie.
La stessa cura si è posta nel trasferimento del pacchetto verso la rete, in cui è stata
utilizzata una tecnica, denominata zero-copy, che garantisce che l'unica copia del
pacchetto sia quella dalla RAM verso il buffer fisico della scheda di rete prima dell'invio
fisico del segnale sulla rete stessa.
Questo ha consentito un incremento delle performance di oltre il 60%.
Kafka
Consumers
Un consumatore che vuole leggere dei messaggi da un Topic invia un messaggio di
fetch al leader di una partizione specificando la posizione di lettura (offset).
In risposta riceve un gruppo ordinato di messaggi a partire da quello in posizione offset.
Dunque una strategia di tipo pull .
Vediamo di capire perché si è scelta questa strategia.
Kafka
Consumers
Le possibili strategie per un sistema di messaging sono due
● pull : i consumer richiedono i dati al broker quando è in grado di elaborarli
● push : il broker invia i messaggi ai consumer non appena questi sono disponibili
Entrambe hanno vantaggi e svantaggi, legati principalmente alla diversa velocità con
cui produttore e consumatore sono in grado di produrre/elaborare dati:
● pull : se la velocità di elaborazione è molto maggiore di quella di produzione si
rischia di avere tanti consumer che sommergono il broker di richieste di nuovi
messaggi, col rischio di saturare il broker;
● push: si ha il problema contrario, in caso di picchi di produzione il broker può
sommergere il consumer di nuovi messaggi, saturando le sue capacità e
generando una specie di Denial of Service;
Kafka
Consumers
Entrambe le strategie richiedono dunque accorgimenti particolari per poter essere
implementate efficacemente. In Kafka si è optato per la strategia pull perché:
● consente di gestire internamente ed in maniera ottimizzata (a livello di broker) il
caching e la persistenza dei messaggi, liberando il consumer da quest'onere;
● consente di avere un utilizzo ottimizzato della banda ed un throughput ottimale;
Infatti gli accorgimenti sul protocollo per il contenimento del numero di richieste, ovvero
l'uso di micro-batch le cui occorrenze sono determinate dal raggiungimento di una
certa soglia di dati o dal passaggio di un certo intervallo di tempo, consentono di
evitare che dei client, eventualmente scarichi, inondino i broker di messaggi di fetch.
Kafka
Consumers - Performance
Sorprendentemente uno dei fattori fondamentali per le performance di un messaging
system è il modo con cui si tiene traccia della posizione di lettura di un consumatore.
Tipicamente viene utilizzato un sistema di metadati che viene aggiornato ad ogni lettura
in risposta ad un ACK fornito dal consumatore.
Questo funziona bene con sistemi di piccole dimensioni, ma su un sistema distribuito di
grosse dimensioni rischia di diventare un collo di bottiglia che limita le performance del
sistema nel suo complesso.
Cosa succede infatti se un ACK viene perso ?
Kafka
Consumers - Performance
Il fallimento nella ricezione di un ACK comporta il rischio di elaborare più volte lo
stesso messaggio:
se fosse un messaggio in cui si comunica di ridurre l'importo disponibile
su un conto bancario, cosa accadrebbe elaborandolo due o più volte ?
Per evitare situazioni simili diventerebbe necessario
● associare uno stato ad ogni messaggio per sapere da quali consumer è stato
realmente elaborato e da quali no;
● generare un elevato numero di messaggi di sincronizzazione tra broker e
consumer per accertarsi della posizione di lettura
...con buona pace delle performance...
Kafka
Consumers - Performance
In Kafka si è quindi adottata cercato di rendere l'operazione di lettura da parte dei
consumer la più leggera possibile.
Come abbiamo visto un Topic è suddiviso in partizioni, ognuna delle quali contiene un
insieme ordinato di messaggi.
Il segreto è nell'imporre che ogni consumer possa leggere da una sola partizione.
In questo modo l'unico stato necessario è la posizione di lettura di ciascun consumer,
che si riduce ad un intero (l'offset) che, come abbiamo visto, deve essere gestito dal
consumer stesso.
Un vantaggio collaterale di questo approccio è che il consumer può decidere di tornare
indietro e rielaborare un set di messaggi che ha già elaborato.
Kafka
Semantica di Invio dei Messaggi
Idealmente si vorrebbe che un messaging system garantisse che
ogni messaggio sia consegnato una ed una sola volta a ciascun consumer.
In un sistema distribuito questa è difficile garantire questo risultato in qualunque
condizione operativa perché dipende strettamente dalle scelte fatte in termini di
durabilità e modalità di consumo dei messaggi.
In pratica le possibili modalità sono tre
● Exactly once: ogni messaggio viene consegnato una ed una sola volta
● At most once: si garantisce di non consegnare due volte lo stesso messaggio, anche a
rischio di perderne qualcuno
● At least once: si garantisce di non perdere messaggi, anche a costo di consegnare
due volte lo stesso messaggio
Kafka
Semantica lato Producer
La semantica di base è piuttosto semplice:
● un messaggio pubblicato da un producer si considera committed (salvato sul log)
solo quando tutti i follower in sync lo hanno salvato
● un messaggio pubblicato non viene perso fintanto che almeno una replica della
partizione è attiva
Tuttavia non esiste un ACK per il producer, quindi in caso di network-error il producer
non ha modo di sapere se il messaggio sia stato realmente pubblicato o meno.
Per tale ragione è buona prassi rendere l'operazione di pubblicazione idempotente, ad
esempio associando una ID univoca ad ogni messaggio in modo che una
ripubblicazione si traduca in una sovrascrittura.
Kafka
Semantica lato Producer
Poiché non tutti gli use-cases hanno le stesse necessità è in fase di sviluppo un
sistema che consenta al producer di specificare il livello di durabilità desiderato:
● attendere che il messaggio sia effettivamente committed (tutti i follower hanno
completato la scrittura)
● attendere solo che il leader abbia completato la scrittura
● inviare il messaggio in maniera completamente asincrona senza preoccuparsi di
quanto accade lato server.
Kafka
Semantica lato Consumer
Abbiamo visto che tutte le repliche hanno lo stesso log con gli stessi offset, e che ogni
Consumer ha il controllo sulla propria posizione di lettura.
In una condizione ideale, un Consumer
● si avvia ed inizia a leggere dal primo messaggio
● aggiorna l'offset
● procede nella lettura dei messaggi successivi
Cosa succede in caso di crash del Consumer ?
Idealmente vorremmo che venisse automaticamente instanziato un nuovo processo e
che questo riprendesse esattamente da dove l'altro si era interrotto.
Come si può garantire che questo avvenga ?
Kafka
Semantica lato Consumer
Sostanzialmente ci sono due strategie applicabili lato Consumer
● leggo un messaggio, salvo la posizione, lo elaboro
in caso di crash potrei perdere dei messaggi (at most once)
● leggo un messaggio, lo elaboro, salvo la posizione
in caso di crash potrei elaborare due volte un messaggio (at least once)
Per ottenere la strategia ideale exactly once si può rendere l'elaborazione idempotente
(per cui rielaborando il messaggio sostanzialmente riscrivo il risultato) oppure realizzare
un sistema analogo al two-phase commit per cui segnalo di aver letto un messaggio ma
non aver ancora completato la sua elaborazione.
Kafka
Producer Performance Comparison
Kafka
Consumer Performance Comparison
Hadoop Ecosystem
Panoramica Tecnologie
Hadoop Ecosystem
Ecosystem ?
Hadoop Ecosystem
Principali Categorie
Cluster Management
Data Collector
Data Serialization
KV
Graph
Document
ColumnStore
Distributed FS
Frameworks
Libraries
Query engines
Hadoop Ecosystem
Ecosistema o giungla
Per quelle categorie di problemi in cui le tecnologie hanno raggiunto una certa maturità
e stabilità si può parlare di ecosistema perché:
- le tecnologie sono “poche”
- ognuna ha una chiara funzionalità
- esiste un equilibrio abbastanza stabile tra le varie tecnologie
In altre categorie di problemi invece
- la soluzione non è chiara
- ci sono diverse tecnologie che cercano di risolvere il problema con approcci spesso
molto diversi
- le funzionalità ed i comportamenti sono soggetti a repentini mutamenti
- molte soluzioni sono incomplete e coprono solo una porzione degli use-cases
Hadoop Ecosystem
Come orientarsi
I problemi BigData sono tipicamente di tipo Data Driven, perciò le tecnologie vengono
scelte in base alle tipologie di dati da trattare ed i problemi da risolvere.
Tuttavia aver selezionato le tecnologie è solo una parte della soluzione.
Come organizzo le tecnologie pere farle interagire efficacemente
ed avere un sistema efficiente ?
Per provare a rispondere a questa domanda recentemente è stato proposto un modello
architetturale, denominato l-architecture, che può essere usato come modello per
costruire la propria soluzione.
Hadoop Ecosystem
Architetturadi riferimento
Hadoop Ecosysteml Architecture
E' stata proposta da Nathan Marz ( Twitter ) verso la fine del 2013, come modello
standard per la creazione di sistemi per la gestione di BigData.
Per capire il modello architetturale proposto nella Lambda Architecture è importante
delineare quali sono le caratteristiche che vorremmo trovare in un BigData System .
Ma prima ancora ..cosa è un Data System e quindi un BigData System ?
Un Data System è un sistema che è in grado di memorizzare dei dati,
combinarli assieme in svariati modi e fornire delle risposte alle nostre domande.
Hadoop Ecosysteml Architecture
In prima approssimazione possiamo suddividere i dati in due tipi
● dati grezzi (raw data): che sono quelli che noi riceviamo dai sistemi esterni
● lista transazioni bancarie, elenco amici
● dati derivati : che sono ottenuti attraverso elaborazioni sui raw-data
● saldo disponibile sul conto, numero di amici
Per moderare le ambiguità può esser utile far riferimento ai primi con il termine dati ed
ai secondi con il termine informazioni .
Possiamo quindi definire un Data System come
un sistema in grado di memorizzare dati storici,
analizzarli e fornire risposte basate su di essi, ovvero
Data System: query = function ( All Data)
Hadoop Ecosysteml Architecture
In effetti qualunque attività che si possa immaginare con i dati può essere espressa
come una funzione che prende in ingresso tutti i dati e fornisce i risultati desiderati.
La Lambda Architecture fornisce quindi un modello generale per l'implementazione di
un BigData System in grado di analizzare un volume qualsiasi di dati e fornire una
risposta con una bassa latenza.
Il modello non impone delle tecnologie, che potranno essere scelte in base alle
particolari esigenze, ma una strategia per unirle e coordinarle assieme in modo da
raggiungere gli obiettivi attesi.
Hadoop Ecosysteml Architecture
Idealmente vorremmo che un BigData System fosse: ● fault-tolerant
● low-latency reads and updates
● scalable
● minimal maintenance
● debuggable
ma anche
● general; ovvero in grado di supportare un ampio numero di applicazioni
● extensible; ovvero deve consentire l'aggiunta di nuove funzionalità in modo semplice e
con costi di sviluppo contenuti, senza ricostruire tutto da capo;
● allows ad hoc queries; ovvero deve garantire la possibilità di creare interrogazioni sui
dati non previste nelle prime fasi di progetto...
●
Hadoop Ecosysteml Architecture
Come abbiamo visto, parlando di BigData non c'è un unico tool che risolve tutti i
problemi, per cui è necessario utilizzare diversi tool e tecniche per costruire una
soluzione completa.
L'idea è quindi quella di costruire dei layer con delle funzionalità dedicate.
Batch LayerBatch Layer
Serving LayerServing Layer
Speed LayerSpeed Layer
Hadoop Ecosysteml Architecture
Data System: query = function ( All Data)
Avendo risorse infinite un Data System potrebbe eseguire tutte le analisi on the fly e
fornire i risultati in tempo reale.
Nella realtà la soluzione più ragionevole è quella di precalcolare le query e salvare i
risultati (HDFS, NoSql, …) in quelle che chiameremo batch views ; inoltre le viste
potrebbero essere indicizzate per cui le query si tradurrebero in letture dalle batch views.
Quindi
Data System:
batch view = function ( All Data)
query = function ( batch view)
Batch LayerBatch Layer
Hadoop Ecosysteml Architecture
L'uso delle batch views consente di ottenere delle risposte molto velocemente perché
non è più necessario scorrere l'intero dataset per costruire le informazioni desiderate
ma è sufficiente scorrere una delle viste precedentemente generate.
Il problema con questo approccio è che la generazione delle viste in batch è
un'operazione costosa che introduce una forte latenza, per cui durante la loro
elaborazione nuovi dati arriverebbero nel sistema e questi non sarebbero presenti nelle
viste stesse.
Trascuriamo per ora questo problema
Batch LayerBatch Layer
Hadoop Ecosysteml Architecture
Il Batch Layer deve quindi svolgere due funzioni per adempiere al proprio compito
● memorizzare un archivio di dati in continua crescita
● calcolare periodicamente e ciclicamente delle viste su questi dati
Queste operazioni si possono eseguire naturalmente in modalità Batch e possono
essere svolte facilmente con Hadoop (MapReduce, Pig, Hive, ….).
Possiamo quindi immaginare il batch layer come un loop infinito che esegue
ciclicamente aggiorna le batch views sui dati che via via accumula nel data store.
Batch LayerBatch Layer
Hadoop Ecosysteml Architecture
function runBatchLayer() {while(true)
recomputeBatchViews()}
Il vantaggi principali di questo approccio sono:
● è facile da implementare e manutenere
● il codice può essere scritto come un'elaborazione single-threaded
● parallelismo nell'esecuzione e scalabilità ci vengono fornite gratis da Hadoop
Batch LayerBatch Layer
Hadoop Ecosysteml Architecture
Il batch layer produce delle viste precalcolate.
Il passo successivo è rendere disponibile il contenuto di queste viste .
Questo è il compito del Serving Layer, che tipicamente è costituito da un database
distribuito e scalabile. Le funzionalità che questo layer deve fornire sono due:
● consentire l'aggiornamento batch di una vista, sostituendo la vecchia con la nuova
non appena questa è disponibile
● un random-read a bassa latenza sul contenuto di una vista
Serving LayerServing Layer
Hadoop Ecosysteml Architecture
E' interessante notare che questi due layer sono in grado
di soddisfare quasi tutti i requisiti che avevamo richiesto
inizialmente, in quanto sia Hadoop che i sistemi NoSQL li
garantiscono di per se :
Serving LayerServing Layer
Batch LayerBatch Layer
● fault-tolerance
● scalable
● general
● extensible
● allows ad-hoc queries
● minima maintenance
Hadoop Ecosysteml Architecture
Il Serving Layer aggiorna le viste non appena il Batch Layer ha terminato il loro calcolo.
Perciò gli unici dati non rappresentati nelle batch views sono i dati che sono arrivati
durante la loro elaborazione (qualche ora).
Il compito dello Speed Layer è dunque quello consentire l'elaborazione in tempi rapidi
di questi dati e rendere disponibili i risultati in tempo reale.
Speed LayerSpeed Layer
Hadoop Ecosysteml Architecture
In pratica lo Speed Layer deve eseguire le stesse elaborazioni del batch layer, ma
invece che guardare a tutto lo storico si concentra solo sugli ultimi dati ricevuti.
Inoltre per garantire la minor latenza possibile, lo Speed Layer non analizza tutti i nuovi
dati in una volta, ma
crea una vista temporanea e la aggiorna ogni qual volta arrivano nuovi dati.
Per fare questo avrà necessità di un DB distribuito e scalabile con elevate performance
di lettura e scrittura.
Speed LayerSpeed Layer
Hadoop Ecosysteml Architecture
Secondo il formalismo precedente possiamo dire che
realtime view = function ( realtime view, new Data)
e dunque
Data System: batch view = function ( All Data)realtime view = function ( realtime view, New Data)query = function ( batch view, realtime view)
Speed LayerSpeed Layer
Serving Layer
Hadoop Ecosysteml Architecture
Graficamente può essere rappresentata in questo modo
All Data
Speed Layer
Batch Layer batchview
batchview
rtview
rtview
query
query
New Data
Hadoop Ecosysteml Architecture
Uno degli aspetti interessanti di questa architettura è che
non appena il batch layer ha terminato di aggiornare una
vista i corrispondenti risultati presenti nella realtime view
non sono più necessari e possono essere cancellati.
Questa proprietà si chiama Complexity Isolation.Batch LayerBatch Layer
Serving LayerServing Layer
Speed LayerSpeed Layer
In altri termini la complessità del sistema viene circoscritta allo Speed Layer.
Tuttavia essendo i suoi risultati temporanei, anche se ci dovessero essere degli errori,
dopo un certo intervallo di tempo il Batch Layer ricalcolerà i risultati e automaticamente
gli errori verranno cancellati .
Hadoop Ecosysteml Architecture
Complexity Isolation
time
realtimebatch
batch realtime
batch realtime
batch realtime
batch realtime
now
I dati prodotti in Realtimesono temporanei
Hadoop Ecosystem
Speed Layer==
Data Streaming
Data Streaming
Principali Tecnologie
Lo Speed Layer è anche definito layer di Data Streaming perché i dati lo attraversano
come un flusso continuo ed ininterrotto e vengono trasformati durante il loro percorso.
I principali tool di Data Streaming sono tre:
● Apache Storm
● Apache Samza
● Apache Spark + Streaming
Tutti e tre sono stati costruiti sulla base di specifici use cases per cui nessuno di essi
risolve completamente il problema del Data Streaming.
Tuttavia si equivalgono per strategia generale ed obiettivi.
Per illustrarne il funzionamento prenderemo a modello Storm.
Data Streaming
Motivazioni
Hadoop e MapReduce hanno rivoluzionato il modo di conservare ed elaborare le
informazioni rendendo possibili elaborazioni prima impensabili.
Sfortunatamente le idee che hanno portato a MapReduce erano orientate al batch
processing per cui non vi è modo di utilizzarlo per realizzare sistemi real-time.
D'altra parte la capacità di elaborare grandi quantità di dati in real-time sta diventando
un requisito sempre più importante per la maggior parte delle aziende.
Le soluzioni di Data Streaming cercano di portare i benefici che Hadoop ha portato al
mondo del batch processing nel mondo delle elaborazioni real-time.
Data Streaming
Il passato
Prima di Storm per sviluppare una soluzione per il real-time processing, tipicamente si
costruiva e gestiva a mano una rete di code e workers, in cui:
● ogni coda conserva dei messaggi
● ogni worker prende un messaggio dalla coda
● lo elabora
● eventualmente aggiorna un db
● eventualmente genera un nuovo messaggio per avviare ulteriori elaborazioni
● invia il messaggio ad una coda
Data Streaming
Il passato
La gestione manuale di questi sistemi presenta parecchi problemi:
● costosa: la gran parte delle energie viene spesa nella costruzione
dell'infrastruttura (messaggi, code, workers) e relativo test e debug; solo una
piccola percentuale del tempo viene realmente impiegata nello sviluppo della logica
di elaborazione dei dati;
● fragile : dovendo ogni volta ricostruire l'infrastruttura il test e il debug va rifatto ogni
volta e quindi le garanzie di fault-tolerance non sono elevate;
● difficile da scalare: se il numero di messaggi o il volume di dati da gestire cresce
improvvisamente l'architettura sviluppata potrebbe non essere in grado di gestire il
carico, col rischio di doverla modificare anche profondamente.
Data Streaming
Obiettivi
Le soluzioni di Data Streaming forniscono dunque un set di primitive che,
analogamente a quanto fanno Hadoop e MapReduce nel batch processing,
semplificano enormemente lo sviluppo di soluzioni di Data Streaming:
● ampio insieme di use-case possibili: le primitive sono state sviluppate in
maniera tale da poter supportare lo sviluppo di un numero aplissimo di use-cases;
● scalabile: per scalare un sistema sviluppato con Storm è sufficiente aggiungere
macchine al cluster e modificare le impostazioni sul livello di parallelismo,
● garanzia che nessun dato vada perduto: Storm è in grado di garantire che ogni
singolo messaggio venga effettivamente elaborato;
● robusto: Storm è in grado di riassegnare le risorse ad altre macchine del cluster
per garantire la fault-tolerance
Storm
Architettura
Con lo sviluppo di Storm si vogliono portare i vantaggi del MapReduce nel mondo del
data streaming, è dunque naturale che nel progettarlo si sia preso spunto da Hadoop.
Un cluster Storm è, di primo acchito, simile ad un cluster MapReduce e possiamo
stabilire le seguenti analogie:
Con una differenza fondamentale:
● un Job MapReduce viene lanciato per analizzare dei dati e poi termina
● una Topologia Storm è sempre attiva ed elabora i dati non appena arrivano
MapReduce Storm
Job Topology
Job Tracker Nimbus
Task Tracker Supervisor
Storm
Architettura
Dunque anche l'architettura di Storm è di tipo Master-Slave:
● Nimbus, il nodo master, si occupa di distribuire il codice sul cluster, assegnare dei
task alle varie macchine e monitorare l'eventuale fallimento dei task;
● i Supervisor, slaves, restano i attesa di compiti assegnati da Nimbus ed
eventualmente avviano dei workers per l'esecuzione dei compiti loro assegnati;
● il lavoro previsto da una singola topologia viene suddiviso su tanti workers
distribuiti su diverse macchine del cluster.
I vari demoni si appoggiano a Zookeeper per il coordinamento delle attività.
Storm
Topologie
Una topologia è un grafo di elaborazioni.
Ogni nodo della topologia contiene un pezzo della logica di elaborazione e i
collegamenti tra i vari nodi specificano come i dati dovranno essere trasferiti da un
nodo all'altro.
Una topologia resta attiva per sempre, in quanto Storm è in grado di gestire fallimenti
hardware e riassegnare eventuali task falliti ad altre macchine disponibili nel cluster,
senza perdita di dati.
Input 1
Input 2
Output 1
Output 2
Storm
Streams
Una delle astrazioni chiave in Storm sono gli Stream.
Uno stream è una infinita sequenza di tuple.
Dunque le Topologie trasformano gli stream :
una topologia prende in ingresso uno o più stream,
li trasforma e produce in uscita uno o più stream.
HDFS / NoSQL / OtherBoltsSpouts
Storm
Topologie e Streams
Storm fornisce le primitive base per la costruzione di topologie:
● spout : uno spout è un nodo di una topologia in grado di catturare uno stream di
input, o in altri termini una sorgente di tuple;
● bolt: un bolt è uno step di elaborazione (simile al map in un job mapreduce) che :
● riceve in ingresso una tupla alla volta;
● la elabora
● eventualmente emette una o più tuple che genereranno nuovi stream
Input 1
Input 2
Output 1
Output 2
Storm
Topologie e parallelismo
Nel definire una topologia è possibile associare un diverso livello di parallelismo ad ogni
nodo in modo da poter calibrare al meglio le risorse in base al tipo di elaborazioni che si
prevede di dover eseguire.
Output 1
Output 2
Storm
Stream Grouping
Poiché ogni task viene eseguito in parallelo su più worker in alcuni casi può essere utile
determinare quale worker dovrà elaborare una particolare tupla.
In maniera simile a quanto accade nel passaggio tra map e reduce, in Storm
è possibile definire delle regole per il passaggio delle tuple da un bolt al successivo.
Questo viene fatto attraverso degli oggetti che vengono chiamati StreamGrouping.
La strategia di distribuzione più semplice, ShuffleGrouping, distribuisce le tuple in
maniera casuale ed uniforme tra tutti i worker del Bolt successivo.
Storm
Stream Grouping
Alcune delle principali strategie disponibili sono:
● FieldsGrouping; le tuple sono raggruppate in base ai valori dei campi specificati,
ad esempio tutte le tuple con lo stesso user-id o lo stesso nome vengono inviate ad
un unico worker;
● PartialKeyGrouping; analogo al precedente ma ogni gruppo viene suddiviso su
più worker (utile in caso di stream skewed);
● GlobalGrouping; tutte le tuple vanno ad un unico worker;
● LocalOrShuffleGrouping; se esiste un worker sulla stessa macchina le tuple
vengono inviate ad esso, altrimenti si comporta come un normale ShuffleGrouping;
Storm
Garanzie di affidabilità
Storm garantisce che tutti i messaggi siano completamente elaborati.
Ma cosa si intende per completamente elaborati ?
Consideriamo un'ipotetica topologia che
esegua un wordcount e quindi emetta,
per ogni parola, il numero di volte che
questa è stata “vista”.
A lato è rappresentato possibile albero di
messaggi generato da questa topologia.
Storm
Garanzie di affidabilità
Storm considera un messaggio completamente elaborato se
tutto l'albero di messaggi generato a partire dalla tupla in ingresso
è stato eseguito con successo entro un determinato intervallo di tempo
(30 sec. per default ).
In caso contrario la sua elaborazione viene considerata fallita.
Cosa succede quando una tupla viene completamente elaborata ?
E quando la sua elaborazione fallisce ?
Per capirlo ripartiamo dall' “inizio” ...
Storm
Garanzie di affidabilità
Il ciclo di vita di una tupla inizia nello spout che, ricordiamo, è una sorgente di tuple.
Quando Storm richiede una tupla allo spout :
● la tupla resta nella coda ma passa nello stato “pending” che impedisce che venga
elaborata da altri processi;
● alla tupla viene assegnato un “id ” che consente di tracciare tutti i messaggi che
verranno generati nella topologia durante la sua elaborazione;
● Storm invia la tupla a tutti i bolt specificati nella topologia e tiene traccia di tutti i
messaggi che verranno generati;
● a seconda dell'esito dell'elaborazione Storm invia allo spout che ha generato la
tupla un messaggio di
● ack per cui la tupla verrà definitivamente rimossa dalla coda
● fail e la tupla verrà reinserita in coda per essere rielaborata.
Storm
Garanzie di affidabilità
Lo scambio di messaggi tra Storm e Spout è in carico a dei worker denominati ACKer.
Come viene evitata la perdita di dati ?
Le possibili cause d'errore sono tre:
● l'ack non è arrivato perché l'elaborazione è fallita :
● la tupla è rimessa in coda e rielaborata successivamente;
● il processo ACKer fallisce:
● in tal caso tutte le tuple che questo processo stava tracciando vengono
reinserite in coda e rielaborate;
● il processo Spout fallisce:
● in tal caso è responsabilità della sorgente da cui lo Spout attinge (es. Kafka)
riassegnare le tuple che lo Spout aveva in carico