Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
ISBN: 9788850317424
Copyright (c) Packt Publishing 2014. First published in the English language under
the title Learning Hadoop 2 (9781783285518).
Il presente file pu essere usato esclusivamente per finalit di carattere personale.
Tutti i contenuti sono protetti dalla Legge sul diritto dautore.
Nomi e marchi citati nel testo sono generalmente depositati o registrati dalle
rispettive case produttrici.
Ledizione cartacea in vendita nelle migliori librerie.
~
Sito web: www.apogeonline.com
Scopri le novit di Apogeo su Facebook
Seguici su Twitter @apogeonline
Rimani aggiornato iscrivendoti alla nostra newsletter
Introduzione
Questo libro vi guider per mano nellesplorazione del fantastico mondo di
Hadoop 2 e del suo ecosistema in continua crescita. Basato sulle solide
fondamenta delle versioni precedenti della piattaforma, Hadoop 2 consente
lesecuzione di pi framework di elaborazione dei dati su un unico cluster
Hadoop.
Per darvi unidea della portata dellevoluzione, studieremo sia il funzionamento
dei nuovi modelli sia come si applicano nellelaborazione di grandi volumi di dati
con algoritmi batch, iterativi e quasi in tempo reale.
Struttura del libro
Il Capitolo 1, Per iniziare, fornisce le basi di Hadoop e per affrontare i
problemi dei big data che intende risolvere. Vedremo anche dove Hadoop 1 ha
spazio di miglioramento.
Il Capitolo 2, Storage, entra nel dettaglio dellHadoop Distributed File
System, dove vengono memorizzati i dati elaborati da Hadoop. Esamineremo le
caratteristiche specifiche di HDFS, spiegheremo come utilizzarle e vedremo come
migliorato in Hadoop 2. Presenteremo anche ZooKeeper, un altro sistema di
storage allinterno di Hadoop, su cui si basano molte funzionalit cruciali.
Il Capitolo 3, Elaborazione: MapReduce e oltre, affronta innanzitutto il
tradizionale modello di elaborazione di Hadoop e come viene utilizzato. Vedremo
poi come Hadoop 2 ha generalizzato la piattaforma per utilizzare pi modelli
computazionali, tra i quali MapReduce solo uno dei tanti.
Il Capitolo 4, Computazione in tempo reale con Samza, approfondisce uno di
questi modelli di elaborazione alternativi abilitati da Hadoop 2. In particolare,
vedremo come elaborare dati in streaming in tempo reale con Apache Samza.
Il Capitolo 5, Computazione iterativa con Spark, entra nel merito di un
modello di elaborazione molto diverso. In questo capitolo parleremo dei mezzi
forniti da Apache Spark per effettuare lelaborazione iterativa.
Il Capitolo 6, Analisi dei dati con Apache Pig, mostra come Apache Pig
semplifichi luso del modello computazionale tradizionale di MapReduce
attraverso un linguaggio che descrive i flussi di dati.
Il Capitolo 7, Hadoop e SQL, analizza come il familiare linguaggio SQL
stato implementato sui dati salvati in Hadoop. Attraverso luso di Apache Hive e
la descrizione di alternative come Cloudera Impala, vedremo come rendere
possibile lelaborazione dei big data usando le competenze e gli strumenti
esistenti.
Il Capitolo 8, Gestione del ciclo di vita dei dati, d unocchiata generale a
come gestire tutti i dati che devono essere elaborati in Hadoop. Attraverso Apache
Oozie, illustreremo come costruire dei workflow per ottenere, elaborare e gestire i
dati.
Il Capitolo 9, Facilitare il lavoro di sviluppo, si concentra sulla scelta degli
strumenti che devono aiutare lo sviluppatore a raggiungere rapidamente dei
risultati. Attraverso Hadoop Streaming, Apache Crunch e Kite, vedremo come
luso dello strumento giusto pu velocizzare il ciclo di sviluppo o fornire nuove
API con una semantica pi ricca e meno ridondanze.
Il Capitolo 10, Eseguire un cluster Hadoop, considera il lato operativo di
Hadoop. Concentrandosi sugli ambiti di primo interesse degli sviluppatori, come
la gestione dei cluster, il monitoraggio e la sicurezza, questo capitolo vi aiuter a
lavorare meglio con il vostro staff operations.
Il Capitolo 11, Come proseguire, vi guida in un breve tour tra alcuni progetti e
strumenti che riteniamo utili ma che non possiamo trattare nel dettaglio per ragioni
di spazio. Vi forniremo anche alcune indicazioni su dove trovare altre informazioni
e come unirvi alle varie community open source.
Cosa serve per questo libro
Considerato che poche persone dispongono di una serie di computer di scorta,
useremo la macchina virtuale Cloudera QuickStart per la maggior parte degli
esempi del libro. unimmagine di una macchina su cui preinstallato un cluster
Hadoop completo. Pu essere eseguita su qualsiasi macchina host che supporta
VMware o la tecnologia di virtualizzazione VirtualBox.
Esploreremo anche la piattaforma Amazon Web Services (AWS) e vedremo
come eseguire alcune delle tecnologie Hadoop sul servizio AWS Elastic
MapReduce. I servizi AWS sono gestibili attraverso un browser web o
uninterfaccia Linux a riga di comando.
Lo scopo del libro
Questo libro rivolto perlopi a sviluppatori di sistemi e applicativi che
vogliono imparare a risolvere problemi pratici usando il framework Hadoop e i
relativi componenti. Nonostante mostreremo gli esempi in linguaggi di
programmazione diversi, il requisito fondamentale una conoscenza solida di
Java. Gli ingegneri e gli architetti dei dati troveranno utile il materiale riguardante
il ciclo di vita dei dati, i formati dei file e i modelli computazionali.
Convenzioni
In questo libro abbiamo applicato stili di testo diversi per distinguere i vari tipi
di informazioni. Ecco alcuni esempi con una spiegazione del loro significato.
Le parti del codice e i nomi dei file sono resi con un carattere monospaziato. Gli
elementi dellinterfaccia sono invece in corsivo, cos come i nomi delle directory
e le parole nuove o importanti.
Un blocco di codice appare cos:
topic_edges_grouped = FOREACH topic_edges_grouped {
GENERATE
group.topic_id as topic,
group.source_id as source,
topic_edges.(destination_id,w) as edges;
}
NOTA
Note, suggerimenti e avvertimenti appaiono in questa forma.
Codice degli esempi
Il codice sorgente del libro si trova su GitHub allindirizzo https://github.com/
learninghadoop2/book-examples. Gli autori applicheranno le eventuali correzioni al
Componenti comuni
Sia HDFS sia MapReduce adottano molti dei principi architetturali descritti nel
paragrafo precedente, e in particolare quelli che seguono.
Entrambi sono concepiti per lesecuzione su cluster di server di base (cio
con specifiche da medie a basse).
Entrambi scalano la loro capacit aggiungendo altri server (scale-out)
rispetto allabitudine precedente di utilizzare un hardware pi grande (scale-
up).
Entrambi hanno meccanismi per identificare e risolvere i problemi.
Entrambi forniscono la maggior parte dei loro servizi in modo trasparente,
consentendo allutente di concentrarsi sul problema del momento.
Entrambi hanno unarchitettura in cui un cluster software risiede sui server
fisici e gestisce aspetti come il bilanciamento del carico di unapplicazione e
la tolleranza ai guasti, senza affidarsi allhardware high-end per applicare
queste capacit.
Storage
HDFS un file system, sebbene non compatibile con POSIX. Questo significa
che non ha le stesse caratteristiche di un file system ordinario ma altre peculiarit.
Salva i file in blocchi di almeno 64 MB o, ancora pi spesso, di 128 MB,
dimensioni ben superiori ai 4-32 KB della maggior parte dei file system.
ottimizzato per il throughput a sfavore della latenza; molto efficiente
nella lettura in streaming di file molto grossi ma scadente quando si tratta di
cercarne di piccoli.
ottimizzato per carichi di lavoro del tipo scrivi una volta e leggi pi
volte.
Invece di gestire i guasti del disco tramite le ridondanze fisiche nelle serie di
dischi o strategie analoghe, HDFS utilizza la replica. Ciascuno dei blocchi
che costituisce un file viene salvato su pi nodi nel cluster, e il servizio
chiamato NameNode li monitora costantemente per garantire che gli eventuali
errori o problemi non abbiano cancellato qualche blocco al di sotto del
fattore di replica desiderato. Se accade, NameNode programma la creazione
di unaltra copia allinterno del cluster.
Calcolo
MapReduce unAPI, un motore di esecuzione e un paradigma; rende possibile
una serie di trasformazioni da una sorgente in un dataset di risultati. Nel caso pi
semplice, i dati di input vengono immessi tramite una funzione map, mentre i dati
temporanei risultanti vengono forniti attraverso una funzione reduce.
MapReduce lavora al meglio su dati non strutturati o semi-strutturati. Non serve
che i dati siano conformi a schemi rigidi; il requisito che possano essere forniti
alla funzione map come una serie di coppie chiave-valore. Loutput della funzione
map un set di altre coppie chiave-valore, mentre la funzione reduce esegue
Meglio se insieme
HDFS e MapReduce possono essere utilizzati singolarmente, ma quando
lavorano insieme fanno emergere il meglio luno dellaltro, e questa interrelazione
stato il fattore principale del successo e delladozione di Hadoop 1.
Quando si progetta un job di MapReduce, Hadoop deve decidere su quale host
eseguire il codice per poter elaborare il dataset nel modo pi efficiente. Non conta
molto se gli host dei cluster di MapReduce traggono i loro dati da un unico host o
array di storage, perch il sistema una risorsa condivisa. Se il sistema di storage
fosse pi trasparente e consentisse a MapReduce di manipolare i suoi dati pi
direttamente, ci sarebbe lopportunit di eseguire lelaborazione dei dati pi da
vicino, in base al principio secondo cui meno costoso spostare lelaborazione
che spostare i dati.
Il modello pi comune di Hadoop vede la distribuzione dei cluster HDFS e
MapReduce sullo stesso gruppo di server. Ogni host che contiene i dati e il
componente HDFS che li gestisce ospita anche un componente MapReduce che
pu programmare ed eseguire lelaborazione. Quando un job viene inviato ad
Hadoop, questo pu utilizzare lottimizzazione della posizione per programmare il
pi possibile i dati sugli host in cui risiedono i dati, riducendo cos il traffico di
rete e ottimizzando le prestazioni.
Hadoop 2: dov laffare?
Se consideriamo i due componenti principali della distribuzione Hadoop, lo
storage e il calcolo, vediamo che Hadoop 2 ha un impatto diverso su ciascuno di
essi. Laddove lHDFS in Hadoop 2 un prodotto pi ricco di funzionalit e pi
resiliente di quello in Hadoop 1, le modifiche per MapReduce sono pi profonde,
e hanno cambiato di fatto il modo in cui Hadoop viene percepito come piattaforma
di elaborazione in generale. Vediamo prima HDFS in Hadoop 2.
Storage in Hadoop 2
Discuteremo larchitettura HDFS nel dettaglio nel Capitolo 2; per ora
sufficiente pensare a un modello master-slave. I nodi slave (i DataNode)
contengono i dati veri e propri del file system. In particolare, ogni host che esegue
un DataNode ha solitamente uno o pi dischi su cui sono scritti i file che
contengono i dati per ogni blocco HDFS. Il DataNode di per s non sa nulla del
file system globale; il suo ruolo quello di memorizzare, servire ed assicurare
lintegrit dei dati di cui responsabile.
Il nodo master (il NameNode) deve sapere quale dei DataNode contiene un
determinato blocco e come quei blocchi sono strutturati a formare il file system.
Quando un client considera il file system per recuperare un file, attraverso una
richiesta al NameNode che ottiene lelenco dei blocchi richiesti.
Questo modello funziona bene ed stato scalato su cluster con decine di
migliaia di nodi in realt come quella di Yahoo!. Per quanto scalabile, tuttavia, c
un rischio di resilienza; se il NameNode diventa non disponibile, allora lintero
cluster diventa inutile. Nessuna operazione HDFS pu essere svolta, e poich la
maggioranza delle installazioni usa HDFS come livello di storage dei servizi,
come MapReduce, anche questi diventano non disponibili, anche se sono in piena
esecuzione senza problemi.
Ancora peggio, il NameNode memorizza i metadati del file system in un file
persistente sul suo file system locale. Se lhost del NameNode va in crash in un
modo tale per cui i dati non sono recuperabili, allora tutti i dati sul cluster sono
irrimediabilmente perduti. Continueranno a esistere sui vari DataNode, ma la
mappatura di quali blocchi contenevano quali file non pi disponibile. Ecco
perch in Hadoop 1 la best practice era quella che il NameNode scrivesse i dati
del suo file system contemporaneamente sui dischi locali e almeno su un volume di
rete remoto (solitamente tramite NFS).
Alcuni produttori di terze parti offrono diverse soluzioni high-availability (HA)
di NameNode, ma il prodotto Hadoop centrale non fornisce questa resilienza nella
Versione 1. Considerati il singolo punto di fallimento architetturale e il rischio di
perdita dei dati, non sar una sorpresa scoprire che la NameNode HA una delle
funzioni principali di HDFS in Hadoop 2, come vedremo nei prossimi capitoli.
Questa caratteristica offre un NameNode in standby che pu essere promosso
automaticamente a soddisfare tutte le richieste qualora il NameNode attivo
fallisse, e garantisce unulteriore resilienza per i dati critici del file system.
HDFS in Hadoop 2 un file system non ancora compatibile con POSIX; ha una
dimensione di blocchi molto grande e baratta ancora la latenza con il throughput.
Tuttavia, ha ora alcune capacit che possono farlo assomigliare di pi a un file
system tradizionale. In particolare, lHDFS core in Hadoop 2 pu essere montato
da remoto su un volume NFS, unaltra funzione che era prima offerta come
proprietaria da fornitori di terzi parti ma che ora parte integrante della base di
codice principale di Apache.
Complessivamente, lHDFS in Hadoop 2 pi resiliente e pu essere integrato
pi facilmente nei processi e nei flussi di lavoro esistenti. una grande evoluzione
del prodotto che era in Hadoop 1.
Calcolo in Hadoop 2
Il lavoro su HDFS 2 iniziato prima che la direzione di MapReduce fosse
stabilita definitivamente. Questo soprattutto perch funzioni come la NameNode
HA erano una strada talmente ovvia che la community conosceva gi gli ambiti pi
critici da affrontare. Tuttavia, MapReduce non contemplava altrettante aree di
miglioramento, ed ecco perch non fu subito chiaro lo scopo di uniniziativa come
quella di MRv2.
Lobiezione principale a MapReduce in Hadoop 1 riguardava il fatto che il suo
modello di elaborazione in batch mal si adattava ai domini problematici in cui
erano necessari tempi di risposta pi rapidi. Hive, per esempio, che vedremo nel
Capitolo 7, fornisce uninterfaccia del tipo SQL sui dati HDFS, ma dietro le quinte
le istruzioni vengono convertite in job di MapReduce che vengono poi eseguiti
come tutti gli altri. Altri prodotti o strumenti adottavano un approccio simile,
fornendo uninterfaccia utente specifica che nascondeva il livello di traduzione di
MapReduce.
Sebbene questo approccio ebbe successo, e nonostante la realizzazione di
prodotti notevoli, rimane il fatto che il pi delle volte c una discrepanza, poich
tutte queste interfacce, alcune delle quali si aspettano un certo tipo di reattivit,
dietro le quinte vengono eseguite su una piattaforma di elaborazione in batch. E
tali discrepanze rimanevano anche se potevano essere apportati a MapReduce dei
miglioramenti a favore di una corrispondenza pi precisa. Questa situazione port
a un cambiamento significativo del focus delliniziativa MRv2. Forse non era
MapReduce ad aver bisogno di modifiche; la vera necessit era quella di
consentire diversi modelli di elaborazione sulla piattaforma Hadoop. Fu cos che
nacque Yet Another Resource Negotiator (YARN).
MapReduce in Hadoop 1 faceva due cose piuttosto diverse: forniva il
framework di elaborazione per eseguire i calcoli di MapReduce, ma gestiva anche
lallocazione della computazione sul cluster. Non solo indirizzava i dati a e tra
operazioni specifiche di map e reduce, ma determinava anche dove ogni attivit
sarebbe stata eseguita, e gestiva lintero ciclo di vita del job, monitorando la
salute di ogni attivit e nodo, riprogrammando in caso di fallimenti e cos via.
Non unoperazione banale, e la parallellizzazione automatizzata dei carichi di
lavoro sempre stato uno dei vantaggi di Hadoop. Se consideriamo MapReduce in
Hadoop 1, dopo che lutente definisce i criteri chiave per il job, qualsiasi altra
cosa di responsabilit del sistema. Da un punto di vista della scala, lo stesso job
di MapReduce pu essere applicato a dataset di qualsiasi volume sui cluster di
qualsiasi dimensione. Se abbiamo 1 GB di dati su un unico host, allora Hadoop
programmer lelaborazione di conseguenza, e far lo stesso anche se abbiamo 1
PB di dati su mille macchine. Dal punto di vista dellutente, la scala effettiva dei
dati e dei cluster trasparente, e al di l del tempo necessario a elaborare il job,
linterfaccia con cui si interagisce con il sistema non cambia.
In Hadoop 2, il ruolo di programmazione dei job e di gestione delle risorse
separato da quello dellesecuzione dellapplicazione vera e propria, ed svolto
da YARN.
YARN responsabile della gestione delle risorse del cluster, quindi
MapReduce esiste in quanto applicazione che gira sul framework di YARN. In
Hadoop 2 linterfaccia di MapReduce completamente compatibile con quella in
Hadoop 1, sia semanticamente sia praticamente. Tuttavia, dietro le quinte,
MapReduce diventata unapplicazione ospitata sul framework YARN.
Il senso di questa discrepanza che possono essere scritte altre applicazioni
che forniscono modelli di elaborazione centrati sul problema contingente
scaricando al contempo su YARN le responsabilit di gestione delle risorse e di
programmazione. Le versioni pi recenti di molti motori di esecuzione sono state
portate su YARN, sia in uno stato pronto per la produzione sia sperimentale; tale
approccio permette che un singolo cluster Hadoop esegua tutto, dai job di
MapReduce orientati al batch attraverso query SQL a risposta rapida fino a stream
di dati continui, oltre a implementare modelli come lelaborazione dei grafici e la
Message Passing Interface (MPI) del mondo dellHigh Performance Computing
(HPC). La Figura 1.2 mostra larchitettura di Hadoop 2.
Figura 1.2 Hadoop 2.
Cloudera QuickStart VM
Uno dei vantaggi delle distribuzioni Hadoop che consentono laccesso a
package software facili da installare. Cloudera va anche oltre, e fornisce
unistanza di Virtual Machine scaricabile gratuitamente, nota come CDH
QuickStart VM, distribuita su CentOS Linux.
Nel resto del libro utilizzeremo la CDH5.0.0 VM come riferimento e come
sistema di base per eseguire gli esempi e il codice sorgente disponibile per i
sistemi di virtualizzazione VMware (http://www.vmware.com/nl/products/player/), KVM
(http://www.linux-kvm.org/page/Main_Page) e VirtualBox (https://www.virtualbox.org/).
Amazon EMR
Prima di utilizzare Elastic MapReduce, dobbiamo impostare un account AWS e
registrarci per i servizi necessari.
Creare un account AWS
Amazon ha integrato i suoi account generali con AWS; se quindi avete gi un
account per uno qualsiasi dei siti di vendita online di Amazon, lo utilizzerete anche
per i servizi AWS.
NOTA
I servizi AWS hanno un costo; dovrete quindi aver associata allaccount una carta di credito
attiva su cui possano essere effettuati gli addebiti.
Prima di proseguire, fondamentale tenere presente che luso dei servizi AWS
implica il pagamento di una tariffa che avverr addebitata sulla carta di credito
associata allaccount di Amazon. In genere le cifre sono basse, ma aumentano con
laumentare dellentit dellinfrastruttura consumata; lo storage di 10 GB di dati in
S3 costa dieci volte pi di 1 GB, ed eseguire 20 istanze di EC2 costa venti volte
una sola istanza. Va poi considerato che i costi effettivi tendono a subire degli
aumenti marginali pi piccoli a livelli pi elevati. In ogni caso, prima di utilizzare
un servizio, leggete con attenzione le sezioni riguardanti i prezzi. Considerate
anche che i dati che vengono trasferiti allesterno dei servizi AWS, come C2 e S3,
sono addebitabili, mentre i trasferimenti tra servizi non lo sono. Questo significa
che spesso pi conveniente progettare luso degli AWS in modo da mantenere i
dati al loro interno per la maggior parte dellelaborazione. Per informazioni su
AWS ed EMR, consultate la pagina http://aws.amazon.com/elasticmapreduce/#pricing.
Credenziali AWS
Prima di utilizzare gli strumenti programmatici o a riga di comando, dovremo
capire come il possessore di un account si autentica sugli AWS per le richieste.
Ogni account AWS ha numerosi identificatori, come quelli elencati di seguito,
che vengono utilizzati quando si accede ai vari servizi.
ID dellaccount: ogni account AWS ha un ID numerico.
Chiave di accesso: la chiave di accesso associata viene usata per identificare
laccount che effettua la richiesta.
Chiave di accesso segreta: fa il paio con la chiave di accesso. La chiave di
accesso normale non segreta e pu essere esposta nelle richieste, mentre
quella segreta quella che utilizzate per validarvi come possessori
dellaccount. Trattatela con la stessa cura con cui trattate la vostra carta di
credito.
Coppie di chiavi: sono utilizzate per il login agli host EC2. possibile
generare coppie di chiavi pubbliche/private in EC2 o importare nel sistema
le chiavi generate allesterno.
Le credenziali e i permessi degli utenti sono gestiti tramite un servizio web
chiamato Identity and Access Management (IAM), che dovrete sottoscrivere per
poter ottenere le chiavi di accesso e segreta.
Sembra tutto un po confuso, e lo , almeno allinizio. Quando si usa uno
strumento per accedere a un servizio AWS, solitamente viene subito richiesto di
aggiungere le credenziali corrette a un file configurato, dopodich tutto funziona.
Se per decidete di esplorare gli strumenti programmatici o a riga di comando,
vale la pena investire un po di tempo per leggere la documentazione relativa a
ciascun servizio per capire come funziona sotto laspetto della sicurezza. Trovate
ulteriori informazioni sulla creazione di un account AWS e sullottenimento delle
credenziali di accesso alla pagina http://docs.aws.amazon.com/iam.
Per accedere allAPI, dobbiamo configurare il software per autenticarci per gli
AWS usando le nostre chiavi di accesso e segreta.
anche il momento giusto per impostare una copia di chiavi EC2 seguendo le
istruzioni fornite allindirizzo https://console.aws.amazon.com/ec2/home?region=us-east-
1#c=EC2&s=KeyPairs. Per quanto una coppia di chiavi non sia strettamente necessaria
Una volta impostata la CLI, possiamo interrogare AWS con aws <service>
<arguments>. Per creare e interrogare un bucket S3 usate un comando come quello
che segue. Notate che i bucket S3 devono essere univoci tra tutti gli account AWS,
quindi i nomi pi comuni come s3://mybucket non saranno disponibili:
$ aws s3 mb s3://learninghadoop2
$ aws s3 ls
Negli ultimi capitoli vi mostreremo come aggiungere dei passi per eseguire job
di MapReduce e script Pig.
Trovate altre informazioni sullAWS CLI alla pagina
http://docs.aws.amazon.com/ElasticMapReduce/latest/DeveloperGuide/emr-manage.html.
Eseguire gli esempi
Allindirizzo https://github.com/learninghadoop2/book-examples trovate il codice di tutti
gli esempi. Vengono forniti gli script e le configurazioni Gradle
(http://www.gradle.org/) per compilare la maggior parte del codice Java. Lo script
gradlew incluso nellesempio caricher Gradle e lo utilizzer per recuperare le
./gradlew jar
Perch Twitter?
Grazie alle sue API programmatiche, Twitter fornisce un modo semplice per
generare dataset di dimensioni arbitrarie e per immetterli nei nostri cluster
Hadoop locali o sul cloud. Il dataset che utilizzeremo avr alcune propriet che si
adattano a numerosi casi di modellazione ed elaborazione dei dati.
I dati di Twitter hanno le seguenti propriet.
Non sono strutturati: ogni aggiornamento dello stato un messaggio testuale
che pu contenere riferimenti a un contenuto multimediale, come URL e
immagini.
Sono anche strutturati: i tweet sono record consecutivi con una data e unora.
Sono rappresentabili graficamente: le relazioni come le risposte e le
menzioni possono essere modellate come una rete di interazioni.
Sono gelolocalizzabili: si conosce la posizione da cui un tweet stato inviato
o dove un utente risiede.
Sono in tempo reale: tutti i dati generati su Twitter sono disponibili attraverso
un flusso in tempo reale (firehose).
Queste propriet si rifletteranno nel tipo di applicazione che possiamo costruire
con Hadoop, e includono esempi di sentiment e trend analysis e social network.
Creare il primo dataset
Le condizioni duso di Twitter vietano la ridistribuzione dei dati generati
dallutente in qualsiasi forma; per questa ragione, non possiamo rendere
disponibile un dataset comune. Utilizzeremo allora uno script di Python per
accedere in modo programmatico alla piattaforma e creare un deposito di tweet
degli utenti raccolti da uno stream live.
Un servizio, pi API
Gli utenti di Twitter condividono oltre 200 milioni di tweet al giorno, noti anche
come aggiornamenti dello stato. La piattaforma offre laccesso a questo corpus di
dati attraverso quattro tipi di API, ciascuna delle quali rappresenta una
sfaccettatura di Twitter e mira a soddisfare casi duso specifici, come il
collegamento e linterazione con il contenuto di Twitter da fonti di terze parti (Per
prodotto), laccesso programmatico al contenuto di utenti o siti specifici (REST),
le funzionalit di ricerca tra le timeline di utenti o siti (Cerca) e laccesso a tutto il
contenuto creato sulla rete di Twitter in tempo reale (API Streaming).
LAPI Streaming consente un accesso diretto allo stream di Twitter, tenendo
traccia delle parole chiave, recuperando i tweet geotaggati da una determinata
regione e altro ancora. In questo libro useremo questa API come sorgente di dati
per illustrare le capacit sia in batch sia in tempo reale da Hadoop. Tuttavia non
interagiremo direttamente con essa; utilizzeremo invece librerie di terze parti per
scaricarci di compiti come la gestione dellautenticazione e delle connessioni.
Anatomia di un tweet
Ogni oggetto restituito da una chiamata allAPI in tempo reale rappresentato
da una stringa JSON serializzata che contiene un set di attributi e metadati oltre al
messaggio di testo.
Questo contenuto aggiuntivo include un ID numerico che identifica in modo
univoco il tweet, la posizione da cui stato condiviso, lutente che lha condiviso
(loggetto utente), se stato ripubblicato da altri utenti (cio se stato ritiwittato)
e quante volte (conteggio dei tweet), il linguaggio macchina del testo, se il tweet
stato inviato in risposta a qualcuno e, in questo caso, gli ID dellutente e del tweet
a cui stato inviato e cos via.
La struttura di un tweet e degli altri oggetti eventualmente esposti dallAPI in
costante evoluzione. Trovate una guida aggiornata alla pagina
https://dev.twitter.com/docs/platform-objects/tweets.
Credenziali di Twitter
Twitter si serve del protocollo OAuth per autenticare e autorizzare laccesso
alla sua piattaforma da software di terze parti.
Lapplicazione ottiene tramite un canale esterno, per esempio un form web, le
seguenti coppie di codici:
un consumer key;
un consumer secret.
Il consumer secret non viene mai trasmesso direttamente alla terza parte perch
viene usato per firmare ogni richiesta.
Lutente autorizza lapplicazione ad accedere al servizio tramite un processo a
tre vie che, una volta completato, fornisce allapplicazione un token costituito da:
un access token;
un access secret.
Analogamente al codice consumer, laccess secret non viene mai trasmesso
direttamente alla terza parte e viene usato per firmare ogni richiesta.
Per usare lAPI Streaming, dovremo prima registrare unapplicazione e ottenere
per essa laccesso programmatico al sistema. Se richiedete un nuovo account
Twitter, accedete alla pagina https://twitter.com/signup e inserite le informazioni
richieste. A seguire, dovremo creare unapplicazione desempio che acceder
allAPI per nostro conto assegnandole i permessi opportuni. Per farlo ci serviremo
del form web alla pagina https://dev.twitter.com/apps.
Quando si crea una nuova app, ci viene chiesto di darle un nome, di fornire una
descrizione e un URL. La schermata che segue mostra le impostazioni di
unapplicazione desempio chiamata Learning Hadoop 2 Book Dataset. Per gli scopi di
questo libro non occorre specificare un URL valido, quindi utilizzeremo un
segnaposto.
Una volta completato il form, controlliamo e accettiamo le condizioni duso e
facciamo clic sul pulsante Create Application nellangolo inferiore sinistro.
Comparir una pagina che riassume i dettagli della nostra applicazione, come
mostrato nella figura. Le credenziali di autenticazione e di permesso si trovano
nella scheda OAuth Tool.
Ed eccoci pronti a generare il nostro primo vero dataset di Twitter.
consumer_key = os.environ[TWITTER_CONSUMER_KEY]
consumer_secret = os.environ[TWITTER_CONSUMER_SECRET]
access_key = os.environ[TWITTER_ACCESS_KEY]
access_secret = os.environ[TWITTER_ACCESS_SECRET]
class EchoStreamListener(tweepy.StreamListener):
def __init__(self, api, dump_json=False, numtweets=0):
self.api = api
self.dump_json = dump_json
self.count = 0
self.limit = int(numtweets)
super(tweepy.StreamListener, self).__init__()
self.count = self.count+1
return False if self.count == self.limit else True
def on_timeout(self):
return True
if __name__ == __main__:
parser = get_parser()
args = parser.parse_args()
Come prima cosa, importiamo tre dipendenze: tweepy e i moduli os e json, presenti
nellintepreter di Python versione 2.6 o successiva.
Definiamo poi la classe EchoStreamListener, che eredita ed estende StreamListener da
tweepy. Come il nome pu far intuire, StreamListener ascolta gli eventi e i tweet che
Il processo di avvio del NameNode consiste innanzitutto nella lettura del file
fsimage, poi nella lettura del file edits e infine nellapplicazione di tutte le modifiche
salvate in edits nella copia in memoria di fsimage. Infine scrive su disco una nuova
versione aggiornata del file fsimage ed pronto per ricevere le richieste da parte
del client.
NOTA
I comandi dfs e dfsadmin possono essere utilizzati anche con lutility a riga di comando
principale di Hadoop, come in hadoop fs -ls /. Questo era lapproccio nelle versioni precedenti
di Hadoop , ma ora stato deprecato a favore del comando hdfs.
Molti dei comandi sono simili a quelli standard del file system Unix e
funzionano come ci si aspetta. Sulla nostra VM di test abbiamo un account utente
chiamato cloudera. Tramite questo utente possiamo ottenere una lista della root del
file system:
$ hdfs dfs -ls /
Found 7 items
drwxr-xr-x - hbase hbase 0 2014-04-04 15:18 /hbase
drwxr-xr-x - hdfs supergroup 0 2014-10-21 13:16 /jar
drwxr-xr-x - hdfs supergroup 0 2014-10-15 15:26 /schema
drwxr-xr-x - solr solr 0 2014-04-04 15:16 /solr
drwxrwxrwt - hdfs supergroup 0 2014-11-12 11:29 /tmp
drwxr-xr-x - hdfs supergroup 0 2014-07-13 09:05 /user
drwxr-xr-x - hdfs supergroup 0 2014-04-04 15:15 /var
Loutput molto simile a quello del comando ls di Unix. Gli attributi del file
funzionano come quelli di user/group/world su un file system Unix (compreso lo
sticky bit t) e in pi forniscono i dettagli sul proprietario, il gruppo e lora di
modifica delle directory. La colonna tra il nome del gruppo e la data di modifica
la dimensione; per le directory 0, mentre per i file avr un valore, come si vede
nel prossimo segmento di codice.
NOTA
Se vengono utilizzati percorsi relativi, vengono tratti dalla directory home dellutente. Se non c
una directory home, possiamo crearla attraverso questi comandi:
$ sudo -u hdfs hdfs dfs mkdir /user/cloudera
$ sudo -u hdfs hdfs dfs chown cloudera:cloudera /user/cloudera
Quasi tutti gli altri sottocomandi di dfs sono piuttosto intuitivi; esplorateli. Pi
avanti nel capitolo affronteremo le snapshot e laccesso programmatico ad HDFS.
Proteggere i metadati del file system
Considerata limportanza del file fsimage per il file system, perderlo una
catastrofe. In Hadoop 1, dove il NameNode era un unico point of failure, la best
practice era quella di configurare il NameNode perch scrivesse fsimage e
contemporaneamente modificasse i file su unarea di storage locale e in unaltra
posizione su un file system remoto (in genere NFS). In caso di fallimento del
NameNode, si poteva avviare un NameNode sostituivo usando questa copia
aggiornata dei metadati del file system. La procedura era per piuttosto complessa,
e comportava un periodo in cui il cluster non sarebbe stato disponibile.
NameNode HA di Hadoop 2
Nella maggior parte dei cluster di produzione di Hadoop 2, tuttavia, ha pi
senso utilizzare la soluzione completa High Availability (HA) invece che affidarsi
ai nodi Checkpoint e di backup. un errore provare a combinare NameNode HA
con i meccanismi degli altri due tipi di nodi.
Lidea di base quella di una coppia di NameNode (attualmente non ne sono
supportati pi di due) configurati in un cluster attivo/passivo. Un NameNode
agisce come il master attivo che serve le richieste dei client, mentre il secondo
rimane in attesa di sostituirlo qualora il primo dovesse fallire. In particolare,
HDFS di Hadoop 2 consente HA attraverso due meccanismi:
fornendo un sistema tale per cui entrambi i NameNode possono avere delle
viste coerenti del file system;
fornendo un mezzo ai client per connettersi sempre al NameNode master.
con degli interi che incrementano per le chiamate successive. Quando un client
crea uno ZNode sotto il lock, controller se il relativo nodo sequenziale ha il
suffisso con lintero pi basso. In caso affermativo, viene trattato come se avesse
il lock, altrimenti dovr attendere finch il nodo che possiede il lock non viene
eliminato. Il client in genere registra un watcher sul nodo con il suffisso pi basso
e viene avvisato quando quel nodo viene rimosso; ora sar quello ZNode ad avere
il lock.
API Java
La classe org.apache.zookeeper.ZooKeeper il principale client programmatico per
accedere a un ensemble di ZooKeeper. Consultate la documentazione Java per i
dettagli; in ogni caso, linterfaccia di base piuttosto intuitiva, con corrispondenze
immediate ai comandi nella CLI. Per esempio:
create lequivalente di create della CLI;
getChildren lequivalente di ls della CLI;
Componenti
Come appena visto, ZooKeeper fornisce un piccolo numero di operazioni
definite con una semantica molto forte che pu essere costruita in servizi di alto
livello, quali i lock, la partecipazione ai gruppi e lelezione di un leader.
ZooKeeper pu essere considerato come un toolkit di funzioni affidabili e ben
ingegnerizzate fondamentali per i sistemi distribuiti sui quali si pu costruire senza
doversi preoccupare delle tortuosit della loro implementazione. Linterfaccia
fornita da ZooKeeper piuttosto semplice, e sono poche le recenti interfacce di
alto livello che forniscono qualcosa di pi nella mappatura delle primitive a basso
livello nella logica dellapplicazione. Il progetto Curator (http://curator.apache.org/)
un buon esempio.
ZooKeeper era usato con moderazione in Hadoop 1, ma ora piuttosto diffuso.
Viene utilizzato sia da MapReduce sia da HDFS per lelevata disponibilit dei
loro componenti JobTracker e NameNode. Hive e Impala, che tratteremo in
seguito, lo usano per inserire dei lock sulle tabelle di dati a cui accedono pi job
contemporaneamente. Kafka, di cui discuteremo nellambito di Samza, impiega
ZooKeeper per i nodi (broker nella sua terminologia), lelezione del leader e la
gestione dello stato.
Per saperne di pi
Non abbiamo descritto ZooKeeper troppo nei dettagli, e abbiamo del tutto
omesso alcuni aspetti come la sua capacit di applicare quote e ACL agli ZNode
nel file system e i meccanismi per costruire i callback. Il nostro scopo era quello
di fornire dettagli sufficienti a darvi unidea di come verr utilizzato nei servizi
Hadoop che esploriamo nel libro. Per ulteriori informazioni, consultate la home
page del progetto.
Failover automatico dei NameNode
Ora che abbiamo presentato ZooKeeper, possiamo mostrare come viene
utilizzato per abilitare il failover automatico dei NameNode.
Il failover automatico dei NameNode porta nel sistema due nuovi componenti;
un quorum e lo ZooKeeper Failover Controller (ZKFC), che gira su ciascun host
di NameNode. ZKFC crea uno ZNode effimero in ZooKeeper e lo detiene finch
rileva il NameNode locale come attivo e correttamente funzionante. Per
determinarlo, continua a inviare semplici richieste di controllo della salute al
NameNode; se questo non risponde correttamente in breve tempo, ZKFC presume
che fallito. Se una macchina NameNode va in crash o fallisce in altro modo, la
sessione di ZKFC in ZooKeeper verr chiusa e anche lo ZNode effimero verr
rimosso.
I processi di ZKFC monitorano anche gli ZNode degli altri NameNode nel
cluster. Se lo ZKFC sullhost del NameNode in standby vede sparire lo ZNode
master, deduce che questo fallito e tenter un failover. Per farlo, cercher di
acquisire il lock per il NameNode (tramite il protocollo illustrato nel paragrafo su
ZooKeeper), e se ci riuscir inizier il failover attraverso lo stesso meccanismo di
fencing/promozione descritto in precedenza.
Snapshot HDFS
Abbiamo gi accennato al fatto che la replica HDFS da sola non una buona
strategia di backup. Nel file system di Hadoop 2, sono state inserite le snapshot,
delle istantanee che aggiungono un altro livello di protezione dei dati ad HDFS.
Le snapshot del file system sono state utilizzate per qualche tempo in diverse
tecnologie. Lidea di base che diventa possibile vedere lo stato esatto del file
system in momenti specifici nel tempo. Questo risultato si ottiene prendendo una
copia dei metadati del file system nel punto in cui si cattura la snapshot, e la si
rende disponibile perch possa essere visualizzata nel futuro.
Man mano che il file system viene modificato, qualsiasi intervento che influisce
sulla snapshot verr trattato in modo speciale. Per esempio, se in una snapshot c
un file che viene eliminato, anche se verr rimosso dallo stato corrente del file
system i suoi metadati rimarranno nella snapshot, e i blocchi associati ai suoi dati
rimarranno nel file system, anche se non saranno accessibili attraverso una vista
del sistema diversa dalla snapshot.
Un esempio illustrer il punto. Immaginate che il vostro file system contenga
questi file:
/data1 (5 blocchi)
/data2 (10 blocchi)
allindirizzo https://hadoop.apache.org/docs/r2.5.0/api/org/apache/hadoop/fs/FileSystem.html.
La tabella che segue ne riepiloga alcuni, insieme allo schema URI corrispondente
e alla classe di implementazione Java.
File system Schema URI Implementazione Java
Locale file org.apache.hadoop.fs.LocalFileSystem
Esistono due implementazioni del file system S3. Quella nativa s3n usata
per leggere e scrivere file normali. I dati salvati con s3n sono accessibili da
qualsiasi strumento, e possono essere utilizzati per leggere i dati generati da altri
strumenti S3. s3n non pu gestire file pi grandi di 5 TB n rinominare le
operazioni.
Analogamente ad HDFS, il file system S3 basato su blocchi memorizza i file in
blocchi e richiede un bucket S3 dedicato al file system. I file memorizzati in un file
system S3 possono superare i 5 TB, ma non ammessa linteroperabilit con altri
strumenti S3. LS3 basato su blocchi supporta la rinomina delle operazioni.
Interfacce di Hadoop
Hadoop scritto in Java, e ovviamente tutte le interazioni con il sistema
avvengono attraverso linterfaccia a riga di comando. Quella che abbiamo usato
con il comando hdfs negli esempi precedenti unapplicazione Java che usa la
classe FileSystem per svolgere operazioni di input/output sui file system disponibili.
Libhdfs
Libhdfs una libreria C che pu essere impiegata per accedere a qualsiasi file
system di Hadoop e non solo ad HDFS. scritta utilizzando la Java Native
Interface (JNI) e imita la classe FileSystem di Java.
Thrift
Apache Thrift (http://thrift.apache.org) un framework per costruire software
cross-language attraverso meccanismi di serializzazione dei dati e di invocazione
remota dei metodi. LAPI Hadoop Thrift, disponibile in contrib, espone i file
system di Hadoop come un servizio Thrift. Questa interfaccia facilita al codice non
Java laccesso ai dati memorizzati in un file system di Hadoop.
Al di l delle interfacce citate, ne esistono altre che permettono laccesso ai file
system di Hadoop via HTTP e FTP (queste solo per HDFS), oltre che come
WebDAV.
Gestire e serializzare i dati
Avere un file system va bene, ma serve un meccanismo che rappresenti i dati e li
memorizzi al suo interno. Vediamo alcune di queste soluzioni.
Linterfaccia Writable
Agli sviluppatori fa comodo poter manipolare tipi di dati a un livello pi alto e
lasciare che sia Hadoop a occuparsi dei processi necessari per serializzarli in
byte da scrivere su un file system e poi ricostruirli da uno stream di byte quando
vengono letti dal file system.
Il package org.apache.hadoop.io contiene linterfaccia Writable, che fornisce questo
meccanismo ed specificata come segue:
public interface Writable
{
void write(DataOutput out) throws IOException ;
void readFields(DataInput in) throws IOException ;
}
Le classi wrapper
Fortunatamente, non dovete partire da zero e costruire le varianti di Writable
per tutti i tipi di dati che userete. Hadoop offre delle classi che racchiudono i tipi
di primitive Java e implementano linterfaccia Writable. Si trovano nel package
org.apache.hadoop.io.
Queste classi sono concettualmente simili alle classi wrapper primitive, come
Integer e Long, che si trovano in java.lang. Ammettono un unico valore primitivo
che pu essere impostato sia in fase di costruzione sia tramite un metodo setter.
Sono le seguenti:
BooleanWritable ;
ByteWritable ;
DoubleWritable ;
FloatWritable ;
IntWritable;
LongWritable;
VIntWritable: un tipo integer a lunghezza variabile;
Le interfacce Comparable e
WritableComparable
Quando abbiamo detto che le classi wrapper implementano Writable siamo stati
leggermente imprecisi; in realt implementano uninterfaccia composta chiamata
WritableComparable nel package org.apache.hadoop.io che combina Writable con
Serializzazione e contenitori
Quando parliamo di formati di file, ci riferiamo a due tipi di scenari.
Serializzazione: vogliamo codificare le strutture di dati generate e manipolate
in fase di elaborazione in un formato che possiamo salvare su un file,
trasmettere e, pi estesamente, recuperare e ritradurre per un unulteriore
manipolazione.
Contenitori: una volta che i dati sono serializzati nei file, i contenitori offrono
un modo per raggruppare pi file e aggiungere altri metadati.
Compressione
Quando si lavora con i dati, la compressione pu far risparmiare molto in
termini di spazio necessario per salvare i file e di I/O dei dati in rete e dai/sui
dischi locali.
Quando si utilizza un framework di elaborazione, la compressione pu avvenire
in tre punti della pipeline:
nei file di input da elaborare;
nei file di output risultato dellelaborazione;
nei file intermedi/temporanei prodotti nella pipeline.
Quando aggiungiamo la compressione in una di queste fasi, abbiamo
lopportunit di ridurre drasticamente la quantit di dati da leggere e scrivere su
disco o sulla rete. La cosa si rivela molto utile con i framework come MapReduce
che potenzialmente possono, per esempio, produrre volumi di dati temporanei che
sono pi grossi dei dataset di input o di output.
Apache Hadoop offre diversi codec di compressione, quali gzip, bzip2, LZO,
snappy, ognuno con i suoi pro e i suoi contro. La scelta del codec dipende dal tipo
di dati da elaborare e dalla natura del framework di elaborazione.
Diversamente dal consueto compromesso tra spazio e tempo, in cui un aumento
dello spazio corrisponde a un calo della velocit di compressione e
decompressione (e viceversa), dovremo considerare che i dati memorizzati in
HDFS saranno oggetto dellaccesso di software parallelo e distribuito, e che ci
sono software che aggiungono anche requisiti propri sui formati di file.
MapReduce, per esempio, pi efficiente sui file che possono essere suddivisi in
sottofile validi.
Questo pu rendere pi complesso il decidere se comprimere e quale codec
usare nel caso, poich la maggior parte dei codec (come gzip) non supporta la
suddivisione dei file, mentre qualcuno lo fa (come LZO).
RCFile
Il formato Row Columnar File (RCFile) fu sviluppato originariamente da
Facebook per lo storage di backend del suo sistema di data warehouse Hive, che
fu il primo sistema mainstream SQL su Hadoop disponibile come open source.
RCFile punta a offrire quanto segue:
caricamento veloce dei dati;
elaborazione rapida delle query;
uso efficiente dello storage;
adattabilit ai carichi di lavoro dinamici.
Trovate ulteriori informazioni su RCFile alla pagina http://bit.ly/1ANuO5Q.
ORC
Il formato di file Optimized Row Columnar (ORC) ha lo scopo di combinare le
prestazioni di RCFile con la flessibilit di Avro. studiato soprattutto per
lavorare con Apache Hive ed stato sviluppato inizialmente da Hortonworks per
superare i limiti percepiti degli altri formati di file disponibili. Trovate ulteriori
informazioni alla pagina http://bit.ly/11efF3t.
Parquet
Parquet (http://parquet.incubator.apache.org) il frutto dello sforzo congiunto di
Cloudera, Twitter e Criteo, e ora stato donato allApache Software Foundation. I
suoi obiettivi sono quelli di fornire un formato di file colonnare moderno e a
prestazioni elevate da usare con Cloudera Impala. Come Impala, stato ispirato
dal documento su Dremel (http://research.google.com/pubs/pub36632.html). Permette di
lavorare con strutture di dati complesse e annidate e consente una codifica
efficiente a livello di colonne.
Avro
Apache Avro ( http://avro.apache.org) un contenitore di file e un formato di
serializzazione di dati binari orientato allo schema. la nostra scelta in tutto il
libro per quanto riguarda il formato binario. Pu essere suddiviso e compresso, il
che lo rende efficace per lelaborazione dei dati con framework come
MapReduce.
Sono molti i progetti che includono un supporto specifico e unintegrazione con
Avro, che si pu quindi applicare diffusamente. Quando i dati vengono salvati in
un file Avro, con esso viene salvato anche il suo schema, definito come un oggetto
JSON. Un file pu essere elaborato successivamente da una terza parte senza una
conoscenza preliminare di come i dati sono codificati. Questo permette
lautodescrizione e ne agevola luso con i linguaggi dinamici e di scripting. Il
modello di schema in lettura favorisce anche lefficienza dello storage dei record,
poich non occorre che i singoli campi vengano taggati.
Nei prossimi capitoli vedremo come queste propriet possono facilitare la
gestione del ciclo di vita dei dati e consentire operazioni complesse come la
migrazione dei dati.
fileWriter.create(schema, file);
fileWriter.append(tweet);
fileWriter.close();
DatumReader<GenericRecord> datumReader =
new GenericDatumReader<>(schema);
DataFileReader<GenericRecord> fileReader =
new DataFileReader(file, datumReader);
GenericRecord genericTweet = null;
while (fileReader.hasNext()) {
genericTweet = (GenericRecord) fileReader
.next(genericTweet);
if (val != null) {
System.out.println(val);
}
}
}
} catch (IOException ie) {
System.out.println(Error parsing or writing file.);
}
}
desiderata:
$ java -jar /opt/cloudera/parcels/CDH-5.0.0-1.cdh5.0.0.p0.47/lib/avro/avro-tools.jar compile
schema tweets_avro.avsc src/main/java
try {
File file = new File(tweets.avro);
DatumWriter<tweets_avro> datumWriter =
new SpecificDatumWriter<>(tweets_avro.class);
DataFileWriter<tweets_avro> fileWriter =
new DataFileWriter<>(datumWriter);
fileWriter.create(tweet.getSchema(), file);
fileWriter.append(tweet);
fileWriter.close();
DatumReader<tweets_avro> datumReader =
new SpecificDatumReader<>(tweets_avro.class);
DataFileReader<tweets_avro> fileReader =
new DataFileReader<>(file, datumReader);
while (fileReader.hasNext()) {
tweet = fileReader.next(tweet);
System.out.println(tweet.getText());
}
} catch (IOException ie) {
System.out.println(Error in parsing or writing
files.);
}
}
Poich ci siamo serviti della generazione del codice, usiamo ora il meccanismo
dello SpecificRecord di Avro insieme alla classe generata che rappresenta loggetto
nel nostro modello di dominio. Possiamo poi istanziare direttamente loggetto e
accedere ai suoi attributi attraverso i consueti metodi get/set.
Scrivere il file unoperazione simile alla precedente, tranne per il fatto che si
utilizzano classi specifiche e si recupera lo schema direttamente dalloggetto tweet
quando necessario. Anche la lettura viene facilitata in modo analogo grazie alla
capacit di creare istanze di una classe specifica e alluso dei metodi get/set.
Riepilogo
In questo capitolo abbiamo compiuto una panoramica dello storage su un cluster
Hadoop. Nello specifico, abbiamo visto quanto segue.
Larchitettura di alto livello di HDFS, il file system principale utilizzato in
Hadoop.
Come funziona HDFS, e in particolare il suo approccio allaffidabilit.
Come Hadoop 2 si integrato in HDFS, soprattutto nella forma del
NameNode HA e delle snapshot del file system.
Cos ZooKeeper e come viene utilizzato in Hadoop per abilitare funzioni
come il failover automatico dei NameNode.
Un giro tra gli strumenti a riga di comando usati per accedere a HDFS.
LAPI per i file system in Hadoop e come HDFS sia, a livello di codice, solo
unimplementazione di unastrazione di un file system pi flessibile.
Come serializzare i dati su un file system Hadoop e qual il supporto fornito
nelle classi core.
I vari formati di file con cui i dati vengono memorizzati pi spesso in Hadoop
e alcuni casi duso.
Nel prossimo capitolo vedremo nel dettaglio come Hadoop fornisce i
framework che possono essere impiegati per elaborare i dati in esso memorizzati.
Capitolo 3
Elaborazione: MapReduce e oltre
Loutput viene poi scritto nuovamente su HDFS come mostrato nella Figura 3.1.
API Java per MapReduce
LAPI Java per MapReduce esposta dal package org.apache.hadoop.mapreduce.
Scrivere un programma MapReduce non altro che compiere un subclassing delle
classi di base Mapper e Reducer fornite da Hadoop e poi un overriding dei metodi
map() e reduce() con la propria implementazione.
La classe Mapper
Per le nostre implementazioni di Mapper, si effettuer il subclassing della classe
di base Mapper e loverride del metodo map(), cos:
class Mapper<K1, V1, K2, V2>
{
void map(K1 key, V1 value Mapper.Context context)
throws IOException, InterruptedException
...
}
eliminano la necessit di dover scrivere dei parser per tutti i tipi di file a
accezione di quelli personalizzati.
A volte pu essere necessario loverriding di altre tre metodi:
protected void setup( Mapper.Context context)
throws IOException, InterruptedException
Questo metodo viene chiamato prima che qualsiasi coppia chiave/valore venga
presentata al metodo map. Limplementazione di default non fa nulla:
protected void cleanup( Mapper.Context context)
throws IOException, InterruptedException
Questo metodo viene chiamato dopo che tutte le coppie chiave/valore sono state
presentate al metodo map. Limplementazione di default non fa nulla:
protected void run( Mapper.Context context)
throws IOException, InterruptedException
La classe Reducer
La classe di base Reducer funziona in modo molto simile alla classe Mapper e
richiede solitamente solo delle sottoclassi per loverriding di un unico metodo
reduce(). Vediamo la sua definizione ridotta:
Il metodo setup() viene chiamato una volta prima che le coppie chiave/valore
vengano presentate al metodo reduce. Limplementazione di default non fa nulla:
protected void cleanup(Reducer.Context context)
throws IOException, InterruptedException
Il metodo cleanup() viene chiamato una volta dopo che tutte le coppie
chiave/valore sono state presentate al metodo reduce. Limplementazione di default
non fa nulla:
protected void run(Reducer.Context context)
throws IOException, InterruptedException
La classe Driver
La classe Driver comunica con il framework di Hadoop e specifica gli elementi
di configurazione necessari per eseguire il job di MapReduce. Questo implica dire
ad Hadoop quali classi Mapper e Reducer utilizzare, dove trovare i dati di input e in
che formato e dove collocare i dati di output e come formattarli.
La logica del driver solitamente gi presente nel metodo principale della
classe scritta per incapsulare un job di MapReduce. Non esiste una classe Driver
genitore da cui creare sottoclassi:
public class ExampleDriver extends Configured implements Tool
{
...
public static void run(String[] args) throws Exception
{
// Crea un oggetto Configuration che viene utilizzato
// per impostare altre opzioni
Configuration conf = getConf();
// Imposta il nome della classe principale nel file JAR del job
job.setJarByClass(ExampleDriver.class);
// Imposta la classe mapper
job.setMapperClass(ExampleMapper.class);
Combiner
Hadoop consente luso di una classe combiner per eseguire un primo
ordinamento delloutput dal metodo map prima che sia recuperato dal reducer.
Gran parte del design di Hadoop si fonda sulla riduzione delle parti costose di
un job, che di solito coincidono con le operazioni di I/O su disco e di rete.
Loutput per il mapper spesso corposo; non insolito che arrivi a molte volte le
dimensioni dellinput originario.
Hadoop fornisce alcune opzioni che permettono di limitare limpatto del
trasferimento in rete di segmenti di dati grandi da parte dei reducer. La casse
combiner adotta un approccio diverso, dove possibile, per eseguire
unaggregazione preventiva per ridurre la mole di dati da trasferire.
La classe combiner non ha una propria interfaccia, deve avere la stessa
segnatura del reducer e quindi implica il subclassing della classe Reduce dal
package org.apache.hadoop.mapreduce. Leffetto quello di eseguire una mini-riduzione
sul mapper delloutput destinato a ciascun reducer.
Hadoop non garantisce che la classe combiner venga eseguita. A volte questo
potrebbe non accadere affatto, mentre altre volte potrebbe essere utilizzata una
volta, due volte o pi volte a seconda delle dimensioni e del numero dei file di
output file generati dal mapper per ogni reducer.
Partizionamento dei file
Una delle certezze implicite dellinterfaccia di Reduce che tutti i valori
associati a una data chiave vengono consegnati a un unico reducer. Quando ci sono
pi attivit di reduce in corso su un cluster, ogni output mapper deve essere
suddiviso in output separati destinati a ciascun reducer. Questi file partizionati
vengono salvati sul file system del nodo locale.
Il numero di attivit dei reducer sul cluster non dinamico come quello dei
mapper, tant che possiamo specificare il valore come parte dellinvio del job.
Hadoop sa quindi quanti reducer sono necessari per completare il job, e da questo
dedurr il numero di partizioni in cui dovr essere suddiviso loutput del mapper.
Di default, Hadoop utilizza una strategia di hash sulla chiave delloutput per
eseguire il partizionamento. Questa funzionalit presente nella classe
HashPartitioner nel package org.apache.hadoop.mapreduce.lib.partition, ma a volte pu
Come iniziare
Negli esempi che seguono, ci baseremo su un dataset generato raccogliendo
1000 tweet usando lo script stream.py illustrato nel Capitolo 1:
$ python stream.py t n 1000 > tweets.txt
NOTA
Finora abbiamo lavorato solo con il testo dei tweet. Nel resto del libro estenderemo stream.py in
modo da generare altri metadati dei tweet nel formato JSON. Tenetelo presente prima di
scaricare terabyte di messaggi con questo script.
Elastic MapReduce
Come accennato nel Capitolo 1, Elastic MapReduce si aspetta che il file JAR
del job e i suoi dati di input si trovino in un bucket S3, e in S3 generer loutput.
AT T ENZIONE
Tutto questo costa dei soldi! Per questo esempio utilizzeremo la configurazione di cluster pi
piccola possibile disponibile per EMPR, ovvero un cluster con un unico nodo.
job.setJarByClass(WordCount.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
output. Nella classe WordCountReducer Text e IntWritable sono accettati sia come input
sia come output. Anche questo un pattern piuttosto comune, in cui il metodo map
esegue uninversione sulla chiave e i valori ed emette una serie di coppie di dati
su cui il reducer effettua laggregazione.
Il driver qui ha molto pi senso, visto che abbiamo dei valori reali per i
parametri. Utilizziamo gli argomenti passati alla classe per specificare le
posizioni di input e output.
Eseguite il job con:
$ hadoop jar build/libs/mapreduce-example.jar com.learninghadoop2.mapreduce.WordCount \
twitter.txt output
Co-occorrenze di parole
Le parole che ricorrono insieme sono come frasi, ed probabile che le frasi pi
comuni e che ritornano spesso siano importanti. Nellelaborazione del linguaggio
naturale (la NLP), una lista di co-occorrenze di un termine chiamata n-gramma.
Gli n-gramma sono alla base di numerosi metodi statistici di analisi del testo.
Vedremo qui un esempio del caso speciale in cui un n-gramma una metrica che
spesso si incontra nelle applicazioni analitiche composto da due termini
(bigramma).
Unimplementazione elementare in MapReduce potrebbe essere unestensione di
WordCount che emette una chiave multicampo costituita da due parole separate da
tabulazione:
public class BiGramCount extends Configured implements Tool
{
public static class BiGramMapper
extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
prev = s;
}
}
}
@Override
public int run(String[] args) throws Exception {
Configuration conf = getConf();
Trending topic
Il simbolo #, chiamato hashtag, usato per contrassegnare le parole o i topic in
un tweet. Originariamente fu creato dagli utenti di Twitter come sistema per
classificare i messaggi. Twitter Search (https://twitter.com/search-home) ha reso
popolare luso degli hashtag come metodo per connettersi e trovare il contenuto
correlato a topic specifici e le persone che ne parlano. Contando la frequenza con
cui un hashtag citato in un dato periodo di tempo, possiamo determinare quali
topic fanno tendenza nel social network:
public class HashTagCount extends Configured implements Tool
{
public static class HashTagCountMapper
extends Mapper<Object, Text, Text, IntWritable>
{
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
Questa classe compilata si trover nel file JAR che abbiamo costruito in
precedenza con Gradle, quindi ora eseguiamo HashTagCount con il seguente comando:
$ hadoop jar build/libs/mapreduce-example.jar \
com.learninghadoop2.mapreduce.HashTagCount twitter.txt output
Ogni riga costituita da un hashtag e dal numero di volte che appare nel dataset
di tweet. Come potete vedere, il job di MapReduce ordina i risultati per chiave.
Se vogliamo trovare i topic pi citati, dovremo ordinare il set di risultati.
Lapproccio pi semplice quello di eseguire un ordinamento totale dei valori
aggregati selezionando poi i primi 10.
Se il dataset di output piccolo, possiamo convogliarlo nelloutput standard e
ordinarlo attraverso lutility sort:
$ hdfs dfs -cat output/part-r-00000 | sort -k2 -n -r | head -n 10
Il pattern Top N
Nel pattern Top N, i dati ordinati vengono conservati in una struttura di dati
locale. Ogni mapper calcola un elenco dei record top N nel suo split e ne invia
lelenco al reducer. Unattivit del reducer trova i record globali top N.
Applicheremo questo pattern di design per implementare un job TopTenHashTag che
trova i primi dieci topic nel dataset. Il job prende come input i dati di output
generati da HashTagCount e restituisce una lista dei 10 hashtag pi citati.
In TopTenMapper usiamo TreeMap per mantenere un elenco degli hashtag in ordine
crescente. La chiave di questa mappa il numero di occorrenze; il valore una
stringa di hashtag separati da tabulazione e la loro frequenza. In map(), per ogni
valore, aggiorniamo la mappa topN. Quando topN ha pi di 10 voci, rimuoviamo la
pi piccola:
public static class TopTenMapper extends Mapper<Object, Text,
NullWritable, Text> {
@Override
protected void cleanup(Context context) throws IOException,
InterruptedException {
for (Text t : topN.values()) {
context.write(NullWritable.get(), t);
}
}
}
@Override
public void reduce(NullWritable key, Iterable<Text> values,
Context context) throws IOException, InterruptedException {
for (Text value : values) {
String[] words = value.toString().split(\t) ;
topN.put(Integer.parseInt(words[1]),
new Text(value));
Infine, percorriamo topN in ordine discendente per generare la lista dei trending
topic.
NOTA
In questa implementazione, ignoriamo gli hashtag che hanno un valore di frequenza gi
presente in TreeMap quando si chiama topN.put(). A seconda dei casi, si consiglia di utilizzare
una struttura di dati diversa (come quella offerta dalla libreria,
https://code.google.com/p/guava-libraries/) o di adattare la strategia di aggiornamento.
NOTA
Queste risorse e molte altre sono state raccolte dal gruppo del professor Bing Liu presso
lUniversit dellIllinois a Chicago, e sono state utilizzate, tra gli altri, in Bing Liu, Minqing Hu e
Junsheng Cheng, Opinion Observer: Analyzing and Comparing Opinions on the Web, Atti della
14ma International World Wide Web Conference (WWW-2005), 10-14 maggio 2005, Chiba,
Giappone.
Nella classe Mapper, definiamo due oggetti che conterranno le liste di positiveWords
e negativeWords come Set<String>:
private Set<String> positiveWords = null;
private Set<String> negativeWords = null;
Eseguiamo loverride del metodo setup() di default del mapper in modo che una
lista di parole positive e negative specificate dalle due propriet di
configurazione job.positivewords.path e job.negativewords.path sia letta da HDFS
usando lAPI del file system come abbiamo visto nel capitolo precedente.
Avremmo anche potuto utilizzare DistributedCache per condividere questi dati nel
cluster. Il metodo helper parseWordsList legge un elenco di liste di parole, rimuove i
commenti e carica le parole in HashSet<String>:
private HashSet<String> parseWordsList(FileSystem fs, Path wordsListPath)
{
HashSet<String> words = new HashSet<String>();
try {
if (fs.exists(wordsListPath)) {
FSDataInputStream fi = fs.open(wordsListPath);
BufferedReader br =
new BufferedReader(new InputStreamReader(fi));
String line = null;
while ((line = br.readLine()) != null) {
if (line.length() > 0 && !line.startsWith(BEGIN_COMMENT)) {
words.add(line);
}
}
fi.close();
}
}
catch (IOException e) {
e.printStackTrace();
}
return words;
}
Nella fase del mapper, emettiamo per ogni hashtag nel tweet il sentiment
complessivo del tweet (semplicemente il conteggio delle parole positive meno
quello delle parole negative) e la lunghezza del tweet.
Utilizzeremo queste informazioni nel reducer per calcolare un rapporto di
sentiment complessivo valutato in base alla lunghezza dei tweet per stimare il
sentiment espresso da un tweet su un hashtag, cos:
public void map(Object key, Text value, Context context)
throws IOException, InterruptedException {
String[] words = value.toString().split( ) ;
Integer positiveCount = new Integer(0);
Integer negativeCount = new Integer(0);
if (positiveWords.contains(str)) {
positiveCount += 1;
} else if (negativeWords.contains(str)) {
negativeCount += 1;
}
wordsCount += 1;
}
Integer sentimentDifference = 0;
if (wordsCount > 0) {
sentimentDifference = positiveCount - negativeCount;
}
String stats ;
for (String hashtag : hashtags) {
word.set(hashtag);
stats = String.format(%d %d, sentimentDifference,
wordsCount);
context.write(word, new Text(stats));
}
}
}
Nella fase del reducer, sommiamo i punteggi del sentiment dati a ciascuna
istanza dellhashtag e dividiamo il risultato per la dimensione totale di tutti i tweet
in cui ricorso:
public static class HashTagSentimentReducer
extends Reducer<Text,Text,Text,DoubleWritable> {
public void reduce(Text key, Iterable<Text> values,
Context context
) throws IOException, InterruptedException {
double totalDifference = 0;
double totalWords = 0;
for (Text val : values) {
String[] parts = val.toString().split( ) ;
totalDifference += Double.parseDouble(parts[0]) ;
totalWords += Double.parseDouble(parts[1]) ;
}
context.write(key,
new DoubleWritable(totalDifference/totalWords));
}
}
sequenzialmente una serie di mapper come primo passo di una pipeline di cleanup
dei dati. I mapper sono aggiunti al job configurato come segue:
ChainMapper.addMapper(
JobConf job,
Class<? extends Mapper<K1,V1,K2,V2>> klass,
Class<? extends K1> inputKeyClass,
Class<? extends V1> inputValueClass,
Class<? extends K2> outputKeyClass,
Class<? extends V2> outputValueClass, JobConf mapperConf)
Il metodo statico, addMapper, richiede il passaggio dei seguenti argomenti.
:
job JobConf per aggiungere la classe Mapper;
class: classe Mapper da aggiungere;
job.setInputFormatClass(TextInputFormat.class);
FileInputFormat.addInputPath(job, new Path(args[0]));
job.setOutputFormatClass(TextOutputFormat.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
Infine, notate che la chiamata di addMapper per lultimo mapper nella catena
specifica le classi di output chiave/valore applicabili allintera pipeline del
mapper quando questo usato come composto.
Trovate il codice sorgente completo dellesempio alla pagina
http://bit.ly/17XEvIa.
Avvio
Il driver lunico elemento del codice che viene eseguito sulla nostra macchina
locale, e la chiamata a Job.waitForCompletion() avvia la comunicazione con il
JobTracker, che il nodo master nel sistema di MapReduce. Il JobTracker
responsabile di tutti gli aspetti della programmazione e dellesecuzione del job,
quindi diventer la nostra interfaccia principale quando eseguiremo le attivit
correlate alla gestione del job.
Per condividere le risorse sul cluster, il JobTracker pu utilizzare uno dei
numerosi approcci di programmazione per gestire i job in arrivo. Il modello
generale quello con alcune code su cui possono essere inviati i job insieme ai
criteri per assegnare le risorse alle varie code. Le implementazioni pi diffuse per
queste policy sono Capacity e Fair Scheduler.
Il JobTracker comunica con il NameNode per nostro conto e gestisce tutte le
interazioni legate ai dati salvati in HDFS.
Suddividere linput
La prima di queste interazioni si ha quando il JobTracker osserva i dati di input
e determina come assegnarli alle attivit di map. Ricordate che i file di HDFS
vengono solitamente suddivisi in blocchi di almeno 64 MB e che il JobTracker
assegna ciascun blocco a unattivit di map. Nel nostro esempio di WordCount
stata utilizzata una quantit di dati limitata che ben si adatta a un unico blocco. Se
immaginate un file di input molto pi grande, nellordine di qualche terabyte, il
modello di suddivisione avr pi senso. Ogni segmento del file split nella
terminologia di MapReduce viene elaborato da unattivit di map. Una volta che
ha calcolato gli split, il JobTracker li posiziona in HDFS insieme al file JAR che
contiene le classi Mapper e Reducer in una directory specifica per il job, il cui
percorso verr passato a ciascuna attivit allavvio.
Avvio dellattivit
Ogni TaskTracker avvia un JVM separata per eseguire le attivit. Questo
aggiunge una penalit di tempo allavvio, ma isola il TaskTracker dai problemi
causati da un comportamento errato delle attivit di map o reduce, e permette di
configurare il job per la condivisione tra attivit eseguite in modo consequenziale.
Se un cluster abbastanza capace da poter eseguire tutte le attivit di map
contemporaneamente, queste verranno avviate e verr dato loro un riferimento per
lo split che devono elaborare e per il file JAR del job. Se il numero delle attivit
supera la capacit del cluster, il JobTracker manterr una coda di attivit in attesa
e le assegner ai nodi man mano che completano le attivit di map inizialmente
assegnate. Ora siamo pronti per vedere i dati eseguiti delle attivit di map. Vi
sembra che il lavoro da fare sia molto? Avete ragione; quando si esegue un
qualsiasi job di MapReduce ci vuole sempre un tempo non indifferente prima che
il sistema parta ed effettui tutta la procedura.
loffset di byte come chiave e il contenuto della chiave come valore. Supponiamo
che il nostro dataset contenga questo testo:
This is a test
Yes it is
Questi dati verranno generati nei file di partizione allinterno della directory di
output specificata nel driver che verr formattato usando limplementazione
OutputFormat specificata. Ogni attivit di reduce scrive su un unico file con il nome
di file part-r-nnnnn, dove nnnnn inizia a 00000 e viene incrementato.
Chiusura
Una volta che tutte le attivit sono state completate con successo, il JobTracker
genera lo stato finale del job sul client, insieme agli aggregati finali di alcuni dei
contatori pi importanti che ha raggruppato lungo il percorso. Il job completo e la
cronologia delle attivit sono disponibili nella directory di log su ciascun nodo o,
in modo pi accessibile, attraverso linterfaccia web del JobTracker; puntate il
browser alla porta 50030 sul nodo del JobTracker.
Input/Output
Abbiamo parlato della suddivisione dei file in split come parte della fase
iniziale del job e di come i dati in uno split vengano inviati allimplementazione
del mapper. Non abbiamo per considerato due aspetti: come i dati vengono
memorizzati nel file e come le singole chiavi e i valori vengono passati alla
struttura del mapper.
InputFormat e RecordReader
Per il primo di questi compiti Hadoop si serve di InputFormat. La classe astratta
InputFormat nel package org.apache.hadoop.mapreduce fornisce due metodi, come mostrato
prossimo paragrafo.
TextInputFormat: viene utilizzato per i file di solo testo.
usi dati non basati su file come input per i job di MapReduce; le sorgenti pi
comuni sono i database relazionali o orientati alle colonne, come Amazon
DynamoDB o HBase.
contenitore SequenceFile.
OutputFormat e RecordWriter
Esiste un pattern analogo per scrivere un output di job coordinato da sottoclassi
di OutputFormat e RecordWriter dal package org.apache.hadoop.mapreduce. Non lo tratteremo
nel dettaglio, ma lapproccio simile, sebbene in OutputFormat le API siano pi
coinvolte, poich il pattern ha i suoi metodi per attivit come la validazione della
specifica delloutput.
questo il passaggio che comporta il fallimento di un job se esiste gi una
directory di output specificata. Per ottenere un comportamento diverso,
occorrerebbe una sottoclasse di OutputFormat che escluda questo metodo.
SequenceFile
La classe SequenceFile nel package org.apache.hadoop.io fornisce un efficace formato
di file binario che spesso utile per loutput di un job di MapReduce. Vale
soprattutto se loutput del job viene elaborato come input di un altro job. I file
sequenza hanno numerosi vantaggi.
In quanto file binari, sono intrinsecamente pi compatti dei file di testo.
Supportano una compressione facoltativa supplementare che pu essere
applicata a diversi livelli, ossia su ciascun record o sullintero split.
Possono essere suddivisi ed elaborati in parallelo.
Questultima caratteristica particolarmente importante perch la maggior parte
dei formati binari, e soprattutto quelli che sono compressi o criptati, non pu
essere suddivisa e deve essere letta come un unico flusso lineare di dati. Se si
usano questi file come input di un job di MapReduce, verr impiegato un unico
mapper per elaborare il file, il che influir sulle prestazioni. In questi casi
preferibile utilizzare un formato suddivisibile, come SequenceFile, oppure
effettuare una pre-elaborazione che lo converta in questo senso. Pu essere un
inconveniente, perch la conversione occupa del tempo, ma in molti casi,
soprattutto nelle attivit di map pi complesse, questo verr controbilanciato dal
tempo risparmiato attraverso laumento del parallelismo.
YARN
YARN nato come parte delliniziativa MapReduce v2 (MRv2), ma ora un
sottoprogetto indipendente allinterno di Hadoop (quindi allo stesso livello di
MapReduce). Lo spunto da cui sorto il rendersi conto che MapReduce in
Hadoop 1 fondeva due responsabilit legate ma distinte: la gestione delle risorse e
lesecuzione dellapplicazione.
Per quanto abbia reso possibile lelaborazione prima inimmaginabile di dataset
enormi, a livello concettuale il modello di MapReduce ha un certo impatto sulle
prestazioni e sulla scalabilit. Implicito in esso che qualsiasi applicazione pu
essere costituita solo da una serie di job perlopi lineari, ognuno dei quali segue
un modello di una o pi azioni di map seguita da una o pi azioni di reduce. Questo
pattern ben si adatta ad alcune applicazioni, ma non a tutte. In particolare, mal si
presta ai carichi di lavoro che richiedono tempi di risposta a bassa latenza; i tempi
di avvio di MapReduce e le catene dei job spesso piuttosto lunghe superano di
molto la tolleranza per un processo che palese allutente. inoltre un modello
poco efficiente per i job che potrebbero essere rappresentati meglio come un DAG
(directed acyclic graph, grafico aciclico diretto) di attivit, i cui i nodi nel grafico
sono i passi dellelaborazione e gli archi sono i flussi di dati. Se analizzata ed
eseguita come DAG, allora lapplicazione pu essere eseguita in un solo passo
con un parallelismo elevato tra le varie fasi; se invece viene vista attraverso la
lente di MapReduce, il risultato solitamente una serie non efficiente di job
interdipendenti.
Diversi progetti hanno costruito numerosi tipi di elaborazione su MapReduce, e
sebbene molti abbiano avuto successo (Apache Hive e Pig sono due esempi
clamorosi), laccoppiamento stretto tra MapReduce come paradigma di
elaborazione e il meccanismo di programmazione dei job in Hadoop 1 ha reso
molto difficile per qualsiasi nuovo progetto ladattamento di questi ambiti alle
proprie necessit specifiche.
La soluzione YARN (Yet Another Resource Negotiator), che offre un
meccanismo di programmazione dei job di Hadoop molto potente e alcune
interfacce definite per i vari modelli di elaborazione da implementare al suo
interno.
Larchitettura di YARN
Per capire il funzionamento di YARN, importante smettere di pensare a
MapReduce e al modo in cui elabora i dati. YARN stesso non ci dice niente sulla
natura delle applicazioni costruite su di esso, mentre focalizzato sul fornire i
meccanismi per programmare ed eseguire i job. Come vedremo, YARN in grado
di ospitare tanto lelaborazione prolungata di stream di dati o di carichi di lavoro
a bassa latenza visibili allutente quanto di carichi di lavoro eseguiti in batch,
come MapReduce.
I componenti di YARN
YARN costituito da due componenti principali: il ResourceManager (RM),
che gestisce le risorse nel cluster, e il NodeManager (NM), che gira su ciascun
host e gestisce le risorse sulla singola macchina. Il ResourceManager e i
NodeManager si occupano della programmazione e della gestione dei contenitori,
una nozione astratta della memoria, della CPU e dellI/O che verranno dedicati a
una parte particolare del codice dellapplicazione. Prendendo MapReduce come
esempio, quando viene eseguito su YARN, il JobTracker e i TaskTracker girano
tutti nei loro contenitori specifici. Tuttavia, in YARN, ogni job di MapReduce ha il
proprio JobTracker dedicato; non c ununica istanza che gestisce tutti i job, come
in Hadoop 1.
YARN responsabile solo della programmazione delle attivit nel cluster; il
progresso a livello dellapplicazione, il monitoraggio e la tolleranza ai guasti sono
gestiti nel codice dellapplicazione. Si tratta di una decisione di design molto
chiara: rendendo YARN il pi indipendente possibile, i suoi compiti sono ben
definiti, e non vincola in modo innaturale i tipi di applicazione che possono essere
eseguiti su di esso.
Da arbitro di tutte le risorse del cluster, ha la capacit di gestirlo come intero
senza focalizzarsi sulle necessit delle risorse a livello di applicazione. Offre una
policy di programmazione nelle implementazioni fornite simile a quella di Hadoop
Capacity e Fair Scheduler; inoltre tratta il codice di tutte le applicazioni come
untrusted a prescindere, e tutte le attivit di gestione e controllo vengono eseguite
nello spazio utente.
Anatomia di unapplicazione YARN
Unapplicazione inviata a YARN ha due componenti: lApplicationMaster
(AM), che coordina il flusso generale dellapplicazione, e la specifica del codice
che verr eseguita sui nodi worker. Per MapReduce su YARN, il JobTracker
implementa la funzionalit ApplicationMaster, e i TaskTracker costituiscono il
codice personalizzato dellapplicazione distribuito sui nodi worker.
Come detto nel paragrafo precedente, in YARN, le responsabilit della gestione
dellapplicazione, del monitoraggio e della tolleranza ai guasti sono portate sul
livello dellapplicazione. Queste attivit vengono svolte dallApplicationMaster;
YARN non dice niente sui meccanismi di comunicazione tra lApplicationMaster e
il codice eseguito nei contenitori worker, per esempio.
Questa genericit consente alle applicazioni YARN di essere svincolate dalle
classi Java. LApplicationManager pu invece richiedere un NodeManager per
eseguire gli script della shell, le applicazioni native o qualsiasi altro tipo di
elaborazione resa disponibile su ciascun nodo.
Pensare a livelli
Queste ultime affermazioni potrebbero suggerire che scrivere applicazioni da
eseguire su YARN sia un lavoro impegnativo, ed cos. LAPI YARN di livello
piuttosto basso e spaventa abbastanza la maggior parte degli sviluppatori che
vogliono semplicemente eseguire alcune attivit di elaborazione sui loro dati. Se
avessimo solo YARN e in ogni nuova applicazione Hadoop dovesse essere
implementato un ApplicationMaster, YARN non sarebbe cos interessante.
La cosa che migliora la situazione che, in generale, la richiesta non quella di
implementare ogni singola applicazione su YARN, bens di utilizzarlo per un
numero di framework di elaborazione pi piccoli che forniscono interfacce pi
friendly da implementare. La prima MapReduce; con questa ospitata su YARN,
lo sviluppatore scrive le consuete interfacce map e reduce, ed quasi totalmente
ignaro dei meccanismi di YARN.
Tuttavia, pu accadere che un altro sviluppatore esegua sullo stesso cluster un
job che utilizza un framework diverso con altre caratteristiche di elaborazione, e
che YARN li gestisca entrambi nello stesso tempo.
Forniremo i dettagli sui vari modelli di elaborazione di YARN attualmente
disponibili, che vanno dallelaborazione in batch alle query a bassa latenza fino
allelaborazione degli stream e dei grafici e oltre.
Con il crescere dellesperienza di YARN, tuttavia, sono nate alcune iniziative
che facilitano lo sviluppo dei framework di elaborazione. Da una parte abbiamo
interfacce di livello pi alto, come Kitten (https://github.com/cloudera/kitten) o
Apache Twill (http://twill.incubator.apache.org/), che forniscono astrazioni pi
accessibili sulle API YARN. forse per pi significativo lemergere di
framework che forniscono strumenti pi ricchi che facilitano la costruzione delle
applicazioni attraverso una classe generica comune di caratteristiche prestazionali.
Modelli di esecuzione
Abbiamo citato diverse applicazioni YARN che hanno caratteristiche di
elaborazione distinte, ma non abbiamo ancora parlato di come viene gestito il
ciclo di vita di unapplicazione YARN, distinguendo tra tre tipi principali:
applicazione per job, per sessione e always on.
Nellelaborazione in batch, come MapReduce su YARN, il ciclo di vita del
framework MapReduce legato a quello dellapplicazione. Se inviamo un job di
MapReduce, il JobTracker e i TaskTracker che lo eseguono vengono creati
specificatamente per quel job e vengono terminati quando questo completato.
Questo funziona bene in batch, ma se vogliamo fornire un modello pi interattivo,
loverhead di avvio per determinare lapplicazione YARN e le allocazioni di tutte
le sue risorse avr un impatto enorme sullesperienza dellutente se ogni comando
eseguito soffre di questa penalizzazione. Un ciclo di vita pi interattivo o basato
sulla sessione vedr lapplicazione YARN partire ed essere poi disponibile a
servire un certo numero di richieste/comandi inviati. Lapplicazione YARN
termina solo con luscita dalla sessione.
Rimane poi il concetto di applicazione a esecuzione prolungata, che elabora
stream di dati continui indipendenti da qualsiasi input interattivo. Per questi casi
ha pi senso che lapplicazione YARN parta ed elabori in continuazione i dati
recuperati attraverso qualche meccanismo esterno, Lapplicazione uscir solo
quando viene chiusa esplicitamente o in caso si verifichi una situazione non
ordinaria.
YARN nel mondo reale: il calcolo oltre
MapReduce
La trattazione precedente rimasta un po astratta, quindi in questo paragrafo
esploreremo alcune applicazioni YARN per vedere come utilizzano il framework e
come ampliano la capacit di elaborazione. Di particolare interesse sono i
framework YARN che adottano approcci diversi per gestire le risorse, la pipeline
di I/O e la tolleranza ai guasti.
Tez
Tez (http://tez.apache.org) unAPI a basso livello e un motore di esecuzione
concentrato sullelaborazione a bassa latenza, ed stata utilizzata come base
dellevoluzione pi recente di Hive, Pig e molti altri framework che implementano
operazioni standard di join, filtro, unione e raggruppamento.
Sebbene la topologia dag del grafico possa essere espressa con poche righe di
codice, il boilerplate richiesto per eseguire il job notevole. Questo codice
gestisce molte delle responsabilit di programmazione ed esecuzione di basso
livello, compresa la tolleranza ai guasti. Quando Tez individua unattivit fallita,
ripercorre il grafico di elaborazione per trovare il punto da cui rieseguire le
attivit fallite.
Hive su Tez
Hive 0.13 il primo progetto di alto profilo per utilizzare Tez come motore di
esecuzione. Parleremo di Hive nel dettaglio nel Capitolo 7; per ora considereremo
solo come viene implementato su YARN.
Figura 3.3 Un DAG di Tez una generalizzazione di MapReduce.
Apache Spark
Spark (http://spark.apache.org) un framework che eccelle nellelaborazione
interattiva e quasi in tempo reale. Creato presso lUC a Berkeley, stato donato
come progetto Apache. Spark fornisce unastrazione che consente ai dati in
Hadoop di essere visualizzati come struttura distribuita su cui possono essere
eseguite alcune serie di operazioni.
Il framework si basa sugli stessi concetti da cui Tez trae ispirazione (Dryad),
ma primeggia con i job che consentono ai dati di essere contenuti ed elaborati in
memoria, e pu programmare in modo efficiente lelaborazione sui dataset in
memoria nel cluster.
Spark controlla automaticamente la replica dei dati nel cluster, garantendo che
ciascun elemento del dataset distribuito sia contenuto in memoria su almeno due
macchine, e offre una tolleranza ai guasti basata sulla replica in qualche modo
simile ad HDFS.
Spark nato come sistema standalone, ma nella release 0.8 stato configurato
anche per girare su YARN. interessante perch, nonostante il suo modello di
elaborazione classico sia orientato al batch, fornisce con la shell un frontend
interattivo, e con il sottoprogretto Spark offre anche unelaborazione quasi in
tempo reale degli stream di dati. Spark qualcosa di diverso a seconda di chi lo
usa; tanto unAPI ad alto livello quanto un motore di esecuzione. Al momento
della stesura di questo libro, in via di sviluppo il porting a Spark di Hive e Pig.
Apache Samza
Samza (http://samza.apache.org) un framework di elaborazione degli stream
sviluppato in LinkedIn e donato allApache Software Foundation. Samza elabora
concettualmente stream di dati infiniti che vengono visti dallapplicazione come
una serie di messaggi.
Samza si integra attualmente in modo stretto con Apache Kafka
(http://kafka.apache.org), sebbene abbia unarchitettura pluggable. Kafka stesso un
sistema di messaging che eccelle con i grandi volumi di dati e fornisce
unastrazione basata sui topic simile a quella della maggior parte delle altre
piattaforme di messaging, come RabbitMQ. I publisher inviano messaggi ai topic,
e i clienti interessati utilizzano i messaggi dai topic man mano che arrivano. Kafka
ha alcune caratteristiche che lo distinguono da altre piattaforme di messaging; per
questa trattazione, quella che pi ci interessa la sua capacit di memorizzare i
messaggi per un dato periodo di tempo, il che permette la loro riproduzione nei
topic. I topic vengono suddivisi su pi host e le partizioni possono essere replicate
tra gli host per proteggere i nodi dal fallimento.
Samza costruisce il suo flusso di elaborazione in base al concetto di stream, che
quando si usa Kafka vengono mappati direttamente sulle partizioni di questo. Un
tipico job di Samza potrebbe rilevare un topic per quanto riguarda i messaggi in
arrivo, eseguire alcune trasformazioni e poi scrivere loutput su un topic diverso.
Pi job di Samza possono poi essere uniti per fornire strutture di elaborazione pi
complesse.
In quanto applicazione di YARN, Samza ApplicationMaster monitora la salute
di tute le attivit di Samza. Se una di queste fallisce, viene istanziata unattivit
sostitutiva in un nuovo contenitore.
Samza raggiunge la tolleranza ai guasti facendo scrivere a ogni attivit il suo
progresso in un nuovo stream (modellato nuovamente come un topic Kafka); in
questo modo qualsiasi attivit sostituiva dovr semplicemente leggere lo stato pi
recente di unattivit da questo topic di checkpoint e poi riprodurre il topic del
messaggio principale dallultima posizione elaborata. Samza offre inoltre un
supporto per lo stato locale dellattivit, utile per i carichi di lavoro di tipo join o
di aggregazione. Lo stato locale viene costruito sullastrazione dello stream, e
quindi intrinsecamente resiliente ai fallimenti dellhost.
Nel capitolo precedente abbiamo parlato di YARN e della portata che hanno i
modelli di calcolo e i framework di elaborazione allesterno del tradizionale
MapReduce basato sul batch abilitato sulla piattaforma Hadoop. In questo capitolo
e nel prossimo esploreremo due di questi progetti in profondit: Apache Samza e
Apache Spark. Abbiamo scelto questi framework perch dimostrano luso
dellelaborazione degli stream e iterativa e perch forniscono meccanismi
interessanti per combinare i vari paradigmi di elaborazione. In questo capitolo
studieremo Samza e tratteremo i seguenti argomenti.
Cos Samza e come si integra con YARN e con altri progetti come Apache
Kafka.
Come Samza fornisce una semplice interfaccia di basata sul callback per
lelaborazione degli stream.
Come Samza unisce pi job di elaborazione degli stream in flussi di lavoro
complessi.
Come Samza supporta lo stato locale persistente allinterno delle attivit per
arricchire tutto quanto pu abilitare.
Elaborazione degli stream con Samza
Per analizzare una piattaforma pura di elaborazione degli stream, utilizzeremo
Samza, disponibile allindirizzo https://samza.apache.org. Il codice l mostrato stato
testato con lattuale release 0.8, e manterremo il repository di GitHub aggiornato
man mano che il progetto evolve.
Samza stato realizzato in LinkedIn e poi donato allApache Software
Foundation nel settembre 2013. Negli anni, LinkedIn ha costruito un modello che
concettualizza gran parte dei suoi stream di dati; da questo si riscontrata la
necessit di un framework che fornisse un meccanismo friendly per lo sviluppatore
per elaborare questi stream ormai diffusi un po ovunque.
Il team di LinkedIn aveva rilevato che quando si tratta di elaborazione dei dati,
gran parte dellattenzione si concentrava agli estremi dello spettro; per esempio, i
carichi di lavoro RPC sono solitamente implementati come sistemi sincroni con
requisiti di latenza molto bassi o come sistemi batch in cui la periodicit dei job
spesso misurata in ore. Larea intermedia stato poco supportata, e questo
lambito a cui si punta Samza; la maggior parte dei suoi job prevede tempi di
risposta che vanno da millisecondi a minuti, presumendo inoltre che i dati arrivino
in uno stream teoricamente infinito di messaggi continuativi.
Il livello dello streaming fornisce laccesso agli stream di dati, sia per il
consumo sia per la pubblicazione. Il livello di esecuzione offre il mezzo tramite
cui le applicazioni Samza possono essere eseguite, ottenere risorse di CPU e
memoria ed essere gestite nei propri cicli di vita. Il livello di elaborazione il
framework di Samza vero e proprio, e le sue interfacce permettono la funzionalit
basata sui messaggi.
Samza fornisce delle interfacce specifiche per supportare i primi due livelli,
sebbene le principali implementazioni correnti utilizzino Kafka per lo streaming e
YARN per lesecuzione. Approfondiremo largomento nei prossimi paragrafi.
Un modello indipendente
Sebbene in questo capitolo parleremo esclusivamente di Kafka e YARN come
strumenti che forniscono i livelli di streaming e di esecuzione di Samza,
importante ricordare che il core di Samza utilizza interfacce ben definite per i due
sistemi. Esistono implementazioni di sorgenti multiple di stream (ne vedremo una
nel prossimo paragrafo), e insieme al supporto di YARN, Samza offre una classe
LocalJobRunner. Questo metodo alternativo pu eseguire istanze di StreamTask durante
della stesura di questo libro, Samza era ancora un progetto relativamente recente,
per cui non abbiamo incluso informazioni dirette sugli esempi, che nel frattempo
potrebbero essere cambiati.)
Per il resto dei casi di Samza di questo capitolo, presupporremo che utilizziate
il package Hello Samza per quanto riguarda i componenti richiesti
(ZooKeeper/Kafka/YARN) o che abbiate implementato alcune integrazioni con
altre istanze di ciascuno.
In questo esempio abbiamo tre diversi job di Samza costruiti uno sullaltro. Il
primo legge le modifiche di Wikipedia, il secondo analizza questi record e il terzo
produce alcune statistiche in base ai record elaborati. Costruiremo il nostro flusso
di lavoro a pi stream tra breve.
Una delle cose pi interessanti qui lesempio di WikipediaFeed, che utilizza
Wikipedia invece di Kafka come propria sorgente dei messaggi. Nello specifico,
fornisce unaltra implementazione dellinterfaccia SystemConsumer di Samza per
consentirgli di leggere i messaggi provenienti da un sistema esterno. Come gi
visto, Samza non vincolato a Kafka e, come mostra lesempio, la costruzione di
una nuova implementazione dello stream non deve avvenire su un componente di
uninfrastruttura generica; devessere specifica per quel job, considerato che il
lavoro richiesto non enorme.
NOTA
La configurazione di default per ZooKeeper e Kafka scrive i dati di sistema nelle directory sotto
a /tmp, che sar la directory impostata se usate Hello Samza. Fate attenzione se utilizzate una
distribuzione Linux che cancella il contenuto di questa directory al reboot. Se intendete eseguire
dei test, preferibile che riconfiguriate questi componenti in modo che utilizzino meno posizioni
effimere. Modificate i file di configurazione pi importanti; si trovano nella directory dei servizi
sotto a hello-samza/deploy.
package com.learninghadoop2.samza.tasks;
public class TwitterParseStreamTask implements StreamTask {
@Override
public void process(IncomingMessageEnvelope envelope,
MessageCollector collector, TaskCoordinator coordinator) {
String msg = ((String) envelope.getMessage());
try {
JSONParser parser = new JSONParser();
Object obj = parser.parse(msg);
JSONObject jsonObj = (JSONObject) obj;
String text = (String) jsonObj.get(text);
collector.send(new OutgoingMessageEnvelope(new
SystemStream(kafka, tweets-parsed), text));
} catch (ParseException pe) {}
}
}
}
Il file di configurazione
Nel codice precedente non c nulla che dica da dove provengono i messaggi; il
framework li presenta semplicemente allimplementazione di StreamTask, ma
ovviamente Samza deve sapere dove recuperarli. Per ogni job esiste un file di
configurazione che definisce questo e altro. Quello che segue si pu trovare come
twitter-parse.properties alla pagina http://bit.ly/1MrpzPZ:
# Job
job.factory.class=org.apache.samza.job.yarn.YarnJobFactory
job.name=twitter-parser
# YARN
yarn.package.path=file:///home/gturkington/samza/build/distributions/learninghadoop2-
0.1.tar.gz
# Attivit
task.class=com.learninghadoop2.samza.tasks.TwitterParseStreamTask
task.inputs=kafka.tweets
task.checkpoint.factory=org.apache.samza.checkpoint.kafka.KafkaCheckpointManagerFactory
task.checkpoint.system=kafka
# Serializer
serializers.registry.string.class=org.apache.samza.serializers.StringSerdeFactory
# Sistemi
systems.kafka.samza.factory=org.apache.samza.system.kafka.KafkaSystemFactory
systems.kafka.streams.tweets.samza.msg.serde=string
systems.kafka.streams.tweets-parsed.samza.msg.serde=string
systems.kafka.consumer.zookeeper.connect=localhost:2181/
systems.kafka.consumer.auto.offset.reset=largest
systems.kafka.producer.metadata.broker.list=localhost:9092
systems.kafka.producer.producer.type=sync
systems.kafka.producer.batch.num.messages=1
Pu sembrare molto codice, ma per ora limitiamoci alla struttura di alto livello
e ad alcune impostazioni chiave. La sezione Job imposta YARN come framework di
esecuzione e assegna un nome al job. Se dovessimo eseguire pi copie dello
stesso job, dovremmo anche assegnare a ogni copia un ID univoco. La sezione
delle attivit specifica la classe di implementazione della nostra attivit e anche il
nome degli stream per i quali dovrebbe ricevere dei messaggi. I Serializer dicono a
Samza come leggere e scrivere i messaggi da e sullo stream, mentre lultima
sezione definisce i sistemi per nome e associa loro le classi di implementazione.
Nel nostro caso, definiamo solo un sistema chiamato kafka e vi facciamo
riferimento quando inviamo il nostro messaggio nellattivit precedente. Notate
che questo nome arbitrario, e potremmo darne uno qualsiasi. Per chiarezza ha
senso chiamare il sistema Kafka con lo stesso nome, ma solo una convenzione. A
volte dovrete assegnare nomi diversi quando avete a che fare con pi sistemi
simili, o magari quando trattate lo stesso sistema in modo diverso a seconda delle
parti del file di configurazione.
In questa sezione, specifichiamo anche il serde da associare agli stream usati
dallattivit. Ricordate che i messaggi di Kafka hanno un corpo e una chiave
facoltativa che viene utilizzata per determinare a quale partizione viene inviato il
messaggio. Samza deve sapere come trattare il contenuto delle chiavi e dei
messaggi per questi stream, e ha il supporto per gestirli come byte grezzi o come
tipi specifici, come stringhe, interi e JSON, come spiegato in precedenza.
Il resto della configurazione rimarr pressoch identico da job a job, poich
include cose come la posizione dellensemble dei ZooKeeper e dei cluster di
Kafka, e specifica come realizzare i checkpoint degli stream. Samza consente
unampia gamma di personalizzazioni; le varie opzioni sono riportate nel dettaglio
alla pagina http://bit.ly/1B4dhGo.
Gli 0 multipli potrebbero confondere, perch sono etichette e non conteggi. Ogni
broker nel sistema ha un ID che solitamente parte da 0, come accade con le
partizioni in ciascun topic. Loutput precedente ci dice che il topic chiamato tweets
ha ununica partizione con ID 0, che il broker che agisce da leader in quella
partizione il broker 0 e che il set delle ISR (in-sync replicas) per questa
partizione solo il broker 0. Questultimo valore particolarmente importante
quando si ha a che fare con la replica.
Utilizzeremo ora lutility Python dei capitoli precedenti per trarre i tweet JSON
dal flusso di Twitter, e poi ricorreremo a un produttore di messaggi della CLI di
Kafka per scrivere i messaggi in un topic di Kafka. Non un modo esattamente
efficiente di fare le cose, ma si adatta alla spiegazione. Supponendo che lo script
di Python si trovi nella nostra directory home, eseguiamo il prossimo comando
dallinterno della directory bin di Kafka:
$ python ~/stream.py j | ./kafka-console-producer.sh --broker-list localhost:9092 --topic
tweets
Questa attivit di Gradle non solo creer larchivio .tar.gz necessario nella
directory build/distributions, ma ne memorizzer anche una versione espansa sotto
a build/samza-package. Sar utile, perch Samza utilizzer gli script contenuti
nella directory bin dellarchivio per inviare effettivamente lattivit a YARN.
Eseguiamo il nostro job. Dobbiamo avere i percorsi di file per due elementi: lo
script run-job.sh di Samza per inviare un job a YARN e il file di configurazione per
il nostro job. Poich nel package del job creato sono riunite tutte le attivit
compilate, diremo a Samza quale attivit eseguire tramite un file di configurazione
diverso che specifica una classe di implementazione dellattivit nella propriet
task.class. Per eseguirla nel concreto, possiamo lanciare il seguente comando
Per comodit, abbiamo aggiunto unattivit di Gradle per eseguire questo job:
$ ./gradlew runTwitterParser
Per vedere loutput del job, utilizzeremo la CLI di Kafka per consumare i
messaggi:
$ ./kafka-console-consumer.sh zookeeper localhost:2181 topic tweets-parsed
Samza e HDFS
Potreste aver notato che abbiamo citato HDFS solo ora per la prima volta nella
nostra discussione su Samza. Se vero che Samza si integra strettamente con
YARN, non ha unintegrazione diretta con HDFS. A un livello logico, i sistemi di
implementazione degli stream di Samza (come Kafka) forniscono quel livello di
storage che viene solitamente fornito da HDFS per i tradizionali carichi di lavoro
di Hadoop. Nella terminologia dellarchitettura di Samza, come descritto in
precedenza, YARN il livello di esecuzione in entrambi i modelli, Samza usa un
livello di streaming per i suoi dati di origine e di destinazione mentre framework
come MapReduce utilizzano HDFS. Questo un buon esempio di come YARN non
solo abiliti modelli di calcolo che elaborano i dati in modo molto diverso dal
MapReduce orientato al batch, ma pu anche utilizzare sistemi di storage del tutto
differenti per i dati dorigine.
Windowing
Spesso torna utile generare alcuni dati in base ai messaggi ricevuti su uno
stream in una specifica finestra temporale. Per esempio, si possono registrare i
valori dellattributo top N misurati minuto per minuto. Samza supporta la
funzionalit di windowing attraverso linterfaccia WindowableTask, per la quale
devessere implementato questunico metodo:
public void window(MessageCollector collector, TaskCoordinator
coordinator);
@Override
public void process(IncomingMessageEnvelope envelope, MessageCollector collector,
TaskCoordinator coordinator) {
tweets++;
}
@Override
public void window(MessageCollector collector, TaskCoordinator
coordinator) {
collector.send(new OutgoingMessageEnvelope(new
SystemStream(kafka, tweet-stats), + tweets));
task.window.ms=5000
NOTA
In questo contesto, il termine windowing fa riferimento alla suddivisione concettuale di Samza
dello stream dei messaggi in intervalli di tempo e al meccanismo per eseguire lelaborazione
nel punto in cui si passa da un intervallo a un altro. Samza non offre direttamente
unimplementazione dellaltro uso del termine, cio la serie di finestre a scorrimento in cui una
serie di valori viene conservata ed elaborata nel tempo. Tuttavia, linterfaccia dellattivit di
windowing fornisce limpianto per implementare anche questo tipo di finestre.
if (en.equals(detectLanguage(rawtext))) {
collector.send(new OutgoingMessageEnvelope(new SystemStream(kafka, english-
tweets),
rawtext.toLowerCase()));
}
}
return li.getLanguage();
}
}
importante perch questa attivit necessaria per analizzare il testo dei tweet
estratto nellattivit precedente e per evitare la duplicazione della logica di
parsing JSON che al suo meglio incapsulata in un unico punto. Possiamo
eseguire questa attivit con il seguente comando:
$ ./gradlew runTextCleanup
Stream di bootstrap
In linea generale, la maggior parte dei job di elaborazione degli stream parte
dallelaborazione dei messaggi che arrivano dopo lavvio, ignorando solitamente
quelli precedenti. Considerata la sua riproducibilit degli stream, Samza non ha
questo vincolo.
Nel nostro job di sentiment analysis, avevamo due insiemi di termini di
riferimento: parole positive e negative. Anche se finora non labbiamo mostrato,
Samza pu consumare messaggi da pi stream; i meccanismi sottostanti
sonderanno tutti gli stream con nome e ne forniranno i messaggi, uno alla volta, al
metodo process. Potremo quindi creare stream per le parole positive e negative e
inviare i dataset su questi stream. Di primo acchito, potremmo programmare di
riavvolgere i due stream fino al punto pi indietro possibile e poi leggere i
tweet man mano che arrivano. Il problema che Samza non garantisce
lordinamento dei messaggi da pi stream, e sebbene esista un meccanismo che
assegna agli stream la priorit pi elevata, non possiamo dare per scontato che
tutte le parole negative e positive vengano elaborate prima dellarrivo del primo
tweet.
Per questi tipi di scenari, Samza prevede gli stream di bootstrap, cio
intermedi. Se per unattivit sono definiti degli stream di bootstrap, Samza li
legger dal primissimo offset finch non vengono elaborati per intero
(tecnicamente, legger gli stream finch non vengono raggiunti, cos che qualsiasi
parola nuova inviata a uno stream verr trattata senza priorit e arriver interposta
tra i tweet).
Creeremo ora un nuovo job chiamato TweetSentimentStreamTask che legge due stream
di bootstrap, ne riunisce il contenuto in alcune HashMap, raccoglie i conteggi in
corso per le tendenze del sentiment e utilizza una funzione window per generare
questi dati a intervalli. Potete trovare questo codice alla pagina
http://bit.ly/1EzCvUH:
@Override
public void process(IncomingMessageEnvelope envelope,
MessageCollector collector, TaskCoordinator coordinator) {
if (positive-words.equals(envelope.getSystemStreamPartition().
getStream())) {
positiveWords.add(((String) envelope.getMessage()));
} else if (negative-words.equals(envelope.getSystemStreamPartition().getStream())) {
negativeWords.add(((String) envelope.getMessage()));
} else if (english-tweets.equals(envelope.getSystemStreamPartition().getStream())) {
tweets++;
int positive = 0;
int negative = 0;
String words = ((String) envelope.getMessage());
@Override
public void window(MessageCollector collector, TaskCoordinator
coordinator) {
String msg = String.format(Tweets: %d Positive: %d Negative:
%d MaxPositive: %d MinPositive: %d, tweets, positiveTweets,
negativeTweets, maxPositive, maxNegative);
collector.send(new OutgoingMessageEnvelope(new
SystemStream(kafka, tweet-sentiment-stats), msg));
Dobbiamo poi indicare che i nostri sono due stream di bootstrap che devono
essere letti dal primo offset in ordine. In particolare, impostiamo tre propriet per
gli stream. Si dice che sono bootstrapped, ossia pronti prima di altri stream; per
raggiungere questo obiettivo, specifichiamo che loffset su ciascuno stream deve
essere reimpostato alla posizione pi vecchia (la prima):
systems.kafka.streams.positive-words.samza.bootstrap=true
systems.kafka.streams.positive-words.samza.reset.offset=true
systems.kafka.streams.positive-words.samza.offset.default=oldest
systems.kafka.streams.negative-words.samza.bootstrap=true
systems.kafka.streams.negative-words.samza.reset.offset=true
systems.kafka.streams.negative-words.samza.offset.default=oldest
Dopo lavvio del job, osservate loutput dei messaggi sullargomento tweet-
sentiment-stats.
NOTA
Per eseguire correttamente i job, si potrebbe pensare di dover avviare il job di parsing JSON
seguito da quello di cleanup prima di avviare il job del sentiment, ma qui non cos. I messaggi
non letti rimangono salvati in Kafka, quindi non importa lordine in cui vengono avviati i job in un
flusso di lavoro multiplo. Ovviamente il job del sentiment produrr conteggi di 0 tweet finch non
inizia a ricevere dati, ma se un job dovesse iniziare prima di un altro da cui dipende non
succeder niente.
Attivit stateful
Lultimo aspetto di Samza che esploreremo riguarda il modo in cui consente che
le partizioni degli stream di elaborazione delle attivit mantengano uno stato
locale persistente. Nel caso precedente abbiamo utilizzato delle variabili private
per tenere traccia dei totali intermedi, ma a volte utile che unattivit abbia uno
stato locale pi ricco. Un esempio potrebbe essere lesecuzione di un join logico
su due stream, dove pu essere comodo trarre un modello di stato da uno stream
per poi confrontarlo con laltro.
NOTA
Samza pu utilizzare gli stream partizionati per ottimizzare il join tra gli stream. Se ciascuno
stream da unire usa la stessa chiave di partizione (per esempio un ID utente), allora ogni
attivit che consuma quello stream ricever tutti i messaggi associati a ciascun ID tra tutti gli
stream.
Samza fornisce unaltra astrazione simile alla sua nozione di framework per
gestire i suoi lavori e che implementa le attivit. Definisce infatti uno store astratto
chiave-valore che pu avere pi implementazioni concrete, servendosi di alcuni
progetti open source esistenti per le implementazioni su disco (utilizzando
LevelDB dalla v0.7 e aggiungendo RocksDB dalla v0.8). Esiste anche uno store in
memoria che non conserva in modo persistente i dati chiave-valore ma che pu
essere utile durante i test o con carichi di lavoro di produzione potenzialmente
molto specifici.
Ogni attivit pu scrivere in questo store chiave-valore, e Samza ne gestisce la
persistenza sullimplementazione locale. Per supportare gli stati persistenti, lo
store modellato come uno stream, e anche tutto quanto viene scritto in esso viene
inserito in uno stream. Se unattivit fallisce, al riavvio pu recuperare lo stato del
suo store chiave-valore riproducendo i messaggi nel topic di backup. La
preoccupazione riguarda ovviamente il numero di messaggi che devono essere
riprodotti; tuttavia, Kafka, per esempio, comprime i messaggi con la stessa chiave,
cos che nel topic rimanga solo laggiornamento pi recente.
Ora modificheremo lesempio precedente del sentiment dei tweet per
aggiungere un conteggio sul sentiment positivo e negativo massimo dei vari tweet.
Il codice che segue si trova come TwitterStatefulSentimentStateTask.java alla pagina
http://bit.ly/1AiGBZT. Il metodo process lo stesso di TwitterSentimentStateTask.java,
@SuppressWarnings(unchecked)
@Override
public void init(Config config, TaskContext context) {
this.store = (KeyValueStore<String, Integer>) context.getStore(tweet-store);
}
@Override
public void process(IncomingMessageEnvelope envelope, MessageCollector collector,
TaskCoordinator coordinator) {
...
}
@Override
public void window(MessageCollector collector, TaskCoordinator coordinator) {
Integer lifetimeMaxPositive = store.get(lifetimeMaxPositive);
Integer lifetimeMaxNegative = store.get(lifetimeMaxNegative);
String msg =
String.format(
Tweets: %d Positive: %d Negative: %d MaxPositive: %d MaxNegative: %d
LifetimeMaxPositive: %d LifetimeMaxNegative: %d,
tweets, positiveTweets, negativeTweets, maxPositive, maxNegative,
lifetimeMaxPositive,
lifetimeMaxNegative);
specifichiamo che le chiavi sono del tipo String e i valori degli Integer. Nel metodo
window, recuperiamo altri valori eventualmente salvati in precedenza per il
Per comodit, il prossimo comando avvier quatto parser JSON: il cleanup del
testo, il job delle statistiche e i job stateful del sentiment:
$ ./gradlew runTasks
Azioni
Le operazioni vengono invocate passando le funzioni a Spark. Il sistema
gestisce le variabili e gli effetti collaterali secondo il paradigma di
programmazione funzionale. Le closure possono fare riferimento alle variabili
nellambito in cui sono state create. Esempi di azioni sono count (che restituisce il
numero di elementi nel dataset) e save (che genera il dataset nello storage). Altre
opzioni parallele sugli RDD includono le seguenti.
map: applica una funzione a ciascun elemento del dataset.
filter: seleziona gli elementi da un dataset secondo criteri forniti dallutente.
Distribuzione
Spark pu essere eseguito sia in modalit locale, analogamente al setup a nodo
singolo di Hadoop, sia come gestore di risorse (resource mananger). Tra i gestori
di risorse attualmente supportati troviamo i seguenti:
Spark Standalone Cluster Mode;
YARN;
Apache Mesos.
Spark su YARN
Per distribuire Spark su YARN occorre un file JAR consolidato ad hoc. Spark
avvia unistanza del cluster distribuito standalone dentro il ResourceManager.
Cloudera e MapR forniscono entrambi Spark su YARN come parte della loro
distribuzione software. Al momento della stesura di questo libro, Spark
disponibile per HDP di Hortonworks come anteprima della tecnologia
(http://hortonworks.com/hadoop/spark/).
Spark su EC2
Spark fornisce uno script di distribuzione, spark-ec2, collocato nella directory
ec2. Questo script imposta automaticamente Spark e HDFS su un cluster di istanze
di EC2. Per poter avviare un cluster di Spark sul cloud di Amazon, accedete alla
directory ec2 ed eseguite questo comando:
./spark-ec2 -k <keypair> -i <key-file> -s <num-slaves> launch <cluster-name>
<keypair> il nome della vostra coppia EC2, <key-file> il file della chiave
privata per la coppia di chiavi, <num-slaves> il numero dei nodi slave da avviare e
<cluster-name> il nome da assegnare al cluster. Consultate il Capitolo 1 per
Copiatelo in HDFS:
$ hdfs dfs -put sample.txt /tmp
La catena corrisponde alle fasi di map e reduce con cui abbiamo gi una certa
familiarit. Nella fase di map, carichiamo ogni riga del dataset (flatMap),
scomponiamo ciascun tweet in una sequenza di parole, contiamo le occorrenze di
ogni parola (map) ed emettiamo le coppie (key, value). Nella fase di reduce,
raggruppiamo per chiave (word) e sommiamo i valori (m, n) per ottenere il conteggio
delle parole.
Infine, visualizziamo nella console i primi 10 elementi, counts.take(10):
counts.take(10).foreach(println)
API Scala
La prima cosa che un driver di Spark deve fare creare un oggetto SparkContext
che dica a Spark come accedere a una cluster. Questo avviene dopo aver importato
le classi e aver effettuato le conversioni implicite in un programma, cos:
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
API Java
Il package org.apache.spark.api.java espone tutte le caratteristiche di Spark
disponibili nella versione di Scala su Java. LAPI Java ha una classe
JavaSparkContext che restituisce istanze di org.apache.spark.api.java.JavaRDD e lavora con
Per mantenere la sicurezza del tipo, alcuni metodi RDD e di funzione, come
quelli che gestiscono le coppie di chiavi e i double, sono implementate come
classi specializzate.
WordCount in Java
Un esempio di WordCount in Java incluso nella distribuzione del codice
sorgente di Spark alla pagina
examples/src/main/java/org/apache/spark/examples/JavaWordCount.java.
Costruiamo poi un RDD dallinfile HDFS. Nel primo passo della catena di
trasformazione, scomponiamo ogni tweet in un dataset e restituiamo un elenco di
parole. Utilizziamo unistanza di JavaPairRDD<String, Integer> per contare le
occorrenze di ogni parola. Per finire, riduciamo lRDD a una nuova istanza di
JavaPairRDD<String, Integer> che contiene una lista di tuple, dove ciascuna rappresenta
output un elenco di stringhe separate da uno spazio bianco. flatMap di Spark applica
questa funzione a ogni riga del dataset tweets.
Nella fase di map, per ogni token word, lambda word: (word, 1) restituisce tuple (word,
1) che indicano loccorrenza di una parola nel dataset. In reduceByKey, raggruppiamo
queste tuple per chiave (word) e sommiamo i valori per ottenere un conteggio delle
parole con lambda m,n:m+n.
Lecosistema di Spark
Apache Spark fornisce diversi strumenti, sia come libreria sia come motore di
esecuzione.
Spark Streaming
Spark Streaming (http://spark.apache.org/docs/latest/streaming-programming-guide.html)
unestensione dellAPI Scala che consente lelaborazione di dati da stream come
i socket Kafka, Flume, Twitter, ZeroMQ e TCP.
Spark Streaming riceve stream di dati di input live e li divide in batch (finestre
temporali dimensionate in modo arbitrario) che vengono poi elaborati dal motore
core di Spark per generare lo stream finale dei risultati. Questa astrazione di alto
livello chiamata DStream (org.apache.spark.streaming.dstream.DStreams) ed
implementata come una sequenza di RDD. DStream rende possibili due tipi di
operazioni: quelle di trasformazione e quelle di output.
Le trasformazioni lavorano su uno o pi DStream per crearne di nuovi. Come parte
della catena delle trasformazioni, i dati possono essere resi persistenti a un livello
di storage (HDFS) oppure su un canale di output. Spark Streaming consente le
trasformazioni in una finestra scorrevole di dati.
Unoperazione basata su finestre richiede la specifica di tre parametri: la
lunghezza della finestra, la sua durata e lintervallo di scorrimento in base al quale
loperazione verr eseguita.
GraphX
GraphX (https://spark.apache.org/docs/latest/graphx-programming-guide.html) unAPI
di calcolo grafico che espone un set di operatori e algoritmi per la computazione
orientata ai grafici, oltre che una variante ottimizzata di Pregel.
MLlib
MLlib (http://spark.apache.org/docs/latest/mllib-guide.html) fornisce funzionalit
Machine Learning (ML) comuni, tra cui test e generatori di dati. MLlib supporta
attualmente quattro tipi di algoritmi: classificazione binaria, regressione,
clustering e filtro collaborativo.
Spark SQL
Spark SQL deriva da Shark, unimplementazione del sistema di warehousing di
Hive che utilizza Spark come motore di esecuzione. Parleremo di Hive nel
Capitolo 7. Con Spark SQL, possibile mescolare query di tipo SQL con codice
Scala o Python. I set di risultati restituiti da una query sono degli RDD, e in quanto
tali possono essere manipolati dai metodi principali di Spark o da MLlib e
GraphX.
Elaborare i dati con Apache Spark
In questo paragrafo implementeremo gli esempi del Capitolo 3 utilizzando lAPI
Scala. Considereremo sia lo scenario dellelaborazione a batch sia quello in
tempo reale. Vedremo come Spark Streaming pu essere utilizzato per effettuare
calcoli statistici sullo stream live di Twitter.
Con le righe seguenti si pu generare uno script helper per eseguire le classi
compilate:
$ sbt add-start-script-tasks
$ sbt start-script
Qui <master> lURI del nodo master. Una sessione interattiva di Scala pu
essere invocata tramite sbt con il seguente comando:
$ sbt console
object HashtagCount {
def main(args: Array[String]) {
[]
val sc = new SparkContext(master,
HashtagCount,
System.getenv(SPARK_HOME))
counts.saveAsTextFile(outputPath)
}
}
Creiamo un RDD iniziale da un dataset salvato in HDFS inputFile e
applichiamo una logica simile a quella dellesempio di WordCount.
Per ogni tweet nel dataset, estraiamo un array di stringhe che corrisponde al
pattern degli hashtag (pattern findAllIn line).toArray e contiamo unoccorrenza di
ciascuna stringa usando loperatore di mappa. In questo modo si genera un nuovo
RDD come lista di tuple nella forma
(word, 1), (word2, 1), (word, 1)
object HashtagSentiment {
def main(args: Array[String]) {
[]
val sc = new SparkContext(master,
HashtagSentiment,
System.getenv(SPARK_HOME))
Come prima cosa, leggiamo un elenco di parole positive e negative negli oggetti
Set di Scala e filtriamo i commenti (le stringhe che iniziano con ;).
val window = 10
val ssc = new StreamingContext(master, TwitterStreamEcho,
Seconds(window), System.getenv(SPARK_HOME))
stateDstream.print
ssc.checkpoint(/tmp/checkpoint)
ssc.start()
}
counts.printSchema
top10.foreach(println)
}
sqlContext.sql(query)
})
ssc.checkpoint(/tmp/checkpoint)
ssc.start()
ssc.awaitTermination()
}
Perch i due lavorino insieme, creiamo prima uno SparkContext sc che utilizziamo
per istanziare sia uno StreamingContext ssc sia un sqlContext. Come prima, usiamo
TwitterUtils.createStream per creare un RDD DStream dstream. In questo esempio
Nei capitoli precedenti abbiamo esplorato alcune API per lelaborazione dei
dati. MapReduce, Spark, Tez e Samza sono di livello piuttosto basso, e scrivere
una logica business complessa con esse richiede spesso uno sviluppo Java
significativo. Inoltre, utenti diversi hanno esigenze diverse. Per un analista
potrebbe essere poco pratico scrivere codice di MapReduce o costruire DAG di
input e output per rispondere a una manciata di query semplici. Allo stesso tempo,
un ingegnere software o un ricercatore potrebbero voler creare dei prototipi delle
loro idee e dei loro algoritmi usando astrazioni di alto livello prima di passare ai
dettagli di implementazione di basso livello.
In questo capitolo e nel prossimo considereremo alcuni strumenti che forniscono
un modo per elaborare i dati in HDFS attraverso astrazioni di livello pi elevato.
Qui illustreremo Apache Pig, e in particolare affronteremo i seguenti argomenti.
Cos Apache Pig e quale modello di flusso dati fornisce.
I tipi di dati e le funzioni di Pig Latin.
Come migliorare Pig tramite del codice personalizzato dellutente.
Come utilizzare Pig per analizzare lo stream di Twitter.
Panoramica su Pig
In origine, il toolkit di Pig era costituito da un compilatore che generava
programmi MapReduce, accorpava le loro dipendenze e le eseguiva su Hadoop. I
job di Pig sono scritti in un linguaggio chiamato Pig Latin e possono essere
eseguiti sia in modalit interattiva sia in batch. Inoltre, Pig Latin pu essere esteso
usando le User Defined Function (UDF) scritte in Java, Python, Ruby, Groovy o
JavaScript.
Gli ambiti in cui si pu utilizzare Pig sono i seguenti:
elaborazione dei dati;
query di analisi ad hoc;
prototipazione rapida degli algoritmi;
pipeline estrazione-trasformazione-caricamento.
Seguendo una tendenza gi rilevata nei capitoli precedenti, Pig si sta muovendo
verso unarchitettura di calcolo general purpose. Dalla versione 0.13 linterfaccia
ExecutionEngine (org.apache.pig.backend.executionengine) agisce da ponte tra il
frontend e il backend di Pig, consentendo agli script Pig Latin di essere compilati
ed eseguiti su framework diversi da MapReduce.
Al momento della stesura di queste righe, la versione 0.13 era dotata di
MRExecutionEngine
(org.apache.pig.backend.hadoop.executionengine.mapReduceLayer.MRExecutionEngine); si ipotizza
che nella versione 0.14 (https://issues.apache.org/jira/browse/PIG-3446) verr incluso il
lavoro su un backend a bassa latenza basato su Tez
(http://pig.apache.org/docs/r0.14.0/api/org/apache/pig/backend/hadoop/executionengine/tez/plan/operator/pa
tree.html). Attualmente, inoltre, in corso lo sviluppo dellintegrazione di Spark
(https://issues.apache.org/jira/browse/PIG-4059).
Pig 0.13 fornisce alcuni miglioramenti nelle prestazioni per il backend di
MapReduce, in particolare con due funzionalit che riducono la latenza dei job pi
piccoli: laccesso diretto a HDFS (https://issues.apache.org/jira/browse/PIG-3642) e la
modalit locale automatica (https://issues.apache.org/jira/browse/PIG-3463). La prima
caratteristica (propriet opt.fetch) abilitata di default.
Quando si esegue un DUMP in uno script semplice (solo map) che contiene
esclusivamente gli operatori LIMIT, FILTER, UNION, STREAM o FOREACH, i dati di input
vengono raccolti da HDFS, e la query viene eseguita direttamente in Pig,
aggirando MapReduce. Con la modalit locale automatica (propriet
pig.auto.local.enabled) Pig esegue una query nella modalit locale di Hadoop quando
exec: avvia uno script Pig allinterno di una sessione interattiva di Grunt.
Elastic MapReduce
Gli script di Pig possono essere eseguiti su EMR creando un cluster con --
applications Name=Pig,Args=--version,<version>, cos:
Si pu anche eseguire ssh nel nodo master e poi passare alle istruzioni Pig Latin
in una sessione di Grunt con il seguente comando:
$ aws emr ssh --cluster-id <cluster id> --key-pair-file <key pair>
Fondamenti di Apache Pig
Linterfaccia principale per programmare Apache Pig Pig Latin, un linguaggio
procedurale che implementa i concetti del paradigma del flusso di dati.
I programmi Pig Latin sono solitamente organizzati come segue.
Unistruzione LOAD legge i dati da HDFS.
Alcune istruzioni aggregano e manipolano i dati.
Unistruzione STORE scrive loutput sul file system.
In alternativa, unistruzione DUMP visualizza loutput sul terminale.
Lesempio che segue mostra una sequenza di istruzioni che genera in output i
primi 10 hashtag ordinati per frequenza, estratti dal dataset di tweet:
tweets = LOAD tweets.json
USING JsonLoader(created_at:chararray,
id:long,
id_str:chararray,
text:chararray);
boolean: un booleano.
datetime: un datetime.
tuple: una lista ordinata di dati in cui gli elementi possono essere di tipo
separate da ,.
Di default, Pig tratta i dati come non tipizzati. Lutente pu dichiarare il tipo di
dato al momento del caricamento o eseguire il cast manuale se necessario. Se non
dichiarato alcun tipo di dato, ma se uno script tratta implicitamente un valore
come di un certo tipo, Pig lo dar per buono ed eseguir il cast di conseguenza. Si
pu fare riferimento ai campi di una bag di tuple in base al nome tuple.field o alla
posizione $<index>. Pig conta partendo da 0, quindi il primo elemento sar indicato
come $0.
Funzioni di Pig
Le funzioni interne sono implementate in Java, e cercano di seguire le
convenzioni standard di questo linguaggio. Vanno tuttavia considerate alcune
differenze.
I nomi delle funzioni sono case sensitive e in maiuscolo.
Se il valore risultate null, vuoto o not a number (NaN), Pig restituisce null.
Se Pig non riesce a elaborare lespressione, restituisce uneccezione.
Trovate un elenco di tutte le funzioni interne alla pagina
http://pig.apache.org/docs/r0.12.0/func.html.
Forniamo uno schema in modo tale che vengano mappati i primi cinque elementi
di un oggetto JSON, cio created_id, id, id_str, text e source. Guarderemo lo schema
di tweet utilizzando describe tweets, che restituisce quanto segue:
tweets: {created_at: chararray,id: long,id_str: chararray,text: chararray,source: chararray}
Valutazione (eval)
Le funzioni di valutazione implementano una serie di operazioni da applicare su
unespressione che restituisce una bag o una mappa dei tipi di dati. Lespressione
risultante viene valutata entro il contesto della funzione.
AVG(expression): calcola la media dei valori numerici in una bag a colonna
singola.
COUNT(expression) : conta tutti gli elementi con valori non null in prima posizione
in una bag.
COUNT_STAR(expression) : conta tutti gli elementi in una bag.
IsEmpty(expression): controlla se una bag o una mappa vuota.
Invocatori dinamici
Gli invocatori dinamici consentono lesecuzione di funzioni Java senza doverle
racchiudere in una UDF. Possono essere utilizzati per qualsiasi funzione statica
che:
non accetta argomenti o accetta una combinazione di string, int, long, double,
float o array con questi stessi tipi;
Macro
Dalla versione 0.9, il preprocessore di Pig Latin supporta lespansione delle
macro. Queste sono definite usando listruzione DEFINE:
DEFINE macro_name(param1, ..., paramN) RETURNS output_bag {
pig_latin_statements
};
Possiamo utilizzarla in uno script Pig o in una sessione di Grunt per contare il
numero di tweet:
tweets_count = count_rows(tweets);
DUMP tweets_count;
import count_rows.macro.
Le macro hanno alcuni limiti; in particolare, al loro interno sono ammesse solo
istruzioni Pig Latin. Non possibile usare istruzioni REGISTER e comandi della shell,
non sono ammesse le UDF e non supportata la sostituzione allinterno della
macro.
Filtro
Loperatore FILTER seleziona le tuple da una relazione basata su unespressione,
come segue:
relation = FILTER relation BY expression;
Possiamo utilizzarlo per filtrare i tweet il cui testo coincide con lespressione
regolare dellhashtag, cos:
tweets_with_tag = FILTER tweets BY
(text
MATCHES (?:\\s|\\A|^)[##]+([A-Za-z0-9-_]+)
);
Aggregazione
Loperatore GROUP raggruppa i dati in una o pi relazioni basate su unespressione
o una chiave:
relation = GROUP relation BY expression;
Il risultato di unoperazione GROUP una relazione che include una tupla per ogni
valore univoco dellespressione di gruppo. Questa tupla contiene due campi: il
primo si chiama group ed dello stesso tipo della chiave del gruppo; il secondo
campo prende il nome della relazione originale ed di tipo bag. I nomi di
entrambi i campi sono generati dal sistema.
Utilizzando la parola chiave ALL, Pig eseguir laggregazione sullintera
relazione. Lo schema GROUP tweets ALL aggregher tutte le tuple nello stesso gruppo.
Come gi detto, Pig consente la gestione esplicita del livello di simultaneit
delloperatore GROUP usando loperatore PARALLEL:
grpd = GROUP tweets BY (created_at, id) PARALLEL 10;
Foreach
Loperatore FOREACH applica le funzioni alle colonne, come segue:
relation = FOREACH relation GENERATE transformation;
anche possibile applicare una funzione alle colonne mostrate. Per esempio,
possiamo usare la funzione REGEX_TOKENIZE per scomporre ogni tweet in parole, cos:
t = FOREACH tweets_with_tag GENERATE FLATTEN(TOKENIZE(text)) as word;
Il modificatore FLATTEN scompone ulteriormente la bag generata da TOKENIZE in una
tupla di parole.
Join
Loperatore JOIN esegue un inner join di due o pi relazioni in base a dei valori
di campo comuni. La sua sintassi la seguente:
relation = JOIN relation1 BY expression1, relation2 BY expression2;
Filtriamo i commenti:
positive_words = FILTER positive BY NOT w MATCHES ^;.*;
positive_words una bag di tuple, ciascuna delle quali contiene una parola.
Possiamo scomporre il testo dei tweet in token e creare una nuova bag di tuple
(id_str, word) cos:
id_words = FOREACH tweets {
GENERATE
id_str,
FLATTEN(TOKENIZE(text)) as word;
}
Uniamo le due relazioni sul campo word e otterremo una relazione di tutti i tweet
che contengono una o pi parole positive:
positive_tweets = JOIN positive_words BY w, id_words BY word;
Repository di UDF
La base di codice di Pig ospita un repository di UDF chiamato Piggybank. Altri
repository popolari sono Elephant Bird di Twitter
(https://github.com/kevinweil/elephant-bird/) e Apache DataFu
(http://datafu.incubator.apache.org/).
Piggybank
Piggybank un luogo dove gli utenti di Pig condividono le loro funzioni. Il
codice condiviso si trova nel repository ufficiale Subversion alla pagina
http://bit.ly/1AA0QTV. La documentazione delle API recuperabile allindirizzo
Elephant Bird
Elephant Bird una libreria open source di Twitter di tutto quanto ha usato
Hadoop in produzione. Contiene diversi strumenti di serializzazione, formati
personalizzati di input e output, scrivibili, funzioni di caricamento/storage di Pig e
varie.
Elephant Bird dotata di una funzione loader JSON estremamente flessibile,
che al momento della stesura di queste righe era la risorsa privilegiata per la
manipolazione dei dati JSON in Pig.
Apache DataFu
Apache DataFu Pig raccoglie alcune funzioni di analisi sviluppate da LinkedIn.
Include funzioni statistiche e di stima, operazioni di bag e set, funzioni di
campionamento, hash e analisi dei link.
Analizzare lo stream di Twitter
Nei prossimi esempi utilizzeremo limplementazione del JsonLoader fornita da
Elephant Bird per caricare e manipolare dati JSON. Ci serviremo di Pig per
esplorare i metadati dei tweet e per analizzare le tendenze nel dataset. Infine,
modelleremo linterazione tra gli utenti attraverso un grafico e useremo Apache
DataFu per analizzare questo social network.
Prerequisiti
Scaricate i file JAR elephant-bird-pig (http://bit.ly/1CoSAL5), elephant-bird-hadoop-
compat (http://bit.ly/1NzA5VZ) ed elephant-bird-core (http://bit.ly/1Dniml6) dal repository
JSON e passa a Pig la mappa di valori risultante come una tupla costituita da un
unico elemento. Questo consente laccesso agli elementi delloggetto JSON senza
dover specificare preventivamente uno schema. Largomento nestedLoad istruisce la
classe affinch carichi strutture di dati annidate.
Le entit danno informazioni quali i tweet dai dati strutturati, gli URL, gli
hashtag e le menzioni senza doverle estrarre dal testo. Allindirizzo
https://dev.twitter.com/docs/entities trovate una loro descrizione. Lentit hashtag un
Le entit per gli oggetti user contengono le informazioni che appaiono nel profilo
dellutente e nei campi della descrizione. Possiamo estrarre gli ID dellautore del
tweet attraverso il campo user nella mappa del tweet:
users = FOREACH tweets GENERATE $0#user#id as id;
Statistiche top n
Una delle prime domande che potremmo porre riguarda la frequenza di
determinati elementi. Per esempio, potremmo voler creare un istogramma dei
primi 10 topic in base al numero di menzioni. Oppure potremmo voler trovare le
prime 50 nazioni o i primi 10 utenti. Prima di considerare i dati dei tweet,
definiremo una macro che ci permetter di applicare la stessa logica di selezione a
collezioni di elementi diverse:
DEFINE top_n(rel, col, n)
RETURNS top_n_items {
grpd = GROUP $rel BY $col;
cnt_items = FOREACH grpd
GENERATE FLATTEN(group), COUNT($rel) AS cnt;
cnt_items_sorted = ORDER cnt_items BY cnt DESC;
$top_n_items = LIMIT cnt_items_sorted $n;
}
Il metodo top_n prende una relazione rel, la colonna col su cui vogliamo
effettuare il conteggio e il numero di elementi n da restituire come parametri. Nel
blocco di Pig Latin, raggruppiamo prima rel per gli elementi in col, contiamo il
numero delle occorrenze di ciascun elemento, ordiniamole e selezioniamo ln pi
frequente.
Per trovare i primi 10 hashtag in inglese, filtriamo per lingua ed estraiamo il
testo:
tweets_en = FILTER tweets by $0#lang == en;
hashtags_bag = FOREACH tweets {
GENERATE
FLATTEN($0#entities#hashtags) AS tag;
}
hashtags = FOREACH hashtags_bag GENERATE tag#text AS tag;
Contiamo ora quante volte ogni codice della nazione e hashtag appaiono
insieme:
hashtags_country_frequency = FOREACH (GROUP hashtags_country ALL) {
GENERATE
FLATTEN(group),
COUNT(hashtags_country) as count;
}
Infine, contiamo le prime 10 nazioni in base allhashtag con la funzione TOP,
come segue:
hashtags_country_regrouped= GROUP hashtags_country_frequency BY cnt;
top_results = FOREACH hashtags_country_regrouped {
result = TOP(10, 1, hashtags_country_frequency);
GENERATE FLATTEN(result);
}
Manipolazione di datetime
Il campo created_at nei tweet JSON ci fornisce alcune informazioni temporali su
quando il tweet stato postato. Sfortunatamente, il suo formato non compatibile
con il tipo datetime interno di Pig.
Piggybank viene in nostro aiuto con alcune UDF per la manipolazione del
tempo, contenute in org.apache.pig.piggybank.evaluation.datetime.convert. Una di queste
CustomFormatToISO, che converte un timestamp formattato in modo arbitrario in una
Adesso non resta che raggruppare gli hourly_tweets per ora e generare un
conteggio dei tweet per gruppo, in questo modo:
hourly_tweets_count = FOREACH (GROUP hourly_tweets BY hour) {
GENERATE FLATTEN(group), COUNT(hourly_tweets);
}
Sessioni
La classe Sessionize di DataFu pu aiutarci a meglio catturare lattivit di un
utente nel tempo. Una sessione rappresenta lattivit di un utente in un dato
periodo. Per esempio, possiamo guardare lo stream di tweet di ogni utente a
intervalli di 15 minuti e misurare queste sessioni per determinare sia il volume di
rete sia lattivit dellutente:
DEFINE Sessionize datafu.pig.sessions.Sessionize(15m);
users_activity = FOREACH tweets {
GENERATE
CustomFormatToISO($0#created_at,
EEE MMMM d HH:mm:ss Z y) AS dt,
(chararray)$0#user#id as user_id;
}
users_activity_sessionized = FOREACH
(GROUP users_activity BY user_id) {
ordered = ORDER users_activity BY dt;
GENERATE FLATTEN(Sessionize(ordered))
AS (dt, user_id, session_id);
}
Il primo elemento della bag di input un timestamp ISO 8601, e la bag deve essere
ordinata secondo questo timestamp. Gli eventi che avvengono entro 15 minuti
luno dallaltro rientreranno nella stessa sessione.
La bag di input viene restituita con un nuovo campo, session_id, che identifica in
modo univoco una sessione. Con questi dati, possiamo calcolare la lunghezza della
sessione e altre statistiche. Trovate altri esempi delluso di Sessionize alla pagina
.
http://datafu.incubator.apache.org/docs/datafu/guide/sessions.html
GENERATE
CustomFormatToISO(dt, EEE MMMM d HH:mm:ss Z y) AS dt,
SHA((chararray)CONCAT(SALT, user_id)) AS source,
SHA(((chararray)CONCAT(SALT, tweet_id))) AS tweet_id,
((reply_to_tweet IS NULL)
? NULL
: SHA((chararray)CONCAT(SALT, reply_to_tweet)))
AS reply_to_tweet_id,
((reply_to IS NULL)
? NULL
: SHA((chararray)CONCAT(SALT, reply_to)))
AS destination,
(chararray)place#country_code as country,
FLATTEN(topics) AS topic;
}
Qui usiamo CONCAT per associare una stringa salt (non cos casuale) ai dati
personali. A seguire, generiamo lhash degli ID con salt con la funzione SHA di
DataFu. Questa richiede che i suoi parametri di input non siano null. Applichiamo
questa condizione utilizzando delle istruzioni if-then-else. In Pig Latin, lo
esprimiamo come <condition is true> ? <true branch> : <false branch> . Se la stringa
null, restituiamo NULL, altrimenti restituiamo un hash con salt. Per rendere il codice
pi leggibile, utilizziamo degli alias per i campi JSON del tweet e referenziamoli
nel blocco GENERATE.
Notate che Pig non ammette un cross join sulla stessa relazione, quindi
dobbiamo creare un tweet_hashtag per il lato destro del join. Qui usiamo loperatore
:: per evitare equivoci sulla relazione e la colonna di cui vogliamo selezionare i
record.
Ancora una volta possiamo cercare i primi 10 topic in base al numero delle
risposte usando la macro top_n:
top_10_topics = top_n(twitter_graph, topic_replied, 10);
Poich lUDF si aspetta come input una bag ordinata di valori integer, contiamo
prima la frequenza di ogni elemento topic_replied:
topics_with_replies_grpd = GROUP twitter_graph BY topic_replied;
topics_with_replies_cnt = FOREACH topics_with_replies_grpd {
GENERATE
COUNT(twitter_graph) as cnt;
}
Utenti influenti
Utilizzeremo ora PageRank un algoritmo sviluppato da Google per
classificare le pagine web (http://ilpubs.stanford.edu:8090/422/1/1999-66.pdf) per
identificare gli utenti influenti nel grafico di Twitter che abbiamo generato nel
paragrafo precedente.
Questo tipo di analisi meglio si adatta ad alcuni casi, come il targeted e il
contextual advertisement, i sistemi di raccomandazione, lindividuazione dello
spam e nella misurazione dellimportanza delle pagine web. Un approccio
analogo, utilizzato da Twitter per implementare la funzione Chi seguire, descritto
nel documento WTF: The Who to Follow service at Twitter found at
(http://stanford.edu/~rezab/papers/wtf_overview.pdf).
PageRank determina il valore di una pagina in base allimportanza di altre
pagine collegate a essa e le assegna un punteggio tra 0 e 1. Un punteggio PageRank
elevato indica che molte pagine puntano a questa. Essere collegati a pagine con un
PageRank alto ovviamente unattestazione di qualit. Nei termini del grafico di
Twitter, si presume che gli utenti che ricevono molte risposte siano importanti o
influenti nel social network. Nel caso di Twitter, consideriamo una definizione
estesa di PageRank, in cui il link tra due utenti dato da una risposta diretta ed
etichettata da un hashtag presente nel messaggio. Qui vogliamo identificare gli
utenti influenti rispetto a un dato topic.
Nellimplementazione di DataFu, ogni grafico rappresentato come una bag di
tuple (source, edges). La tupla source un ID integer che rappresenta il nodo
sorgente. Gli archi sono una bag di tuple (destination, weight). destination un ID
integer che rappresenta il nodo di destinazione e weight un double che rappresenta
il peso che deve avere larco. Loutput dellUDF una bag di coppie (source, rank),
dove rank il valore PageRank dellutente sorgente nel grafico. Qui abbiamo
parlato di nodi, archi e grafici in termini astratti. Nel caso di Google i nodi sono
pagine web, gli archi sono link da una pagina allaltra e i grafici sono gruppi di
pagine connesse in modo diretto e indiretto.
Nel nostro caso, i nodi rappresentano gli utenti e gli archi le menzioni
in_reply_to_user_id_str; gli archi sono etichettati dagli hashtag nei tweet. Loutput di
PageRank dovrebbe suggerirci quali utenti sono influenti rispetto a un dato topic
considerati i loro pattern di interazione.
In questo paragrafo scriveremo una pipeline per:
rappresentare i dati come un grafico in cui ogni nodo un utente e gli archi
sono etichettati da hashtag;
mappare gli ID e gli hashtag in integer cos che possano essere elaborati da
PageRank;
applicare PageRank;
conservare i risultati in HDFS in un formato interoperabile (Avro).
Rappresentiamo il grafico come bag di tuple nella forma (source, destination,
topic), dove ogni tupla rappresenta linterazione tra i nodi. Trovate il codice
La classe StringToInt prende una stringa come input, chiama il metodo hashCode() e
restituisce loutput del metodo a Pig. Il codice dellUDF si trova alla pagina
http://bit.ly/1O5CaMm:
package com.learninghadoop2.pig.udf;
import java.io.IOException;
import org.apache.pig.EvalFunc;
import org.apache.pig.data.Tuple;
Ora possiamo registrare lUDF in Pig e creare un alias per StringToInt, come
segue:
REGISTER myudfs-pig.jar
DEFINE StringToInt com.learninghadoop2.pig.udf.StringToInt();
Filtriamo poi i tweet senza destination n topic:
tweets_graph_filtered = FILTER twitter_graph by
(destination IS NOT NULL) AND
(topic IS NOT null);
Una volta che i dati sono nel formato opportuno, possiamo riutilizzare
limplementazione di PageRank e il codice di esempio (che trovate alle pagina
http://bit.ly/1CskfuG) fornito da DataFu, come mostrato di seguito:
Contiamo le occorrenze di ogni tupla, ossia quante volte due persone hanno
discusso di un certo topic:
topic_edges = foreach reply_to {
GENERATE flatten(group), ((double)COUNT(from_to.topic_id)) as w;
}
Ricordate che il topic larco del grafico; partiamo creando unassociazione tra
il nodo sorgente e il topic sorgente, cos:
topic_edges_grouped = GROUP topic_edges by (topic_id, source_id);
Una volta creato il grafico di Twitter, calcoliamo il PageRank di tutti gli utenti
(source_id):
topic_rank = FOREACH (GROUP topic_edges_grouped BY topic) {
GENERATE
group as topic,
FLATTEN(PageRank(topic_edges_grouped.(source,edges))) as (source,rank);
}
topic_rank = FOREACH topic_rank GENERATE topic, source, rank;
Salviamo il risultato in HDFS nel formato Avro. Se nel percorso delle classi
non ci sono dipendenze Avro, dovremo aggiungere il file JAR MapReduce di Avro
al nostro ambiente prima di accedere ai singoli campi. In Pig, per esempio,
aggiungiamo quanto segue sulla Cloudera CDH5 VM:
REGISTER /opt/cloudera/parcels/CDH/lib/avro/avro.jar
REGISTER /opt/cloudera/parcels/CDH/lib/avro/avro-mapred-hadoop2.jar
STORE topic_rank INTO replies-pagerank using AvroStorage();
NOTA
Negli ultimi paragrafi abbiamo dato per scontate un paio di cose sullaspetto che potrebbe avere
un grafico di Twitter e sul significato dei concetti di topic e interazione dellutente. Considerati i
vincoli posti, il social network risultante che abbiamo analizzato sar relativamente piccolo e
non necessariamente rappresentativo dellintero Twitter. Lestrapolazione dei risultati da questo
dataset scoraggiata. Nella pratica, ci sono molti altri fattori di cui tener conto quando si deve
generare un modello robusto di interazione social.
Riepilogo
In questo capitolo abbiamo introdotto Apache Pig, una piattaforma per lanalisi
su larga scala su Hadoop. In particolare, abbiamo trattato quanto segue.
Gli obiettivi di Pig come sistema per fornire unastrazione a flusso di dati che
non richiede uno sviluppo effettivo su MapReduce.
Un confronto tra lapproccio di Pig e di SQL allelaborazione dei dati, dove
Pig un linguaggio procedurale e SQL dichiarativo.
Iniziare a lavorare con Pig: unattivit semplice come una libreria che genera
del codice personalizzato e non richiede servizi aggiuntivi.
Una panoramica dei tipi di dati, delle funzioni principali e dei meccanismi di
estensione forniti da Pig.
Alcuni esempi di applicazione di Pig allanalisi dettagliata del dataset di
Twitter, che dimostrano la sua capacit di esprimere concetti complessi in
una modalit concisa.
Le librerie quali Piggybank, Elephant Bird e DataFu che forniscono dei
repository per numerose funzioni utili e gi pronte di Pig.
Nel prossimo capitolo torneremo sul confronto con SQL esplorando alcuni
strumenti che espongono unastrazione di tipo SQL sui dati conservati in HDFS.
Capitolo 7
Hadoop e SQL
livello pi elevato (o pi familiari) dei dati conservati in HDFS, e Pig uno dei
pi diffusi. In questo capitolo esploreremo laltra astrazione pi comune
implementata su Hadoop: SQL. In particolare, vedremo quanto segue.
In quali casi usare SQL su Hadoop e perch cos popolare.
HiveQL, il dialetto SQL introdotto da Apache Hive.
Utilizzare HiveQL per eseguire analisi di tipo SQL del dataset di Twitter.
Come HiveQL ricalca le funzioni pi comuni dei database relazionali come le
viste e i join.
Il modo in cui HiveQL consente di incorporare funzioni definite dallutente
nelle sue query.
Come SQL su Hadoop completa Pig.
Altri prodotti SQL su Hadoop come Impala e in cosa sono diversi da Hive.
Perch SQL su Hadoop
Finora abbiamo visto come scrivere programmi Hadoop usando le API
MapReduce e come Pig Latin fornisce unastrazione di scripting e un wrapper per
la logica business personalizzata per mezzo delle UDF. Pig uno strumento molto
potente, ma il suo modello di programmazione a flusso di dati non familiare alla
maggior parte degli sviluppatori o analisti. Lo strumento tradizionale privilegiato
da questi professionisti per esplorare i dati SQL.
Nel 2008 Facebook ha rilasciato Hive, la prima implementazione di SQL su
Hadoop utilizzata su vasta scala. Invece di fornire un modo per sviluppare pi
rapidamente le attivit di map e reduce, Hive offre unimplementazione di HiveQL,
un linguaggio di interrogazione basato su SQL. Hive prende le istruzioni HiveQL e
traduce immediatamente e automaticamente le query in uno o pi job di
MapReduce. Esegue quindi il programma MapReduce nel suo complesso e
restituisce i risultati allutente. Linterfaccia per Hadoop non solo riduce il tempo
necessario a produrre i risultati dallanalisi dei dati, ma estende anche
significativamente la rete di chi pu usare Hadoop. Non occorrono competenze di
sviluppo software: chiunque ha familiarit con SQL pu usare Hive.
La combinazione di questi attributi fa s che HiveQL venga spesso impiegato
come strumento di analisi dei dati e aziendale per eseguire query ad hoc sui dati
conservati in HDFS. Con Hive, lanalista pu lavorare sul perfezionamento delle
query senza coinvolgere lo sviluppatore software. Come Pig, consente lestensione
di HiveQL per mezzo di UDF (User Defined Function), permettendo la
personalizzazione del dialetto SQL di base con funzionalit specifiche per il
business.
-- Users
users = foreach tweets {
generate
(chararray)CustomFormatToISO($0#created_at,
EEE MMMM d HH:mm:ss Z y) as dt,
(chararray)$0#id_str as id_str,
$0#user as user;
}
user_fields = foreach users {
generate
(chararray)CustomFormatToISO(user#created_at,
EEE MMMM d HH:mm:ss Z y) as dt,
(chararray)user#id_str as user_id,
(chararray)user#location as user_location,
(chararray)user#name as user_name,
(chararray)user#description as user_description,
(int)user#followers_count as followers_count,
(int)user#friends_count as friends_count,
(int)user#favourites_count as favourites_count,
(chararray)user#screen_name as screen_name,
(int)user#listed_count as listed_count;
}
unique_users = distinct user_fields;
store unique_users into $outputDir/users
using PigStorage(\u0001);
Questo codice scrive i dati in tre file TSV separati per il tweet, lutente e le
informazioni sulla posizione. Notate che nel comando store passiamo un argomento
quando chiamiamo PigStorage. Questo singolo argomento modifica il separatore
predefinito da un carattere di tabulazione al valore Unicode U0001; in alternativa
potete anche premere Ctrl+C+A. Questo il separatore tipico delle tabelle di
Hive e ci torner particolarmente utile perch i dati dei tweet potrebbero
contenere delle tabulazioni in altri campi.
Panoramica su Hive
Illustreremo ora come importare i dati in Hive e come eseguire una query
sullastrazione della tabella Hive che ci procurer i dati. In questo esempio e nel
resto del capitolo presupporremo che le query vengano digitate nella shell che pu
essere invocata tramite il comando hive.
Recentemente stato reso disponibile un client chiamato Beeline, che potrebbe
diventare il client CLI privilegiato nel prossimo futuro.
Quando si importano nuovi dati in Hive, si segue solitamente un processo in tre
passi.
1. Creiamo la specifica della tabella in cui devono essere importati i dati.
2. Importiamo i dati nella tabella creata.
3. Eseguiamo le query HiveQL sulla tabella.
La maggior parte delle istruzioni HiveQL ha un corrispettivo diretto nelle
istruzioni con nome simili di SQL standard. (Se vi serve un ripasso su SQL,
trovate numerose risorse online.)
Hive fornisce query con una vista strutturata dei dati, ma perch ci sia
possibile, dobbiamo prima definire la specifica delle colonne della tabella e
importare i dati nella tabella prima di poter eseguire qualsiasi query. Tale
specifica viene generata con unistruzione CREATE che indica il nome della tabella, il
nome e i tipi delle sue colonne e alcuni metadati riguardanti la sua
memorizzazione:
CREATE table tweets (
created_at string,
tweet_id string,
text string,
in_reply_to string,
retweeted boolean,
user_id string,
place_id string
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY \u0001
STORED AS TEXTFILE;
Listruzione crea una nuova tabella tweets definita da un elenco di nomi per le
colonne nel dataset e dal rispettivo tipo di dati. Specifichiamo che i dati sono
delimitati dal carattere Unicode U0001 e che il formato usato per salvare i dati
TEXTFILE.
Questo codice restituisce il numero totale di tweet presenti nel dataset. HiveQL,
come SQL, non case sensitive per quanto riguarda parole chiave e nomi di
colonne e tabelle. Per convenzione, le istruzioni SQL usano le maiuscole per le
parole chiave del linguaggio SQL, e lo stesso faremo noi quando utilizzeremo
HiveQL nei file, come vedremo tra poco. Tuttavia, quando digiteremo dei comandi
interattivi, prenderemo la via pi facile e ci atterremo alle minuscole.
Se considerate attentamente il tempo impiegato dai vari comandi nellesempio
precedente, noterete che caricare dei dati in una tabella dura quanto crearne la
specifica, mentre il semplice conteggio di tutte le righe occupa un tempo pi lungo.
Anche loutput mostra che la creazione della tabella e il caricamento dei dati non
comportano lesecuzione di job di MapReduce, e questo spiega i tempi di
esecuzione cos brevi.
Tipi di dati
HiveQL supporta molti dei tipi di dati comuni forniti dai sistemi di database
standard. Questi includono i tipi primitivi, come float, double, int e string, attraverso
tipi di collezioni strutturate che forniscono un corrispettivo SQL a tipi come arrays,
structs e unions (structs con opzioni per alcuni campi). Poich Hive implementato
Istruzioni DDL
HiveQL offre alcune istruzioni per creare, eliminare e modificare i database, le
tabelle e le viste. Listruzione CREATE DATABASE <name> crea un nuovo database con il
nome dato. Un database rappresenta un namespace dove sono contenuti i metadati
della tabella e della vista. Se sono presenti pi database, listruzione USE <database
name> specifica quale usare per interrogare le tabelle o per creare nuovi metadati.
Listruzione CREATE TABLE [IF NOT EXISTS] <name> crea una tabelle con il nome dato.
Come gi accennato, quello che viene effettivamente creato sono i metadati che
rappresentano la tabella e la relativa mappatura sui file in HDFS, oltre che una
directory in cui salvare i file dei dati. Se esiste gi una tabella o una vista con lo
stesso nome, Hive sollever uneccezione.
I nomi delle tabelle e delle colonne non sono case sensitive. Nelle versioni pi
vecchie di Hive (0.12 e precedenti), in questi nomi erano ammessi solo caratteri
alfanumerici e underscore. Da Hive 0.13, il sistema supporta i caratteri Unicode
nei nomi delle colonne. Le parole riservate, come load e create, devono prevedere
dei backtick (il carattere `) per poter essere trattate in modo letterale.
La parola chiave EXTERNAL specifica che la tabella si trova in risorse fuori dal
controllo di Hive, un meccanismo che pu essere utile per estrarre dati da unaltra
sorgente allinizio della pipeline ETL (Extract-Transform-Load) basata su
Hadoop. La clausola LOCATION specifica dove trovare il file (o la directory)
sorgente. La parola chiave EXTERNAL e la clausola LOCATION sono state utilizzate nel
codice seguente:
CREATE EXTERNAL TABLE tweets (
created_at string,
tweet_id string,
text string,
in_reply_to string,
retweeted boolean,
user_id string,
place_id string
) ROW FORMAT DELIMITED
FIELDS TERMINATED BY \u0001
STORED AS TEXTFILE
LOCATION ${input}/tweets;
Questa tabella verr creata nel metastore, ma i dati non verranno copiati nella
directory /user/hive/warehouse.
NOTA
Hive non contempla i concetti di chiave primaria e di identificatore univoco. Lunicit e la
normalizzazione dei dati sono aspetti da affrontare prima di caricare i dati nel data warehouse.
Listruzione CREATE VIEW <view name> AS SELECT crea una vista con il nome dato. Per
esempio, possiamo creare una vista per isolare i retweet dagli altri messaggi, cos:
CREATE VIEW retweets
COMMENT Tweets that have been retweeted
AS SELECT * FROM tweets WHERE retweeted = true;
A meno che non sia diversamente specificato, i nomi delle colonne si ottengono
tramite listruzione SELECT. Attualmente Hive non supporta le viste materializzate.
Le istruzioni DROP TABLE e DROP VIEW rimuovono i metadati e i dati da una tabella o
vista. Quando si elimina una tabella o una vista EXTERNAL, vengono cancellati solo i
metadati; i file dei dati effettivi non vengono toccati.
Hive consente la modifica dei metadati di una tabella attraverso listruzione
ALTER TABLE, che pu essere utilizzata per modificare il tipo di una colonna, il nome,
Hive utilizza le classi Serializer e Deserializer (SerDe), oltre che FileFormat per
leggere e scrivere le righe delle tabelle. Si usa una SerDe nativa se il ROW FORMAT non
specificato o se il ROW FORMAT DELIMITED specificato in unistruzione CREATE TABLE. La
clausola DELIMITED dice al sistema di leggere i file delimitati. Per lescape dei
caratteri di delimitazione si pu utilizzare la clausola ESCAPED BY.
Hive impiega attualmente le seguenti classi FileFormat per leggere e scrivere i
file in HDFS.
TextInputFormat e HiveIgnoreKeyTextOutputFormat: leggono/scrivono i dati nel formato
di solo testo.
e SequenceFileOutputFormat: leggono/scrivono i dati nel
SequenceFileInputFormat
JSON
Dalla versione 0.13, Hive dotato dellorg.apache.hive.hcatalog.data.JsonSerDe
nativo. Per le versioni precedenti, uno dei moduli di
serializzazione/deserializzazione JSON pi ricchi probabilmente Hive-JSON-
Serde (https://github.com/rcongiu/Hive-JSON-Serde).
Possiamo usare un modulo o laltro per caricare i tweet JSON senza la
necessit di una pre-elaborazione e definire semplicemente uno schema di Hive
che corrisponda al contenuto di un documento JSON. Nel prossimo esempio,
utilizzeremo Hive-JSON-SerDe. Come facciamo per qualsiasi modulo di terze
parti, carichiamo i suoi JAR con il codice seguente:
ADD JAR JAR json-serde-1.3-jar-with-dependencies.jar;
)
ROW FORMAT SERDE org.openx.data.jsonserde.JsonSerDe
STORED AS TEXTFILE
LOCATION tweets;
Avro
AvroSerde (https://cwiki.apache.org/confluence/display/Hive/AvroSerDe) permette di
leggere e scrivere i dati nel formato Avro. Dalla versione 0.14, si possono creare
tabelle Avro usando listruzione STORED AS AVRO, e Hive si occuper di creare lo
schema Avro opportuno per la tabella. Le versioni precedenti di Hive sono un po
pi prolisse.
Come esempio, carichiamo il dataset di PageRank che abbiamo generato nel
Capitolo 6. Il dataset era stato creato usando la classe AvroStorage di Pig, e ha
questo schema:
{
type:record,
name:record,
fields: [
{name:topic,type:[null,int]},
{name:source,type:[null,int]},
{name:rank,type:[null,float]}
]
}
Nel DDL, diciamo ad Hive che i dati sono salvati nel formato Avro usando
AvroContainerInputFormat e AvroContainerOutputFormat. Ogni riga deve essere serializzata e
avro.schema.url.
Se nel percorso della classe non sono presenti dipendenze Avro, dovremo
aggiungere il JAR Avro MapReduce al nostro ambiente prima di accedere ai singoli
campi. In Hive, sulla CDH5 VM:
ADD JAR /opt/cloudera/parcels/CDH/lib/avro/avro-mapred-hadoop2.jar;
Possiamo anche usare questa tabella come qualsiasi altra. Per esempio,
possiamo interrogare i dati per selezionare le coppie utente-topic che hanno un
PageRank elevato:
SELECT source, topic from tweets_pagerank WHERE rank >= 0.9;
Storage a colonne
Hive sfrutta anche lo storage in colonne attraverso i formati ORC
(https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC) e Parquet
(https://cwiki.apache.org/confluence/display/Hive/Parquet).
Se una tabella contiene molte colonne, non insolito che una query ne elabori
solo un piccolo sottoinsieme. Tuttavia, anche in SequenceFile ogni riga completa e
tutte le sue colonne verranno lette dal disco, decompresse ed elaborate.
Loperazione consuma parecchie risorse di sistema anche per quei dati che
sappiamo fin dallinizio non essere interessanti.
Anche i database relazionali tradizionali salvano i dati per riga, e un tipo di
database colonnare sposta il focus sulle colonne. Nel modello pi semplice,
invece di un file per tabella, ci sar un file per ciascuna colonna della tabella. Se
una query deve accedere solo a cinque colonne in una tabella che ne contiene 100,
allora verranno letti solo i file di quelle cinque colonne. Sia ORC sia Parquet
usano questo principio e altre ottimizzazioni per velocizzare le interrogazioni.
Query
Le tabelle possono essere interrogate usando la consueta istruzione SELECT FROM.
Listruzione WHERE permette di specificare le condizioni di filtro, GROUP BY aggrega i
record, ORDER BY specifica i criteri di ordinamento e LIMIT determina il numero di
record da recuperare. Ai record raggruppati possono essere applicate funzioni
aggregate, come count e sum. Per esempio, il prossimo codice restituisce i primi 10
utenti pi prolifici nel dataset:
SELECT user_id, COUNT(*) AS cnt FROM tweets GROUP BY user_id ORDER BY cnt DESC LIMIT 10
Eccoli:
2263949659 4
1332188053 4
959468857 3
1367752118 3
362562944 3
58646041 3
2375296688 3
1468188529 3
37114209 3
2385040940 3
Questa riga dir ad hive, ma non a beeline, di visualizzare i nomi delle colonne
come parte delloutput.
SUGGERIM ENT O
Potete aggiungere il comando al file .hiverc che si trova solitamente nella root della directory
home dellutente per applicarlo a tutte le sessioni della CLI di hive.
AT T ENZIONE
Hive supporta solo i (semi) join di equality, outer e left.
Anche se qui usiamo una sola colonna di partizione, possiamo suddividere una
tabella in base a pi chiavi di colonna; sufficiente disporle in una lista con le
voci separate da virgole nella clausola PARTITIONED BY.
Notate che le colonne della chiave di partizione devono essere incluse come
ultime colonne in qualsiasi istruzione si sta utilizzando per linserimento in una
tabella partizionata. Nel codice precedente usiamo la funzione to_date di Hive per
convertire il timestamp created_at in una stringa formattata come YYYY-MM-DD.
I dati partizionati sono salvati in HDFS come
/path/to/warehouse/<database>/<table>/key=<value>. Nel nostro esempio, la struttura della
Per aggiungere i metadati per tutte le partizioni attualmente non presenti nel
metastore possiamo utilizzare listruzione MSCK REPAIR TABLE <table_name>;. Su EMR,
equivale a eseguire la seguente istruzione:
ALTER TABLE <table_name> RECOVER PARTITIONS;
Se si omette OVERWRITE, ogni istruzione INSERT aggiunger dei dati alla tabella. A
volte pu andar bene, ma spesso i dati sorgente da immettere in una tabella di Hive
hanno lo scopo di aggiornare completamente un subset dei dati lasciando inalterato
il resto.
Se eseguiamo unistruzione INSERT OVERWRITE (o LOAD OVERWRITE) in una partizione,
solo questa sar interessata dallintervento. Per esempio, se dovessimo inserire
dei dati utente volendo intervenire solo sulle partizioni che contengono dati nel file
sorgente, dovremmo aggiungere la parola chiave OVERWRITE allistruzione INSERT
precedente.
Possiamo anche aggiungere delle condizioni allistruzione SELECT. Per esempio,
immaginiamo di voler aggiornare i dati per un certo mese:
INSERT INTO TABLE partitioned_user
PARTITION (created_at_date)
SELECT created_at ,
user_id,
location,
name,
description,
followers_count,
friends_count,
favourites_count,
screen_name,
listed_count,
to_date(created_at) as created_at_date
FROM user
WHERE to_date(created_at) BETWEEN 2014-03-01 and 2014-03-31;
Bucketing e ordinamento
Il partizionamento un costrutto che potete sfruttare utilizzando la colonna (o le
colonne) di partizione nella clausola WHERE delle interrogazioni sulle tabelle. Esiste
un altro meccanismo chiamato bucketing che pu segmentare ulteriormente il
modo in cui una tabella memorizzata, e lo fa in un modo che permette allo stesso
Hive di ottimizzare i propri piani di interrogazione per trarre vantaggio dalla
struttura.
Creiamo versioni a bucket dei nostri tweet e delle tabelle utente; osservate le
istruzioni supplementari CLUSTER BY e SORT BY nellistruzione CREATE TABLE:
CREATE table bucketed_tweets (
tweet_id string,
text string,
in_reply_to string,
retweeted boolean,
user_id string,
place_id string
) PARTITIONED BY (created_at string)
CLUSTERED BY(user_ID) into 64 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY \u0001
STORED AS TEXTFILE;
Notate che abbiamo modificato la tabella tweets in modo che venga partizionata;
il bucketing pu essere applicato solo a una tabella gi segmentata.
Cos come dobbiamo specificare una colonna di partizione quando aggiungiamo
dati a una tabella partizionata, dovremo anche assicurarci che i dati inseriti in una
tabella a bucket siano raggruppati correttamente. Per farlo, impostiamo il flag
seguente prima di immettere i dati nella tabella:
SET hive.enforce.bucketing=true;
Con il join eseguito sulla colonna usata per il bucketing, Hive pu ottimizzare
lelaborazione, poich sa che ogni bucket contiene lo stesso insieme di colonne
user_id in entrambe le tabelle. Quando si deve decidere su quali righe eseguire
In questa query, Hive effettuer lhash delle righe della tabella in 64 bucket in
base al nome della colonna; utilizzer quindi solo il secondo bucket per la query.
possibile specificare pi bucket, e se RAND() fornito come clausola ON, lintera riga
viene usata dalla funzione di bucketing.
Anche se funziona, non un sistema particolarmente efficiente, poich si dovr
analizzare lintera tabella per generare il sottoinsieme di dati. Se campioniamo una
tabella in bucket e ci assicuriamo che il numero di bucket campionati uguale o
un multiplo dei bucket nella tabella, allora Hive legger solo i bucket in questione.
Per esempio:
SELECT MAX(friends_count)
FROM bucketed_user TABLESAMPLE(BUCKET 2 OUT OF 32 on user_id);
Nella query precedente sulla tabella bucketed_user, che creata con 64 bucket
sulla colonna user_id, il campionamento legger solo i bucket richiesti, poich usa
la stessa colonna. In questo caso, si tratter dei bucket 2 e 34 di ciascuna
partizione.
Unultima forma di campionamento quella dei blocchi. In questo caso,
possiamo specificare la parte di tabella da campionare, e Hive ne utilizzer
unapprossimazione leggendo solo quei blocchi di dati sorgente in HDFS che sono
sufficienti per assecondare tale dimensione.
La dimensione dei dati pu essere indicata come percentuale della tabella, come
assoluta o come numero di righe in ciascun blocco. La sintassi seguente di
TABLESAMPLE campiona rispettivamente lo 0,5 percento della tabella, 1 GB di dati o
Hive e S3
Come detto nel Capitolo 2, si pu specificare un file system predefinito diverso
da HDFS per Hadoop, e S3 una possibilit. Al suo interno si possono salvare
tabelle specifiche, i cui dati verranno raccolti in un cluster da elaborare; i dati
risultanti possono essere scritti in una posizione diversa in S3 (la stessa tabella
non pu essere la sorgente e la destinazione di una singola query) oppure in HDFS.
Possiamo prendere un file dei nostri dati dei tweet e collocarlo in una posizione
in S3 con un comando come il seguente:
$ aws s3 put tweets.tsv s3://<bucket-name>/tweets/
Come prima cosa dobbiamo specificare la chiave daccesso e quella segreta per
entrare nel bucket. Si pu procedere in tre modi:
impostando fs.s3n.awsAccessKeyId e fs.s3n.awsSecretAccessKey con i valori opportuni
nella CLI di Hive;
impostando gli stessi valori in hive-site.xml, tenendo per presente che questo
limita luso di S3 a un unico set di credenziali;
specificando esplicitamente la posizione della tabella nellURL della stessa,
cio s3n://<access key>:<secret access key>@<bucket>/<path>.
Dopodich possiamo creare una tabella facendo riferimento a questi dati:
CREATE table remote_tweets (
created_at string,
tweet_id string,
text string,
in_reply_to string,
retweeted boolean,
user_id string,
place_id string
) CLUSTERED BY(user_ID) into 64 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY \t
LOCATION s3n://<bucket-name>/tweets
Pu essere un modo eccezionalmente efficace di raccogliere i dati di S3 in un
cluster locale di Hadoop per lelaborazione.
NOTA
Per poter utilizzare le credenziali AWS nellURI di una posizione in S3 a prescindere da come
vengono passati i parametri, le due chiavi di accesso non devono contenere i caratteri /, +, = o
\. Se necessario, si pu generare un nuovo set di credenziali dalla console IAM allindirizzo
https://console.aws.amazon.com/iam/.
In teoria, potreste lasciare i dati nella tabella esterna e farvi riferimento quando
necessario per evitare le latenze e i costi di trasferimento dei dati sulla WAN,
anche se spesso ha pi senso raccogliere i dati in una tabella locale e partire da
qui per lelaborazione. Se la tabella partizionata, potreste per esempio ritrovarvi
a dover recuperare una partizione nuova ogni giorno.
restituiscono tipi scrivibili di base. UnAPI pi ricca che fornisce il supporto per i
tipi di dati diversi da quelli scrivibili si trova nel package
org.apache.hadoop.hive.ql.udf.generic.GenericUDF. Vedremo ora come si pu utilizzare la
prima API per implementare una stringa in una funzione ID simile a quella usata
nel Capitolo 5 per mappare gli hashtag in integer in Pig. Per creare una UDF con
questa API sufficiente estendere la classe UDF e scrivere un metodo evaluate():
public class StringToInt extends UDF {
public Integer evaluate(Text input) {
if (input == null)
return null;
Prima di poter utilizzare una UDF, la si deve registrare in Hive con i seguenti
comandi:
ADD JAR myudfs-hive.jar;
CREATE TEMPORARY FUNCTION string_to_int AS com.learninghadoop2.hive.udf.StringToInt;
implementa una classe Java. La funzione verr eliminata alla chiusura della
sessione di Hive. Dalla versione 0.13, possibile creare funzioni permanenti la
cui definizione conservata nel metastore usando CREATE FUNCTION .
Una volta registrata, StringToInt pu essere impiegata in una query come
qualsiasi altra funzione. Nel prossimo esempio estrarremo prima un elenco di
hashtag dal testo dei tweet applicando regexp_extract, dopodich utilizzeremo
string_to_int per mappare ogni tag su un ID numerico:
Come abbiamo fatto nel Capitolo 6, possiamo usare la query precedente per
creare una tabella di lookup:
CREATE TABLE lookuptable (tag string, tag_id bigint);
INSERT OVERWRITE TABLE lookuptable
SELECT unique_hashtags.hashtag,
string_to_int(unique_hashtags.hashtag) as tag_id
FROM
(
SELECT regexp_extract(text,
(?:\\s|\\A|^)[##]+([A-Za-z0-9-_]+)) AS hashtag
FROM tweets
GROUP BY regexp_extract(text,
(?:\\s|\\A|^)[##]+([A-Za-z0-9-_]+))
) unique_hashtags
GROUP BY unique_hashtags.hashtag, string_to_int(unique_hashtags.hashtag);
Interfacce programmatiche
Oltre che con gli strumenti a riga di comando hive e beeline, possibile inviare
query HiveQL al sistema attraverso le interfacce programmatiche JDBC e Thrift. Il
supporto per ODBC era incluso nelle versioni pi vecchie di Hive, ma dalla
versione 0.12 devessere costruito da zero. Trovate ulteriori informazioni sulla
procedura alla pagina https://cwiki.apache.org/confluence/display/Hive/HiveODBC.
JDBC
Un client Hive scritto usando le API JDBC assomiglia in tutto e per tutto a un
programma client scritto per altri sistemi di database (per esempio MySQL).
Quello che segue un esempio. Trovate il codice sorgente alla pagina
http://bit.ly/1AFnlqu:
// stringa di connessione
public static String URL = jdbc:hive2://localhost:10000;
La sezione URL lURI JDBC che descrive il punto finale della connessione. La
sintassi per stabilire una connessione remota jdbc:hive2:<host>:<port>/<database>. Le
connessioni in modalit incorporata possono essere stabilite senza specificare un
host o una porta, come jdbc:hive2://.
hive e hive2 sono i driver da usare quando ci si connette ad HiveServer e HiveServer2.
Poi, come con qualsiasi altro programma JDBC, stabiliamo una connessione
allURL e usiamola per istanziare una classe Statement. Eseguiamo QUERY, senza
autenticarci, e salviamo il dataset di output nelloggetto ResultSet. Infine, scorriamo
il resultSet e visualizziamone il contenuto nella riga di comando.
Compiliamo ed eseguiamo lesempio con i comandi seguenti:
$ javac HiveJdbcClient.java
$ java -cp $(hadoop
classpath):/opt/cloudera/parcels/CDH/lib/hive/lib/*:/opt/cloudera/parcels/CDH/lib/hive/lib/hive-
jdbc.jar: com.learninghadoop2.hive.client.HiveJdbcClient
Thrift
Thrift fornisce un accesso di basso livello ad Hive e offre alcuni vantaggi
rispetto allimplementazione JDBC di HiveServer. Principalmente, consente pi
connessioni dallo stesso client e luso agevole di linguaggi di programmazione
diversi da Java. Con HiveServer2, non lopzione pi frequente, ma vale la pena
citarlo per quanto riguarda la compatibilit. Alla pagina http://bit.ly/1O6nZ9V trovate
un esempio di client di Thrift implementato usando lAPI Java. Questo client pu
essere utilizzato per connettersi ad HiveServer, ma a causa di differenze di
protocollo, il client non funzioner con HiveServer2.
Nellesempio definiamo un metodo getClient() che prende come input lhost e la
porta di un servizio HiveServer e restituisce unistanza di
org.apache.hadoop.hive.service.ThriftHive.Client.
Chiamiamo getClient() dal metodo main e usiamo il client per eseguire una query
su unistanza di HiveServer in esecuzione sul localhost sulla porta 11111:
public static void main(String[] args) throws Exception {
Client client = getClient(localhost, 11111);
client.execute(show tables);
List<String> results = client.fetchAll();
for (String result : results) {
System.out.println(result);
}
}
Assicuratevi che giri proprio su questa porta; se cos non fosse, avviate
unistanza con questo comando:
$ sudo hive --service hiveserver -p 11111
Questa impostazione presuppone che Tez sia installato sul cluster; disponibile
nella forma sorgente da http://tez.apache.org e in molte altre distribuzioni (ma non in
Cloudera al momento della stesura di queste righe).
Il valore alternativo mr, che usa il classico modello di MapReduce (su YARN);
quindi possibile, in ununica installazione, fare un confronto con le prestazioni di
Hive usando Tez.
Impala
Hive non lunico prodotto che fornisce la capacit di SQL su Hadoop. Il
secondo pi utilizzato probabilmente Impala, annunciato a fine 2012 e rilasciato
nella primavera del 2013. Per quanto sviluppato inizialmente in Cloudera, il suo
codice sorgente viene caricato periodicamente su un repository Git open source
(https://github.com/cloudera/impala).
Impala nato partendo dalla stessa percezione della debolezza di Hive che ha
portato alliniziativa Stinger. Inoltre trae parte della sua ispirazione da Google
Dremel
(http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36632.pdf),
descritto pubblicamente per la prima volta in un documento del 2009. Dremel fu
costruito in Google per colmare il divario tra la necessit di query molto veloci su
dataset molto grandi e la latenza elevata insita nel modello di MapReduce allora
esistente alla base di Hive. Dremel costituiva un approccio sofisticato al
problema: invece di attenuare linconveniente su MapReduce cos come
implementato da Hive, creava un nuovo servizio che accedeva agli stessi dati
conservati in HDFS. Dremel beneficiava anche del grande lavoro svolto per
ottimizzare il formato di storage dei dati allo scopo di velocizzare le query
analitiche.
Larchitettura di Impala
Larchitettura di base di Impala costituita tre componenti principali: i daemon
di Impala, lo store dello stato e i client. Versioni recenti hanno aggiunto altri
componenti che migliorano il servizio, ma noi ci concentreremo sullarchitettura di
alto livello.
Il daemon di Impala (impalad) dovrebbe essere eseguito su ogni host sul quale un
processo di DataNode gestisce i dati di HDFS. Notate che impalad non accede ai
blocchi del file system attraverso lAPI FileSystem di HDFS, ma utilizza una
funzione di letture a cortocircuito che rendono laccesso pi efficiente.
Quando un client invia una query, pu farlo su qualsiasi processo impalad in
esecuzione, che diventer il coordinatore delloperazione. Laspetto chiave delle
prestazioni di Impala che per ogni query genera del codice nativo personalizzato,
che viene poi inserito ed eseguito da tutti i processi impalad sul sistema. Il codice
cos ottimizzato esegue la query sui dati locali, e ogni impalad restituisce un
sottoinsieme dei risultati al nodo coordinatore, che effettua laccorpamento finale
dei dati per produrre il risultato definitivo. Questo tipo di architettura dovrebbe
essere familiare a chiunque ha lavorato con una delle soluzioni di data warehouse
MPP (Massively Parallel Processing) oggi disponibili, solitamente commerciali e
costose. Con il cluster in esecuzione, il daemon dello store dello stato garantisce
che ciascun processo impalad sia consapevole di tutti gli altri e fornisce una vista
della salute complessiva del cluster.
Strumenti di supporto
Niente panico! Esistono alcune categorie di strumenti che possono darci una
mano. In questo capitolo illustreremo alcuni esempi di tre categorie.
Servizi di orchestrazione: la creazione di una pipeline di importazione
costituita in genere da alcune fasi distinte; ci serviremo di uno strumento di
orchestrazione per consentirne la descrizione, lesecuzione e la gestione.
Connettori: data limportanza dellintegrazione con i sistemi esterni, vedremo
come utilizzare i connettori per semplificare le astrazioni fornite dallo
storage di Hadoop.
Formati di file: il modo in cui salviamo i dati ha un impatto sul modo in cui
gestiamo levoluzione del formato nel tempo; in questo veniamo aiutati da
alcuni formati di supporto ricchi.
Costruire la capacit per lanalisi dei
tweet
Nei capitoli precedenti abbiamo utilizzato diverse implementazioni dellanalisi
dei dati di Twitter per descrivere vari concetti. Scenderemo ora pi in profondit
seguendo un ipotetico case study. Creeremo una pipeline di importazione dei dati,
costruendo un workflow (un flusso di lavoro) pronto per la produzione tenendo
conto dellaffidabilit e dellevoluzione futura.
Procederemo in modo progressivo lungo tutto il capitolo. In ogni fase,
evidenzieremo cosa cambiato, ma per ragioni di spazio non potremo includere
tutti i listati per intero; trovate comunque tutte le iterazioni nel codice sorgente.
Oozie
Volendo potremmo usare qualcosa come cron per una programmazione semplice
dei job, ma ricordate che dobbiamo alimentare una pipeline costruita tenendo
presente laffidabilit. Ci serve allora uno strumento che ci permetta anche di
individuare gli errori e di reagire di fonte a situazioni eccezionali. Lo strumento
che useremo qui Oozie (http://oozie.apache.org), un motore di workflow e uno
scheduler concepito con un focus sullecosistema di Hadoop.
Oozie fornisce un mezzo per definire un workflow come una serie di nodi con
parametri configurabili e una transizione controllata da un nodo allaltro.
installato come parte della Cloudera QuickStart VM; il client principale a riga di
comando si chiama oozie, appunto.
NOTA
Abbiamo testato i workflow di questo capitolo sulla versione 5.0 della Cloudera QuickStart VM,
e al momento della stesura di queste righe Oozie nella versione 5.1 (la pi recente) mostrava
qualche inconveniente. Nei nostri workflow non c tuttavia nulla di specifico per una data
versione, quindi dovrebbero essere compatibili con qualsiasi implementazione di Oozie v4
correttamente funzionante.
Oozie usa XML per descrivere i suoi workflow, salvati solitamente in un file
chiamato workflow.xml. Percorriamo la definizione di un workflow di Oozie che
chiama un comando di shell.
Lo schema per un workflow di Oozie detto workflow-app, e possiamo dare al
flusso un nome specifico. Pu essere utile quando si visualizza la cronologia di un
job nella CLI o in uninterfaccia web di Oozie. Negli esempi di questo libro
utilizzeremo un numero di versione progressivo che ci permetter di distinguere
pi facilmente le varie iterazioni nel repository sorgente. Ecco come assegnare un
nome al workflow-app:
<workflow-app xmlns=uri:oozie:workflow:0.4 name=v1>
I workflow di Oozie sono costituiti da una serie di nodi connessi, ognuno dei
quali indica un passo nel processo e che sono rappresentati da nodi XML nella
definizione del flusso. In Oozie ci sono alcuni nodi che si occupano della
transizione del workflow da un passo allaltro. Il primo di essi il nodo di inizio
(start), che dichiara il nome del primo nodo da eseguire nel flusso:
<start to=fs-node/>
Abbiamo poi la definizione del nodo start con nome. In questo caso un nodo
action, che il tipo generico della maggior parte dei nodi di Oozie che svolgono
I vari passi del workflow di Oozie vengono eseguiti come job di MapReduce.
Questa azione della shell verr quindi eseguita come istanza di unattivit
specifica su un dato TaskTracker. Dovremo perci indicare quali file devono
essere copiati sulla directory di lavoro locale sulla macchina del TaskTracker
prima che lazione venga eseguita. Nel nostro caso copieremo lo script della shell
principale, il generatore di tweet di Python e il file di configurazione di Twitter:
<file>${workflowRoot}/${EXEC}</file>
<file>${workflowRoot}/twitter.keys</file>
<file>${workflowRoot}/stream.py</file>
Una volta chiuso lelemento della shell, specifichiamo nuovamente cosa fare a
seconda che lazione sia stata completata con successo o meno. Poich
MapReduce usato per lesecuzione dei job, la maggioranza dei tipi di nodi ha
per definizione una logica interna di riprova e ripristina, anche se questo non
vale per i nodi shell:
</shell>
<ok to=end/>
<error to=fail/>
</action>
Se il flusso fallisce, va eliminato. Il tipo di nodo kill fa proprio questo:
impedisce che il workflow passi alla fase successiva, registrando man mano dei
messaggi derrore. Ecco luso tipico di questo nodo:
<kill name=fail>
<message>Shell action failed, error
message[${wf:errorMessage(wf:lastErrorNode())}]</message>
</kill>
Inoltre, tutti i job correnti e quelli recenti possono essere visualizzati con
$ oozie jobs
Facilitare lo sviluppo
A volte pu essere complicato gestire i file e le risorse di un job di Oozie
durante lo sviluppo. Parte di questi elementi deve trovarsi in HDFS mentre altri
devono essere a livello locale, e modificare alcuni file significa doverne
modificare altri. Lapproccio pi semplice consiste nello sviluppare o
nellapportare le modifiche in un clone della directory del workflow sul file
system locale, passandole poi da qui alla directory con nome simile in HDFS,
ovviamente senza dimenticare di sottoporle tutte a un controllo di revisione. In
termini di esecuzione operativa del workflow il file job.properties lunico
elemento che deve essere nel file system locale, mentre tutti gli altri file devono
trovarsi in HDFS. Ricordate che facilissimo modificare la copia locale del
workflow, dimenticarsi di riportare i cambiamenti in HDFS e poi ritrovarsi con un
flusso che non rispecchia le modifiche.
Visto che vogliamo passare da questa fase al nodo di Hive, dovremo impostare i
seguenti elementi nel modo opportuno:
<ok to=hive-node/>
<error to=fail/>
</action>
Lazione di Hive leggermente diversa dai nodi precedenti; inizia nella stessa
maniera, ma poi specifica il suo namespace, come segue:
<action name=hive-node>
<hive xmlns=uri:oozie:hive-action:0.2>
<job-tracker>${jobTracker}</job-tracker>
<name-node>${nameNode}</name-node>
Dobbiamo anche fornire il meccanismo per passare gli argomenti allo script di
Hive. Tuttavia, invece di costruire la riga di comando un componente alla volta,
aggiungeremo alcuni elementi param che mapperanno il nome di un elemento di
configurazione nel file job.properties sulle variabili indicate nello script; questo
meccanismo supportato anche con le azioni di Pig:
<param>dbName=${dbName}</param>
<param>ingestDir=${ingestDir}</param>
</hive>
Questo workflow esegue tutti i passi descritti; genera i dati dei tweet, estrae dei
sottoinsiemi di dati attraverso Pig e poi li porta in Hive.
workflowRoot=${nameNode}/user/${user.name}/${tasksRoot}/v2
oozie.wf.application.path=${nameNode}/user/${user.name}/${tasksRoot}/v2
oozie.use.system.libpath=true
EXEC=gettweets.sh
inputDir=/tmp/tweets
outputDir=/tmp/tweetdata
ingestDir=/tmp/tweetdata
dbName=twttr
Introduzione a HCatalog
Se analizziamo bene il nostro workflow, possiamo rilevare uninefficienza nel
modo in cui usa DFS come interfaccia tra Pig e Hive. Dobbiamo produrre in output
il risultato dello script di Pig in HDFS, dove lo script di Hive potr poi utilizzarlo
come posizione delle nuove tabelle. Emerge cos che spesso molto utile che i
dati siano salvati in Hive, ma che questa anche una limitazione, perch sono
pochi gli strumenti che possono accedere al suo metastore e da qui leggere e
scrivere i dati. Se ci pensiamo, Hive ha due livelli principali: i suoi strumenti per
accedere ai dati e manipolarli e il framework per eseguire delle query su di essi.
Il sottoprogetto HCatalog di Hive fornisce unimplementazione indipendente del
primo di questi livelli, cio il mezzo per accedere e manipolare i dati nel
metastore di Hive. HCatalog offre dei meccanismi perch altri strumenti, come Pig
e MapReduce, leggano e scrivano nativamente dati strutturati in tabelle che sono
salvate in HDFS.
Ricordate per che i dati in HDFS sono salvati nei formati pi diversi. Il
metastore di Hive fornisce i modelli per astrarre questi file da Hive nella struttura
della tabella relazionale che ci familiare. Quando salviamo i dati in HCatalog,
quindi, in realt li salviamo in HDFS in modo tale che possano essere esposti
dalle strutture tabellari specificate nel metastore di Hive. Per contro, quando
facciamo riferimento ai dati di Hive, parliamo di dati i cui metadati si trovano nel
metastore di Hive, e che sono accessibili attraverso uno strumento che riconosce i
metastore, come HCatalog.
Utilizzare HCatalog
Lo strumento a riga di comando HCatalog viene chiamato hcat ed preinstallato
sulla Cloudera QuickStart VM, oltre che con qualsiasi versione di Hive successiva
alla 0.11 compresa.
Lutility hcat non prevede una modalit interattiva, quindi in genere si utilizza
con argomenti espliciti a riga di comando o puntandola a un file di comandi, come
segue:
$ hcat e use default; show tables
$ hcat f commands.hql
Sebbene HCatalog sia utile e possa essere incorporato negli script, quello per
cui ci interessa di pi in questo contesto la sua integrazione con Pig. HCatalog
definisce un nuovo loader Pig chiamato HCatLoader e uno storer chiamato HCatStorer.
Come si pu intuire dai loro nomi, questi due strumenti permettono che gli script di
Pig vengano letti e scritti direttamente in tabelle di Hive. Possiamo sfruttare questo
meccanismo per sostituire le azioni di Pig e Hive viste in precedenza nel
workflow di Oozie con ununica azione di Pig basata su HCatalog che scrive
loutput del job di Pig direttamente nelle tabelle di Hive.
Per chiarezza, creeremo tre nuove tabelle chiamate tweets_hcat, places_hcat e
users_hcat in cui inseriremo questi dati (notate che non sono pi tabelle esterne):
La CLI di HCat tuttavia, non offre una shell interattiva simile alla CLI di Hive.
Ora possiamo usare il nostro script di Pig precedente, e ci baster modificare i
comandi di storage, sostituendo luso di PigStorage con HCatStorer. Lo script
aggiornato, extract_to_hcat.pig, includer quindi comandi store come questo:
store tweets_tsv into twttr.tweets_hcat using org.apache.hive.hcatalog.pig.HCatStorer();
La sharelib di Oozie
Lultima aggiunta apre le porte a un aspetto importante di Oozie di cui finora non
abbiamo parlato: la sua sharelib. Quando Oozie esegue i vari tipi di azione,
necessita di diversi JAR per poter accedere ad Hadoop e invocare vari strumenti,
tra cui Hive e Pig. Come parte dellinstallazione di Oozie, in HDFS si trova un
gran numero di JAR dipendenti che verranno utilizzati da Oozie e dai vari tipi di
azione, e che costituiscono la sharelib.
Per la maggior parte dei casi sufficiente sapere che la sharelib esiste,
solitamente nella directory /user/oozie/share/lib in HDFS, e conoscere quando
devono essere aggiunti dei valori di configurazione espliciti (come nellesempio
precedente). Quando si usa unazione di Pig, verranno prelevati automaticamente i
JAR di Pig, ma quando lo script di Pig usa qualcosa come HCatalog, la
dipendenza non risulter nota a Oozie.
La CLI di Oozie consente la manipolazione della sharelib (ma questo va oltre
lambito del libro). Il comando che segue, per esempio, pu rivelarsi utile per
vedere quali sono i componenti inclusi nella sharelib di Oozie:
$ oozie admin -shareliblist
Il prossimo comando serve invece per vedere i singoli JAR che comprendono
un determinato componente nella sharelib, in questo caso HCatalog:
$ oozie admin -shareliblist hcat
Questi comandi tornano comodi per verificare che i JAR richiesti siano stati
inclusi e per vedere quali versioni specifiche sono utilizzate.
Modifichiamo quindi la nostra azione di Pig nel file workflow.xml per includere
questaltro parametro:
<script>${workflowRoot}/pig/extract_to_hcat.pig</script>
<param>inputDir=${inputDir}</param>
<param>partitionKey=${partitionKey}</param>
AT T ENZIONE
Una conseguenza di questo comportamento che la nuova esecuzione di un workflow di HCat
con gli stessi argomenti fallir. Tenetene conto quando testate i workflow o sperimentate con il
codice di esempio del libro.
Produrre dati derivati
Ora che la pipeline principale definita, ci sono alcune azioni che necessario
intraprendere dopo aver aggiunto ogni nuovo dataset. Nel meccanismo precedente
in cui abbiamo aggiunto ciascun set di dati degli utenti in una partizione separata,
la tabella users_hcat contiene gli utenti pi volte. Creiamo allora una tabella per gli
utenti univoci e rigeneriamola ogni volta che inseriamo nuovi dati.
In questa tabella riuniremo solo gli attributi di un utente che non cambia mai
(ID) o che lo fa raramente (il nome dello schermo e cos via). Possiamo scrivere
una semplice istruzione Hive per popolare la tabella partendo dalla tabella
completa users_hcat:
USE twttr;
INSERT OVERWRITE TABLE unique_users
SELECT DISTINCT user_id, name, description, screen_name
FROM users_hcat;
Ogni singolo nodo di azione specificato non diverso da quelli che abbiamo gi
usato. Un nodo action porta a una serie di altri nodi; lunico requisito che ogni
serie parallela di azioni termini con una transizione al nodo join associato al nodo
fork, come segue:
<action name=setup-filesystem-node>
<ok to=setup-join-node/>
<error to=fail/>
</action>
<action name=create-tables-node>
<ok to=setup-join-node/>
<error to=fail/>
</action>
Grazie a questa sezione, non dovremo pi specificare i valori nei nodi di Hive e
Pig nel resto del workflow (notate che in questo momento il nodo shell non
supporta il meccanismo della configurazione globale). Questo permette di
semplificare notevolmente alcuni dei nostri nodi. Per esempio, il nodo di Pig si
presenta cos:
<action name=hcat-ingest-node>
<pig>
<configuration>
<property>
<name>oozie.action.sharelib.for.pig</name>
<value>pig,hcatalog</value>
</property>
</configuration>
<script>${workflowRoot}/pig/extract_to_hcat.pig</script>
<param>inputDir=${inputDir}</param>
<param>dbName=${dbName}</param>
<param>partitionKey=${partitionKey}</param>
</pig>
<ok to=derived-data-node/>
<error to=fail/>
</action>
Possiamo aggiungere altri elementi di configurazione o effettuare loverriding di
quelli specificati nella sezione global, ottenendo una definizione molto pi chiara
che si concentra solo sulle informazioni specifiche per lazione in questione. Nel
v7 del workflow sono stati inseriti sia la sezione global sia un workflow
secondario, e la leggibilit ne guadagna.
Le sfide dei dati esterni
Quando ci affidiamo a dati esterni per la nostra applicazione, dipendiamo
implicitamente dalla loro qualit e stabilit. Ovviamente questo vale sempre, ma
nel caso di dati generati da fonti terze su cui non abbiamo alcun controllo, i rischi
sono pi elevati.
Dobbiamo quindi pensare a come limitarli quando costruiamo applicazioni che
dovrebbero essere affidabili basandoci su quei dati, e soprattutto quando il volume
di questi cresce.
Azione di validazione
La logica per effettuare la validazione o il cleanup necessari pu essere
incorporata direttamente in altre azioni. A un nodo shell che esegue uno script di
raccolta dati si possono aggiungere dei comandi specifici per gestire in modo
diverso i record mal formattati. Le azioni di Pig e Hive che caricano i dati nelle
tabelle possono applicare un filtro durante limportazione (riesce meglio in Pig) o
aggiungere delle precisazioni quando si copiano i dati da una tabella di
importazione allo store operativo.
Esiste anche un argomento per aggiungere un nodo di validazione nel workflow,
anche se inizialmente non esegue una logica vera e propria. Potrebbe essere per
esempio unazione di Pig che legge i dati, applica la validazione e scrive i dati
validati in una nuova posizione dove possa essere letta dai nodi successivi. Il
vantaggio che in seguito si potr aggiornare la logica di validazione senza
modificare le altre azioni, riducendo cos il rischio di interrompere
accidentalmente la pipeline e rendendo i nodi meglio definiti per quanto riguarda
le responsabilit. Ne consegue che anche un workflow secondario di validazione
un modello, poich non solo fornisce una separazione tra le responsabilit, ma
rende la logica di validazione pi facile da testare e aggiornare.
Lovvio svantaggio di questo approccio che comporta unulteriore
elaborazione e un altro ciclo di lettura dei dati e di loro riscrittura, il che contrasta
con uno dei vantaggi che abbiamo evidenziato quando abbiamo parlato delluso di
HCatalog da Pig.
una questione di compromesso fra prestazioni e complessit e manutenzione
del workflow. Quando prendete in considerazione come effettuare la validazione e
cosa questa significa per il vostro workflow, valutate tutti questi elementi prima di
scegliere unimplementazione invece di unaltra.
Possiamo utilizzare questa tabella come qualsiasi altra, per esempio per copiare
nella tabella di Avro i dati provenienti dalla partizione 3 della tabella non di Avro:
SET hive.exec.dynamic.partition.mode=nonstrict
INSERT INTO TABLE tweets_avro
PARTITION (partition_key)
SELECT FROM tweets_hcat
NOTA
Come negli esempi precedenti, se nel percorso della classe non sono presenti delle
dipendenze Avro, dovremo aggiungere il JAR di MapReduce di Avro al nostro ambiente prima di
poter compiere qualsiasi selezione dalla tabella.
Abbiamo cos una nuova tabella di tweet specificata da uno schema di Avro, che
ora come ora assomiglia a una tabella qualsiasi. Quello che ci interessa qui, per,
come sfruttare il meccanismo di Avro per gestire levoluzione dello schema.
Aggiungiamo un nuovo campo:
{
namespace: com.learninghadoop2.avrotables,
type:record,
name:tweets_avro,
fields:[
{name: created_at, type: [null ,string]},
{name: tweet_id_str, type: [null,string]},
{name: text,type:[null,string]},
{name: in_reply_to, type: [null,string]},
{name: is_retweeted, type: [null,string]},
{name: user_id, type: [null,string]},
{name: place_id, type: [null,string]},
{name: new_feature, type: string, default: wow!}
]
}
Senza aggiungere dati, possiamo eseguire su questo nuovo campo delle query
che restituiranno il valore predefinito per i dati esistenti:
SELECT new_feature FROM tweets_avro LIMIT 5;
...
OK
wow!
wow!
wow!
wow!
wow!
Programmare i workflow
Finora abbiamo eseguito tutti i nostri workflow di Oozie su richiesta dalla CLI.
Oozie dotato di uno scheduler che consente lavvio dei job su una base
temporale oppure quando vengono soddisfatti alcuni criteri esterni come la
comparsa dei dati in HDFS. Una buona soluzione per i nostri workflow potrebbe
essere eseguire la pipeline dei tweet ogni 10 minuti, per esempio, e aggiornare i
dati di riferimento una sola volta al giorno.
AT T ENZIONE
A prescindere da dove vengono recuperati i dati, pensate con attenzione a come gestire i
dataset che eseguono unoperazione di eliminazione/sostituzione. In particolare, non eliminate
prima di recuperare e validare i nuovi dati; se lo fate, i job che richiedono i dati di riferimento
falliranno fino al prossimo recupero che avr successo. Pu essere una buona idea includere
delle operazioni distruttive in un workflow secondario che viene avviato solo a completamento
avvenuto della procedura di recupero.
come segue:
oozie.coord.application.path=${nameNode}/user/${user.name}/${tasksRoot}/tweets_10min
# minuscole e scomposizione
line = line.lower().split()
count = 1
current = None
if word == current:
count += 1
else:
if current:
print %s\t%s % (current.decode(utf-8), count)
current = word
count = 1
if current == word:
print %s\t%s % (current.decode(utf-8), count)
NOTA
In entrambi i casi utilizziamo implicitamente i formati di input e output di Hadoop presentati nei
capitoli precedenti. TextInputFormat elabora il file sorgente e fornisce una riga alla volta allo
script di map. Per contro, TextOutputFormat garantisce che loutput delle attivit di reduce sia
scritto correttamente come un testo.
AT T ENZIONE
I tweet sono codificati in UTF-8. Accertatevi che PYTHONIOENCODING sia impostato di
conseguenza per poter convogliare i dati in un terminale UNIX:
$ export PYTHONIOENCODING=UTF-8
Hadoop raccoglie comunque dei valori per ogni chiave e garantisce che siano
passati esclusivamente a un singolo reducer. In altre parole, un reducer ottiene tutti
i valori per un certo numero di chiavi e li raggruppa; tuttavia, a differenza di
quanto accade con lAPI Java, i valori non sono assemblati in singole esecuzioni
del reducer, uno per chiave. Poich Hadoop Streaming utilizza i canali di stdin e
stdout per lo scambio dei dati tra le attivit, i messaggi di debug e di errore non
# minuscole e scomposizione
text = text.lower().split()
else:
print(
u%s\t%s\t%s % (
cur_term.decode(utf-8), cur_doc_id.decode(utf-8), freq))
cur_doc_id = doc_id
cur_term = term
freq = 1
print(
u%s\t%s\t%s % (
cur_term.decode(utf-8), cur_doc_id.decode(utf-8), freq))
NOTA
Specifichiamo quali campi appartengono alla chiave (per lo shuffling) nelle opzioni del
comparatore.
line = sys.stdin.readline().strip()
cur_term, cur_doc_id, cur_tf = line.split(\t)
cur_tf = int(cur_tf)
cur_df = 1
try:
term, doc_id, tf = line.strip().split(\t)
tf = int(tf)
except:
logger.warn(Invalid record: %s % line)
continue
key_cache.append(
u%s\t%s\t%s % (term.decode(utf-8), doc_id.decode(utf-8), tf))
else:
for key in key_cache:
print(%s\t%s % (key, cur_df))
print (
u%s\t%s\t%s\t%s % (
cur_term.decode(utf-8),
cur_doc_id.decode(utf-8),
cur_tf, cur_df)
)
try:
term, doc_id, tf, df = line.split(\t)
tf = float(tf)
df = float(df)
num_doc = float(num_doc)
except:
logger.warn(Invalid record %s % line)
# idf = num_doc / df
tf_idf = tf * (1+math.log(num_doc / df))
print(%s\t%s\t%s % (term, doc_id, tf_idf))
Il numero dei documenti nella collezione viene passato come parametro a tf-
idf.py:
/usr/bin/hadoop jar /opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/
hadoop-streaming.jar \
-D mapreduce.reduce.tasks=0 \
-input /tmp/df-out.tsv/part-00000 \
-output /tmp/tf-idf.out \
-file tf-idf.py \
-mapper python tf-idf.py 15578
Per calcolare il numero totale di tweet, possiamo usare le utility Unix cat e wc in
combinazione con Hadoop Streaming:
/usr/bin/hadoop jar /opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-streaming.jar \
-input tweets.json \
-output tweets.cnt \
-mapper /bin/cat \
-reducer /usr/bin/wc
Data Core
Come suggerisce il nome, il core alla base di tutte le funzionalit fornite nel
modulo Data. Le sue astrazioni principali sono i dataset e i repository.
Linterfaccia org.kitesdk.data.Dataset viene utilizzata per rappresentare un set di
dati non mutevole:
@Immutable
public interface Dataset<E> extends RefinableView<E> {
String getName();
DatasetDescriptor getDescriptor();
Dataset<E> getPartition(PartitionKey key, boolean autoCreate);
void dropPartition(PartitionKey key);
Iterable<Dataset<E>> getPartitions();
URI getUri();
}
E next();
void remove();
void close();
boolean isOpen();
}
Come i reader, i writer sono oggetti utilizzabili una volta sola. Serializzano
istanze delle entit di tipo E e le scrivono sul sistema di storage sottostante. In
genere i writer non sono istanziati direttamente, ma pu essere creata
unimplementazione adatta attraverso il metodo factory newWriter().
Implementazioni di DatasetWriter tratterranno le risorse finch non viene chiamato
close(), e attendono che il chiamante invochi close() in un blocco finally quando il
Data HCatalog
Data HCatalog un modulo che abilita laccesso ai repository di HCatalog. Le
sue astrazioni principali sono org.kitesdk.data.hcatalog.HCatalogAbstractDatasetRepository
e limplementazione concreta, org.kitesdk.data.hcatalog.HCatalogDatasetRepository.
Descrivono un DatasetRepository che utilizza HCatalog per gestire i metadati e HDFS
per lo storage:
public class HCatalogDatasetRepository extends HCatalogAbstractDatasetRepository {
HCatalogDatasetRepository(Configuration conf) {
super(conf, new HCatalogManagedMetadataProvider(conf));
}
HCatalogDatasetRepository(Configuration conf, MetadataProvider provider) {
super(conf, provider);
}
public <E> Dataset<E> create(String name, DatasetDescriptor descriptor) {
getMetadataProvider().create(name, descriptor);
return load(name);
}
public boolean delete(String name) {
return getMetadataProvider().delete(name);
}
public static class Builder {
}
}
NOTA
Come nel caso di Kite 0.17, Data HCatalog stato deprecato a favore del nuovo modulo Data
Hive.
La posizione della directory dei dati viene scelta da Hive/HCatalog (le cosiddette
tabelle gestite) o specificata quando si crea unistanza di questa classe fornendo
un file system e una directory root nel costruttore (tabelle esterne).
Data Hive
Il modulo Data Hive espone gli schemi di Hive attraverso linterfaccia Dataset.
Come nel caso di Kite 0.17, questo package sostituisce Data HCatalog.
Data MapReduce
Il package org.kitesdk.data.mapreduce fornisce interfacce per leggere e scrivere i
dati in e da un dataset con MapReduce.
Data Spark
Il package org.kitesdk.data.spark fornisce interfacce per leggere e scrivere i dati in
e da un dataset con Apache Spark.
Data Crunch
Il package org.kitesdk.data.crunch.CrunchDatasets una classe helper che espone
dataset e viste come classi ReadableSource o Target di Crunch:
public class CrunchDatasets {
public static <E> ReadableSource<E> asSource(View<E> view, Class<E> type) {
return new DatasetSourceTarget<E>(view, type);
}
public static <E> ReadableSource<E> asSource(URI uri, Class<E> type) {
return new DatasetSourceTarget<E>(uri, type);
}
public static <E> ReadableSource<E> asSource(String uri, Class<E> type) {
return asSource(URI.create(uri), type);
}
hoc come PigLatin. Inoltre, offre un sistema di tipi altamente personalizzabile che
ci d la possibilit di lavorare (e di mescolare) con le interfacce Writable di
Hadoop, con HBase e con gli oggetti serializzati di Avro.
FlumeJava presume che MapReduce sia il livello di astrazione sbagliato per
molte classi di problemi, in cui i calcoli sono spesso costituiti da pi lavori
concatenati. Spesso, per ragioni di prestazioni, necessario combinare operazioni
indipendenti dal punto di vista logico (per esempio filtro, proiezione, aggregazione
e altre trasformazioni) in un unico job fisico di MapReduce. Questo aspetto
influisce anche sulla testabilit del codice. Non potendo affrontare questo
argomento nel capitolo, vi rimandiamo alla documentazione di Crunch.
Per iniziare
I JAR di Crunch sono gi installati sulla QuickStart VM; di default, si trovano in
/opt/cloudera/parcels/CDH/lib/crunch. In alternativa, possibile scaricare
alcune librerie di Crunch recenti allindirizzo https://crunch.apache.org/download.html,
dai repository specifici di Maven Central o Cloudera.
Concetti
Le pipeline di Crunch vengono create unendo due astrazioni: PCollection e PTable.
LinterfacciaPCollection<T> una collezione distribuita e non mutevole di oggetti di
tipo T. Linterfaccia PTable<Key, Value> unhashtable (una sotto-interfaccia di
PCollection) distribuita e non mutevole di chiavi del tipo Key e di valori del tipo
Value che espone metodi per lavorare con le coppie chiave-valore.
groupByKey: ordina e raggruppa gli elementi di una PTable in base alle loro
chiavi.
combineValues: aggrega i valori da una groupByKey operation.
pipeline.enableDebug();
pipeline.writeTextFile(counts, args[1]);
// Esegue la pipeline come un job di MapReduce.
pipeline.done();
Aggregazione e ordinamento
Molti dei pattern di elaborazione dei dati forniti dallorg.apache.crunch.lib si
basano sul metodo groupByKey di PTable. Il metodo si presenta in tre forme diverse.
groupByKey(): consente al planner di determinare il numero delle partizioni.
groupByKey(int numPartitions): viene usato per impostare il numero delle
leftJoin;
rightJoin.
I metodi hanno un tipo di ritorno e una segnatura comuni. Come riferimento,
descriveremo il metodo join usato di frequente che implementa un inner join:
public static <K,U,V> PTable<K,Pair<U,V>> join(PTable<K,U> left,
PTable<K,V> right)
su Apache Spark.
SparkPipeline
Con SparkPipeline, Crunch delega gran parte dellesecuzione a Spark e si
occupa relativamente poco delle attivit di pianificazione, con le seguenti
eccezioni:
input multipli;
output multipli;
serializzazione dei dati;
operazioni di checkpoint.
Al momento della stesura di queste righe, SparkPipeline ancora oggetto di un
pesante sviluppo e potrebbe non gestire tutti i casi duso di una MRPipeline
standard. La community di Crunch sta lavorando attivamente per garantire una
compatibilit completa tra le due implementazioni.
MemPipeline
MemPipeline viene eseguita in memoria su un client. A differenza di
MRPipeline, non viene creata esplicitamente ma viene referenziata chiamando il
metodo statico MemPipeline.getInstance(). Tutte le operazioni avvengono in memoria, e
luso di PType ridotto al minimo.
Esempi di Crunch
Utilizzeremo ora Apache Crunch per reimplementare in una maniera pi
modulare parte del codice di MapReduce scritto finora.
Co-occorrenza di parole
Nel Capitolo 3 abbiamo mostrato un job di MapReduce, BiGramCount, che
contava le co-occorrenze delle parole nei tweet. La stessa logica pu essere
implementata come una DoFn. Invece di emettere una chiave multicampo e doverla
sottoporre a parsing in una fase successiva, con Crunch possiamo usare un tipo
complesso Pair<String, String>, come segue:
class BiGram extends DoFn<String, Pair<String, String>> {
@Override
public void process(String tweet,
Emitter<Pair<String, String>> emitter) {
String[] words = tweet.split( ) ;
TF-IDF
Possiamo implementare la catena di job della TF-IDF con una MRPipeline, come
segue:
public class CrunchTermFrequencyInvertedDocumentFrequency
extends Configured implements Tool, Serializable {
@SuppressWarnings(deprecation)
public TF() {}
}
}
return 1;
}
// Crea un oggetto per coordinare la creazione e lesecuzione della pipeline.
Pipeline pipeline =
new MRPipeline(TermFrequencyInvertedDocumentFrequency.class, getConf());
// Calcola la DF
PTable<String, Long> df = Aggregate.count(tf.parallelDo( new DocumentFrequencyString(),
Avros.strings()));
Lapproccio che seguiamo qui presenta una serie di vantaggi rispetto allo
streaming. Prima di tutto, non occorre concatenare manualmente i job di
MapReduce usando uno script separato. Questa attivit lobiettivo principale di
Crunch. In secondo luogo, possiamo esprimere ciascun componente della metrica
come una classe distinta, facilitandone il riuso nelle applicazioni future.
Per implementare la frequenza dei termini, creiamo una classe DoFn che prende
come input un tweet ed emette Pair<String, TF>. Il primo elemento un termine,
mentre il secondo unistanza della classe POJO che verr serializzata tramite Avro.
La parte TF contiene tre variabili: term, documentId e frequency. Nellimplementazione
del riferimento, ci aspettiamo che i dati di input siano una stringa JSON che
deserializzeremo e sottoporremo a parsing. Includiamo anche la scomposizione
come sotto-attivit del metodo di elaborazione.
A seconda dei casi duso potremmo astrarre entrambe le operazioni in DoFn
separate, cos:
class TermFrequencyAvro extends DoFn<String,Pair<String, TF>> {
public void process(String JSONTweet,
Emitter<Pair <String, TF>> emitter) {
Map<String, Integer> termCount = new HashMap<>();
String tweet;
String docId;
try {
Object obj = parser.parse(JSONTweet);
termine, cio il primo elemento della coppia. Per ottenere la frequenza del
documento aggreghiamo e contiamo la PCollection di termini risultante:
class DocumentFrequencyString extends DoFn<Pair<String, TF>, String> {
@Override
public void process(Pair<String, TF> tfAvro,
Emitter<String> emitter) {
emitter.emit(tfAvro.first());
}
}
TF tf = tfDf.first();
double idf = 1.0+Math.log(numDocs / df);
double tfIdf = idf * tf.frequency;
Qui usiamo MapFn perch la nostra intenzione produrre in output un record per
ciascun input. Trovate il codice sorgente di questo esempio alla pagina
http://bit.ly/1Hx2Twe.
Kite Morphlines
Kite Morphlines una libreria di trasformazione dei dati che si ispira ai pipe di
Unix, sviluppata originariamente come parte di Cloudera Search. Una morphline
una catena di comandi di trasformazione in memoria che si affida a una struttura di
plug-in per sfruttare fonti di dati eterogenee. Utilizza dei comandi dichiarativi per
effettuare operazioni ETL sui record. I comandi vengono definiti in un file di
configurazione che viene passato successivamente a una classe driver.
Lobiettivo quello di semplificare lattivit di incorporamento della logica
ETL in una base di codice Java attraverso una libreria che consente agli
sviluppatori di sostituire la programmazione con una serie di impostazioni di
configurazione.
Concetti
Le morphline sono costruite attorno a due astrazioni: Command e Record. I record
sono implementazioni dellinterfaccia org.kitesdk.morphline.api.Record:
public final class Record {
private ArrayListMultimap<String, Object> fields;
morphlines : [{
id : read_tweets
importCommands : [org.kitesdk.morphline.**]
commands : [{
readJson {
outputClass : com.fasterxml.jackson.databind.JsonNode
}}
{
head {
limit : 10
}}
]
}]
host. Il primo passo da compiere nel metodo main consiste nel caricare la
configurazione JSON della morphline, costruire un oggetto MorphlineContext e
compilarlo in unistanza di Command che agisce da nodo di inizio della morphline.
Notate che Compiler.compile() prende un parametro finalChild, in questo caso
RecordEmitter. Lo utilizzeremo perch agisca da sink per la morphline, visualizzando
@Override
public Command getParent() {
return null;
}
@Override
public void notify(Record record) {
@Override
public boolean process(Record record) {
line.set(record.get(_attachment_body).toString());
System.out.println(line);
return true;
}
}
public static void main(String[] args) throws IOException {
/* carica un file di configurazione della morphline e lo imposta */
File morphlineFile = new File(args[0]);
String morphlineId = args[1];
MorphlineContext morphlineContext = new MorphlineContext.Builder().build();
Command morphline = new Compiler().compile(morphlineFile, morphlineId,
morphlineContext, new RecordEmitter());
Notifications.notifyBeginTransaction(morphline);
try {
Notifications.notifyStartSession(morphline);
boolean success = morphline.process(record);
if (!success) {
System.out.println(Morphline failed to process record: + record);
}
/* Conferma la morphline */
} catch (RuntimeException e) {
Notifications.notifyRollbackTransaction(morphline);
morphlineContext.getExceptionHandler().handleException(e, null);
}
finally {
in.close();
}
/* esce */
Notifications.notifyShutdown(morphline);
}
}
In questo esempio, carichiamo i dati nel formato JSON dal file system locale in
un oggetto InputStream e lo impieghiamo per inizializzare una nuova istanza di Record.
La classe RecordEmitter contiene lultima istanza elaborata del record nella catena,
sulla quale estraiamo l_attachment_body visualizzandolo nelloutput standard.
Trovate il codice sorgente di MorphlineDriver alla pagina http://bit.ly/1DTXp0y.
Utilizzare la stessa morphline da un job di MapReduce un gioco da ragazzi.
Durante la fase di setup del mapper, costruiamo un contesto che contiene la logica
di istanziazione, mentre il metodo map imposta loggetto Record e invia la logica di
elaborazione, come segue:
public static class ReadTweets
extends Mapper<Object, Text, Text, NullWritable> {
private final Record record = new Record();
private Command morphline;
@Override
protected void setup(Context context)
throws IOException, InterruptedException {
File morphlineConf = new File(context.getConfiguration()
.get(MORPHLINE_CONF));
String morphlineId = context.getConfiguration()
.get(MORPHLINE_ID);
MorphlineContext morphlineContext =
new MorphlineContext.Builder()
.build();
record.removeAll(Fields.ATTACHMENT_BODY);
}
}
@Override
public void notify(Record notification) {
}
@Override
public Command getParent() {
return null;
}
@Override
public boolean process(Record record) {
line.set(record.get(Fields.ATTACHMENT_BODY).toString());
try {
context.write(line, null);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}
Layout fisico
Se utilizzate un cluster fisico, dovrete considerare alcuni aspetti che su EMR
sono alla portata di tutti.
Conoscere i rack
I rack sono la prima cosa da conoscere nel caso di cluster abbastanza grandi da
usare pi di un rack nello spazio del data center. Come spiegato nel Capitolo 2,
quando HDFS colloca delle repliche dei nuovi file, cerca di porre la seconda
replica su un host che non sia il primo, e la terza replica in un rack diverso nel
caso di un sistema a pi rack. Lo scopo massimizzare la resilienza; anche se
fallisce lintero rack, rester almeno una replica disponibile. MapReduce utilizza
una logica analoga per cercare di bilanciare e distribuire al meglio le attivit.
Se non intervenite in alcun modo, ogni host verr specificato nellunico rack di
default. Se per il cluster cresce oltre questa soglia, dovrete aggiornare il nome
del rack.
Dietro le quinte, Hadoop determina il rack di un nodo eseguendo uno script
fornito dallutente che mappa lhostname del nodo sui nomi dei rack. Cloudera
Manager consente che questi nomi siano impostati su un dato host, recuperandoli
quando gli script specifici vengono chiamati da Hadoop. Per impostare il rack per
un host, fate clic su Hosts > <nomehost> > Assign Rack e assegnate il rack dalla
home page di Cloudera Manager.
Aggiornare i servizi
Da sempre, laggiornamento di Hadoop unoperazione che porta via molto
tempo e in qualche modo rischiosa. Questo vale tuttora nel caso di cluster
distribuiti manualmente, ossia che non sono gestiti da uno strumento come
Cloudera Manager.
Cloudera Manager si prende in carico la parte dellattivit legata ai tempi, ma
non necessariamente il rischio. Qualsiasi aggiornamento dovrebbe sempre essere
considerato come unazione dagli esiti spesso imprevisti, tra cui un certo tempo di
inattivit. Niente pu sostituire un aggiornamento di prova eseguito su un cluster di
test; solo cos si pu capire perch importante trattare Hadoop come un
componente che ha un ciclo di vita di distribuzione pari a quello di qualsiasi altro
componente.
A volte un aggiornamento richiede la modifica dei metadati di HDFS, e
potrebbe influenzare il file system. proprio qui che sta il rischio. Oltre a
effettuare un aggiornamento di prova, sfruttate la possibilit di impostare HDFS in
modalit di upgrade, che crea una snapshot dello stato del file system precedente
allaggiornamento che verr conservata finch laggiornamento non viene
applicato. Pu rivelarsi molto utile, perch anche un aggiornamento che va storto e
corrompe i dati pu essere potenzialmente annullato.
Costruire un cluster su EMR
Elastic MapReduce una soluzione flessibile che, a seconda dei requisiti e dei
carichi di lavoro, pu risiedere su un cluster fisico di Hadoop o sostituirlo. Come
visto finora, EMR fornisce alcuni cluster precaricati e configurati con Hive,
Streaming e Pig, oltre che cluster di JAR personalizzati che consentono
lesecuzione di applicazioni di MapReduce.
Una seconda distinzione da fare quella tra cicli di vita transitori e a
esecuzione prolungata. Un cluster EMR transitorio viene generato su richiesta; i
dati sono caricati in S3 o HDFS, vengono elaborati alcuni workflow, i risultati di
output vengono salvati e il cluster viene chiuso automaticamente. Un cluster a
esecuzione prolungata viene mantenuto attivo anche dopo che il flusso di lavoro
terminato, e resta disponibile per copiarvi nuovi dati o per lesecuzione di nuovi
workflow. Sono i cluster pi adatti per i data warehouse o per lavorare con
dataset abbastanza grandi la cui elaborazione e il cui caricamento sarebbero
inefficaci se paragonati a un cluster transitorio.
In un documento imprescindibile per i potenziali utenti
(https://media.amazonwebservices.com/AWS_Amazon_EMR_Best_Practices.pdf), Amazon fornisce
uneuristica per determinare quale tipo di cluster pi adatto:
Se il numero di job per giorno * (tempo per impostare il cluster compreso il tempo di caricamento dei dati
di Amazon S3 usando Amazon S3 + tempo di elaborazione dei dati) < 24 ore, valutate ladozione di
cluster Amazon EMR transitori o di istanze fisiche. I cluster a esecuzione prolungata vengono istanziati
passando largomento alive al comando ElasticMapreduce, che abilita lopzione Keep Alive e
disabilita la terminazione automatica.
Notate che i due tipi di cluster condividono le stesse propriet e gli stessi limiti;
in particolare, i dati su HDFS non sono pi disponibili una volta che il cluster
viene chiuso.
NOTA
EMR non supporta lo storage dei blocchi S3. LURI di s3 mappato su s3n.
Monitoraggio integrato
improbabile che costruiate da soli i vari strumenti di monitoraggio, mentre
pi probabile che ne integriate di gi esistenti, insieme ad altri framework. Nel
caso di alcuni strumenti open source, come Nagios e Zabbix, ci sono diversi
template di esempio che possono arricchire le metriche dei servizi e dei nodi di
Hadoop.
Tutto questo contribuisce alla separazione suggerita in precedenza; il fallimento
del ResourceManager di YARN potrebbe essere un evento ad altra criticit che
genera degli avvertimenti da inviare allo staff delle operations, mentre un carico
elevato su alcuni host specifici dovrebbe essere rilevato senza comportare linvio
degli avvisi. Abbiamo dunque avvisi attivati quando qualcosa va storto e
lindividuazione e linvio delle informazioni necessarie per investigare nei dati
del sistema nel tempo per effettuare unanalisi delle tendenze.
Cloudera Manager fornisce uninterfaccia REST, che si integra anchessa con
strumenti come Nagios, e che incorpora delle metriche dei servizi definite da
Cloudera Manager invece di doverne definire di proprie.
Nel caso di infrastrutture aziendali costruite su framework come IBM Tivoli o
HP OpenView per le quali il monitoraggio pi impegnativo ed esteso, Cloudera
Manager pu inviare gli eventi attraverso dei trap (cio degli avvisi) SNMP che
verranno raccolti da questi sistemi.
A seguire, nei metodi map, reduce, setup e cleanup delle vostre implementazioni di
map o reduce, potrete incrementare un contatore di uno, cos:
Context.getCounter(AppMetrics.BAD_RECORDS).increment(1);
Livelli di log
Di default, Hadoop registra i messaggi su Log4j. Log4j configurato attraverso
log4j.properties nel percorso delle classi. Il file definisce cosa viene registrato e in
che forma:
log4j.rootLogger=${root.logger}
root.logger=INFO,console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n
<property>:
Notate che questa impostazione influir su tutti i log prodotti dalla classe
ResourceManager, compresi quelli generati dal sistema e dalle applicazioni che girano
su YARN.
Accedere ai logfile
La posizione dei logfile e le convenzioni di denominazione in genere
differiscono a seconda della distribuzione. Apache Ambari e Cloudera Manager
accentrano laccesso ai logfile, sia per i servizi sia per le singole applicazioni.
Sulla Cloudera QuickStart VM, trovate una panoramica dei processi attualmente in
esecuzione e dei link ai rispettivi logfile, i canali stderr e stdout, allindirizzo
http://localhost.localdomain:7180/cmf/hardware/hosts/1/processes (Figura 10.4).
Applicazioni
Nel riquadro di sinistra troviamo un elenco dei vari stati possibili
dellapplicazione: NEW, SUBMITTED, ACCEPTED, RUNNING, FINISHING,
FINISHED, FAILED e KILLED. A seconda dello stato, sono disponibili le
seguenti informazioni:
lID dellapplicazione;
lutente;
il nome dellapplicazione;
la coda dello scheduler in cui viene posta lapplicazione;
gli orari e lo stato di inizio/fine;
il link allinterfaccia utente di Tracking per una cronologia dellapplicazione.
Figura 10.6 Il Resource Manager.
Vista Nodes
La vista Nodes un frontend del menu del servizio del NodeManager, che
mostra lo stato di salute e la posizione delle applicazioni in esecuzione sul nodo
(Figura 10.7).
Ogni singolo nodo del cluster mostra ulteriori informazioni e statistiche a livello
di host attraverso la sua interfaccia utente. Si tratta di dati quali la versione di
Hadoop in esecuzione sul nodo, quanta memoria disponibile su di esso, lo stato
del nodo e un elenco delle applicazioni in esecuzione e dei contenitori (Figura
10.8).
Finestra Scheduler
La Figura 10.9 mostra la finestra Scheduler.
MapReduce
Per quanto le stesse informazioni e i dettagli dei log siano disponibili sia in
MapReduce v1 sia in MapReduce v2, la modalit di accesso leggermente
diversa.
MapReduce v1
La Figura 10.10 mostra linterfaccia utente del JobTracker di MapReduce.
disponibile di default allindirizzo http://<jobtracker>:50070, e mostra informazioni
su tutto quanto attualmente in esecuzione, oltre che sui job di MapReduce ritirati,
insieme a un riepilogo delle risorse e dello stato di salute del cluster e
informazioni sui tempi programmazione e la percentuale di completamento (Figura
10.11).
Sono disponibili i dettagli per ogni job in esecuzione e ritirato, compresi lID, il
proprietario, la priorit, lassegnazione delle attivit e lavvio dellattivit per il
mapper. Facendo clic su un link jobid si aprir una pagina di riepilogo (lo stesso
URL esposto dal comando mapred job list), che visualizza i dettagli sulle attivit di
map e reduce e alcune statistiche generali del contatore ai livello di job, file system e
Facendo clic sui link nella tabella Job si apre unulteriore pagina dettagliata
sullattivit e i suoi tentativi (Figura 10.13).
Da qui, possiamo accedere ai log di ogni tentativo, sia per le attivit riuscite sia
per quelle fallite o soppresse per ogni singolo host del TaskTracker. Questo log
contiene le informazioni pi granulari sullo stato del job di MapReduce, compreso
loutput degli appender di Log4j oltre che delloutput convogliato sui canali stdout
e stderr e il syslog (Figura 10.14).
Figura 10.14 I log del TaskTracker.
MapReduce v2 (YARN)
Come abbiamo visto nel Capitolo 3, con YARN, MapReduce solo uno dei
numerosi framework di elaborazione che possono essere distribuiti. Ricorderete
dai capitoli precedenti che i servizi del JobTracker e del TaskTracker sono stati
sostituiti rispettivamente dal Resource Manager e dal NodeManager.
In quanto tali, le interfacce utente del servizio e dei logfile di YARN sono pi
generiche di quelle di MapReduce v1. Il nome application_1405630696162_0002
mostrato nel ResourceManager corrisponde a un job di MapReduce con ID
job_1405630696162_0002. LID dellapplicazione appartiene allattivit in
esecuzione nel contenitore, e facendo clic su di esso si pu ottenere una
panoramica sul job di MapReduce e scendere di livello tra le singole attivit nelle
varie fasi finch non si raggiunge il log dellattivit specifica (Figura 10.15).
Figura 10.15 Unapplicazione YARN che contiene un job di MapReduce.
JobHistory
YARN offre un servizio REST JobHistory che visualizza i dettagli sulle
applicazioni completate. Attualmente supporta solo MapReduce e fornisce
informazioni sui job portati a termine. Tra queste abbiamo lo stato finale del job
SUCCESSFUL o FAILED , chi ha inviato il job, il numero totale delle attivit di
map e reduce e informazioni temporali.
NameNode e DataNode
Linterfaccia web per lHadoop Distributed File System (HDFS) mostra le
informazioni sul NameNode e sul file system in generale. Di default, si trova
allindirizzo http://<namenodehost>:50070/ (Figura 10.17).
Figura 10.17 Linterfaccia utente del NameNode.
Nei capitoli precedenti abbiamo esaminato molte parti di Hadoop 2 e del suo
ecosistema. Tuttavia, per ragioni di spazio, non abbiamo potuto approfondire certi
argomenti, ad altri abbiamo solo accennato e di alcuni non abbiamo parlato affatto.
Lecosistema di Hadoop, con le varie distribuzioni progetti Apache e non
Apache , un contesto incredibilmente vivace e attivo. In questo capitolo
intendiamo completare il materiale precedentemente affrontato nel dettaglio
proponendo una sorta di guida verso altre destinazioni stimolanti. In particolare,
tratteremo quanto segue.
Distribuzioni di Hadoop.
Altri progetti interessanti, Apache e non Apache.
Risorse di informazione e di aiuto.
Tenete conto che, ovviamente, qualsiasi panoramica sullecosistema risente dei
nostri interessi e delle nostre preferenze, e che quando leggerete queste righe
probabilmente sar gi datata.
In altre parole, non pensate neanche per un momento che quanto presentato qui
sia tutto quello che disponibile: solo uno stuzzichino!
Distribuzioni alternative
In genere, in questo libro abbiamo utilizzato la distribuzione Cloudera per
Hadoop, ma abbiamo cercato di mantenere la trattazione il pi indipendente
possibile dalla distribuzione. Abbiamo anche parlato dellHortonworks Data
Platform (HDP), ma queste non sono le uniche opzioni di distribuzione
disponibili.
Prima di dare unocchiata in giro, valutate se vi serve una distribuzione.
Potreste accedere al sito web di Apache, scaricare i tarball dei progetti che vi
interessano e lavorare per assemblare il tutto. Tuttavia, considerate le dipendenze
della versione, probabile che loperazione impegni pi tempo del previsto.
Inoltre, al prodotto finale mancher probabilmente una rifinitura in termini di
strumenti o script per il rilascio e la gestione operativa. Questi sono i motivi per
cui la maggior parte degli utenti preferisce utilizzare una distribuzione di Hadoop
esistente.
Una nota sulle estensioni gratuite e commerciali: essendo questo un progetto
open source con una licenza piuttosto aperta, i creatori della distribuzione sono
anche liberi di migliorare Hadoop con alcune estensioni proprietarie che vengono
rese disponibili gratuitamente come open source o come prodotti commerciali.
Potrebbe crearsi una situazione conflittuale, perch alcuni difensori dellopen
source non gradiscono la commercializzazione dei loro progetti di successo; ai
loro occhi come se lentit commerciale godesse dei frutti del lavoro della
community open source senza impegnarsi in prima persona. Per altri invece un
segno di salute della licenza Apache flessibile; il prodotto rimarr sempre gratuito,
e i singoli e le aziende possono decidere se procedere o meno con le estensioni
commerciali. Non diamo un giudizio, ma tenete presente che una questione che vi
troverete ad affrontare quasi sicuramente.
Dovete quindi valutare se vi occorre una distribuzione e se s per quali ragioni:
che vantaggi vi darebbe rispetto al farne a meno? Sceglierete un prodotto del tutto
open source o siete disposti a pagare per le estensioni commerciali? Con queste
domande in mente, diamo unocchiata ad alcune delle distribuzioni principali.
Distribuzione di Cloudera per Hadoop
La distribuzione di Cloudera (http://www.cloudera.com) dovrebbe esservi familiare,
visto che labbiamo utilizzata in tutto il libro. CDH stata la prima distribuzione
alternativa ampiamente disponibile, e la sua portata in termini di software, elevato
livello di qualit e gratuit lha resa una scelta molto popolare.
Recentemente, Cloudera sta estendendo i prodotti che inserisce nelle sue
distribuzioni oltre i progetti core di Hadoop. Accanto a Cloudera Manager e
Impala (entrambi sviluppati da Cloudera), ha aggiunto altri strumenti come
Cloudera Search (basato su Apache Solr) e Cloudera Navigator (una soluzione di
amministrazione dei dati). Mentre le versioni CDH precedenti alla 5 erano
maggiormente concentrate sui vantaggi dellintegrazione di una distribuzione, la
versione 5 (e presumibilmente anche quelle successive) sta inserendo sempre pi
capacit nella base dei progetti Hadoop Apache.
Cloudera offre inoltre un supporto commerciale per i suoi prodotti, insieme a
servizi di formazione e consulenza. Trovate i dettagli sulla pagina web della
compagnia.
MapR
Un tipo differente di distribuzione offerto da MapR Technologies (la
compagnia e la distribuzione sono solitamente indicate come MapR). La
distribuzione disponibile allindirizzo http://www.mapr.com basata su Hadoop, ma
prevede alcune modifiche e miglioramenti.
MapR si concentra sulle prestazioni e la disponibilit. Per esempio, stata la
prima distribuzione a offrire una soluzione HA per il NameNode e il JobTracker di
Hadoop, che, come ricorderete dal Capitolo 2, era un punto debole significativo in
Hadoop 1. Ha anche offerto unintegrazione nativa con i file system NFS molto
prima di Hadoop 2, facilitando di parecchio lelaborazione dei dati esistenti. Per
raggiungere questa funzionalit, MapR ha sostituito HDFS con un file system
completamente compatibile con POSIX che non prevede NameNode, producendo
cos un vero sistema distribuito senza nodo master, e un uso migliore
dellhardware rispetto allHDFS di Apache.
MapR fornisce unedizione community ed enterprise della sua distribuzione. Nel
prodotto gratuito non sono disponibili tutte le estensioni. La compagnia offre anche
servizi di supporto come parte delliscrizione alla versione enterprise del
prodotto, oltre a servizi di formazione e consulenza.
E il resto
Le distribuzioni di Apache Hadoop non sono solo territorio delle giovani start-
up, n rappresentano un mercato statico. Intel aveva una propria distribuzione fino
ai primi mesi del 2014, quando ha deciso di inglobare le modifiche in CDH. IBM
ha una sua distribuzione chiamata IBM Infosphere Big Insights, disponibile in
unedizione gratuita e in una commerciale. Alcuni grandi compagnie che
producono distribuzioni proprie hanno poi diversi store, alcuni apertamente
disponibili, mentre altri no. Non avete che limbarazzo della scelta
Scegliere una distribuzione
Come scegliere una distribuzione? Come abbiamo visto, quelle disponibili (e
non le abbiamo trattate tutte) variano da package e integrazioni di prodotti
completamente open source fino a soluzioni di integrazione e analisi su misura.
Non esiste la distribuzione migliore in assoluto: pensate alle vostre esigenze e
valutate le alternative. Poich offrono tutte la possibilit di scaricare gratuitamente
almeno una versione di base, pu essere una buona idea provarne qualcuna
direttamente.
Altri framework di calcolo
Abbiamo parlato pi volte della miriade di possibilit portate nella piattaforma
Hadoop da YARN. Abbiamo visto nel dettaglio due modelli nuovi, Samza e Spark.
Sul framework sono stati portati anche altri framework collaudati, come Pig.
Per fornire un quadro dinsieme pi ampio, in questo paragrafo illustreremo la
portata dellelaborazione possibile presentando una serie di modelli di calcolo
attualmente disponibili per Hadoop su YARN.
Apache Storm
Storm (http://storm.apache.org) un framework di calcolo distribuito scritto quasi
per intero nel linguaggio di programmazione Clojure. Utilizza sprout e bolt creati
dagli utenti per definire fonti di informazione e manipolazioni che consentono
lelaborazione distribuita dei dati in streaming. Unapplicazione Storm concepita
come una topologia di interfacce che crea uno stream di trasformazioni. Fornisce
una funzionalit simile a un job di MapReduce tranne per il fatto che, in teoria,
questa topologia continua a essere eseguita a tempo indeterminato finch non viene
interrotta manualmente.
stata costruita inizialmente come distinta da Hadoop, ma Yahoo! sta
sviluppando il porting di YARN (https://github.com/yahoo/storm-yarn).
Apache Giraph
Giraph nata come implementazione open source del documento Pregel di
Google (http://kowshik.github.io/JPregel/pregel_paper.pdf). Sia Giraph sia Pregel si
ispirano al modello del calcolo distribuito Bulk Synchronous Parallel (BSP)
introdotto da Valiant nel 1990. Giraph aggiunge ulteriori funzionalit compreso il
calcolo sul master, gli aggregatori e il calcolo parallelo. Trovate il porting di
YARN allindirizzo https://issues.apache.org/jira/browse/GIRAPH-13.
Apache HAMA
Hama un progetto Apache di alto livello che mira, come altri metodi che
abbiamo incontrato finora, ad azzerare i punti deboli di MapReduce rispetto alla
programmazione iterativa. Simile al gi citato Giraph, Hama implementa le
tecniche BSP e si ispira nella sua essenza al documento Pregel. Trovate il porting
di YARN alla pagina https://issues.apache.org/jira/browse/HAMA-431.
Altri progetti interessanti
Che utilizziate una distribuzione in bundle o che vi limitiate al download
standard di Apache Hadoop, troverete diversi riferimenti a progetti correlati. In
questo libro ne abbiamo trattati molti, come Hive, Samza e Crunch; facciamo un
rapido accenno ad altri.
Ricordate che lobiettivo evidenziare alcuni strumenti interessanti (dal punto
di vista dellautore) e suggerire la vastit dei progetti disponibili. Come gi detto,
guardatevi intorno, perch ne vengono lanciati sempre di nuovi.
HBase
Il progetto correlato ad Apache Hadoop forse pi famoso che non abbiamo
considerato in queste pagine HBase (http://hbase.apache.org). Basato sul modello
BigTable per lo storage dei dati diffuso da Google in un documento accademico
(vi suona familiare?), HBase un datastore non relazionale che risiede su HDFS.
Laddove MapReduce e Hive si concentrano su pattern di accesso ai dati di tipo
batch, HBase cerca di fornire un accesso ai dati a bassissima latenza. Di
conseguenza, a differenza delle altre tecnologie citate, pu supportare direttamente
i servizi di interazione con lutente.
Il modello di dati di HBase non segue lapproccio relazionale che era stato
utilizzato in Hive e in tutti gli altri RDBMS, n offre quelle garanzie di ACID
(Atomicity, Consistency, Isolation, Durability) che sono date per scontate con gli
store relazionali. invece una soluzione senza schema chiave-valore che prende
una vista dei dati orientata alle colonne; queste possono essere aggiunte in fase di
runtime e dipendono dai valori immessi in HBase. Ogni operazione di ricerca
risulta quindi molto rapida, poich consiste in una mappatura efficace dalla chiave
della riga alla colonna desiderata. HBase considera anche i timestamp come
unaltra dimensione sui dati, cos che sia possibile recuperare direttamente i dati
da un punto nel tempo.
Il modello di dati molto potente, ma non si adatta a tutti i casi duso, proprio
come il modello relazionale non applicabile universalmente. Se per vi
occorrono viste strutturate a bassa latenza di dati su vasta scala salvati in Hadoop,
allora HBase unopzione assolutamente da considerare.
Sqoop
Nel Capitolo 7, abbiamo introdotto gli strumenti per presentare uninterfaccia di
tipo relazionale per i dati salvati in HDFS. Spesso necessario recuperare questi
dati da un database relazionale esistente o salvare loutput di elaborazione.
Apache Sqoop (http://sqoop.apache.org) fornisce un meccanismo per specificare in
modo dichiarativo il movimento dei dati tra i database relazionali e Hadoop.
Prende una definizione, e da questa genera dei job di MapReduce per eseguire il
recupero o lo storage dei dati necessario. Produce inoltre del codice per aiutare
nella manipolazione dei record relazionali con classi Java personalizzate, e pu
integrarsi con with HBase e Hcatalog/Hive attraverso un set di opzioni molto
ricco.
Al momento della stesura di queste righe, Sqoop era in via di sviluppo, anche se
non in modo radicale. La sua versione originale, Sqoop 1, era unapplicazione lato
client pura. Analogamente al primo strumento a riga di comando di Hive, Sqoop 1
non ha server e genera tutto il codice sul client. Purtroppo questo significa che
ogni client deve conoscere molti dettagli sulle sorgenti fisiche dei dati, compresi i
nomi esatti degli host e le credenziali di autenticazione.
Sqoop 2 fornisce un server di Sqoop centralizzato che incorpora tutti questi
dettagli e offre varie sorgenti di dati configurate ai client che si connettono. un
modello superiore, ma almeno fino a poco tempo fa la community consigliava di
continuare a utilizzare Sqoop 1 fino a unulteriore evoluzione della nuova
versione. Se vi interessa questo tipo di strumento, verificatene lo stato dellarte.
Whirr
Quando si intende utilizzare dei servizi cloud come Amazon AWS per le
distribuzioni di Hadoop, in genere molto pi semplice impiegare un servizio di
livello pi elevato come Elastic MapReduce piuttosto che impostare il proprio
cluster su EC2. Per quanto esistano alcuni script che possono darci una mano, non
va trascurato loverhead implicito nelluso di distribuzioni basate su Hadoop sulle
infrastrutture cloud. qui che ci viene in soccorso Apache Whirr
(http://whirr.apache.org).
Whirr non centrato su Hadoop; ha a che fare con unistanziazione indipendente
dal fornitore di servizi di cui Hadoop solo un esempio. Il suo scopo quello di
fornire una modalit programmatica per specificare e creare delle distribuzioni
basate su Hadoop su strutture cloud gestendo tutti i servizi sottostanti al vostro
posto. Lo fa a prescindere dal provider, per cui, per esempio, una volta che avete
eseguito un cluster su EC2, potete utilizzare lo stesso codice per creare
unimpostazione identica su un altro provider come Rightscale o Eucalyptus.
Questo riduce il rischio di lock-in del produttore, che spesso fonte di
preoccupazione quando si distribuisce sul cloud.
Attualmente Whirr ancora limitato nei servizi che pu creare e supportare, ma
vale la pena tenerne docchio i progressi se siete interessati alla distribuzione
semplificata sul cloud.
SUGGERIM ENT O
Se state costruendo lintera infrastruttura su Amazon Web Services, potreste trovare un aiuto
anche per quanto riguarda la definizione dei requisiti delle applicazioni, anche se, ovviamente,
nello stile specifico di AWS.
Mahout
Apache Mahout (http://mahout.apache.org/) una raccolta di algoritmi distribuiti,
classi Java e strumenti per eseguire analisi avanzate su Hadoop. Simile alla
MLLib di Spark citata brevemente nel Capitolo 5, Mahout dotato di alcuni
algoritmi per i casi duso pi comuni: raccomandazioni, creazione di cluster,
regressioni ed engineering delle funzioni. Sebbene il sistema sia focalizzato su
attivit di elaborazione di linguaggio naturale e di text mining, i suoi elementi
costitutivi (operazioni algebriche lineari) sono applicabili in vari campi. Dalla
versione 0.9, il progetto stato scorporato dal framework di MapReduce a favore
di modelli di programmazione pi ricchi come Spark. Lobiettivo ultimo della
community quello di ottenere una libreria indipendente dalla piattaforma basata
su Scala DSL.
Hue
Sviluppato inizialmente da Cloudera e commercializzato come linterfaccia
utente per Hadoop, Hue (http://gethue.com/) un insieme di applicazioni accorpate
sotto uninterfaccia web comune che agiscono come client per i servizi core con
alcuni componenti dellecosistema di Hadoop.
Hue supporta molti degli strumenti di cui abbiamo parlato nei capitoli
precedenti e fornisce uninterfaccia integrata per analizzare e visualizzare i dati.
Ci sono due componenti particolarmente interessanti.
Da una parte, abbiamo un query editor che consente allutente di creare e
salvare interrogazioni di Hive (o Impala), esportare il risultato impostato nel
formato CSV o in quello di Microsoft Excel e poi strutturarlo nel browser. Leditor
include la capacit di condividere HiveQL e i set di risultati, facilitando la
collaborazione allinterno di unorganizzazione.
Cascading
Sviluppato da Concurrent, e rilasciato come open source sotto una licenza
Apache, Cascading (http://www.cascading.org/) un framework diffuso che astrae la
complessit di MapReduce e consente di creare dei flussi di lavoro complessi su
Hadoop. I suoi job possono essere compilati ed eseguiti su MapReduce, Tez e
Spark. Da un punto di vista concettuale, il framework simile ad Apache Crunch,
trattato nel Capitolo 9, sebbene nella pratica ci siano alcune differenze per quanto
riguarda lastrazione dei dati e gli obiettivi finali. Cascading adotta un modello di
dati a tuple (analogamente a Pig) invece che oggetti arbitrari, e spinge lutente ad
affidarsi a un DSL di livello pi elevato e a potenti tipi interni e strumenti per
manipolare i dati. Si potrebbe dire che Cascading sta a PigLatin e ad HiveQL
come Crunch sta a una funzione definita dallutente.
Come Morphlines, trattato anchesso nel Capitolo 9, il modello di dati di
Cascading segue un approccio sorgente-pipe-sink, nel quale i dati vengono
catturati da una sorgente, convogliati in un elaborazione in pi passi e il cui output
viene rilasciato in un sink, pronto per essere prelevato da unaltra applicazione.
Cascading incoraggia gli sviluppatori a scrivere il codice in diversi linguaggi
JVM. possibile il porting del framework per Python (PyCascading), JRuby
(Cascading.jruby), Clojure (Cascalog, e Scala (Scalding). Cascalog e Scalding in
particolare hanno trovato riscontro e diffusione al di fuori dei loro ecosistemi
specifici.
Un ambito in cui Cascading eccelle quello della documentazione. Il progetto
fornisce Javadoc articolati dellAPI, tutorial ben fatti
(http://www.cascading.org/documentation/tutorials/) e un ambiente di apprendimento
interattivo basato su alcuni esercizi (https://github.com/Cascading/Impatient).
Un altro punto di forza di Cascading sta nella sua integrazione con ambienti di
terze parti. Amazon EMR lo supporta come framework di elaborazione di prima
classe e consente di avviarne dei cluster sia dalla riga di comando sia tramite
delle interfacce web
(http://docs.aws.amazon.com/ElasticMapReduce/latest/DeveloperGuide/CreateCascading.html).
Esistono plug-in per SDK per gli ambienti di sviluppo integrato IntelliJ IDEA ed
Eclipse. Uno dei progetti principali del framework, Cascading Patterns, una
raccolta di algoritmi ad apprendimento automatico, offre unutility per tradurre i
documenti scritti con il Predictive Model Markup Language (PMML) in
applicazioni su Apache Hadoop, facilitando linteroperabilit con ambienti
statistici e strumenti scientifici diffusi come R (http://cran.r-
project.org/web/packages/pmml/index.html).
Risorse per gli AWS
Molte tecnologie Hadoop possono essere distribuite su AWS come parte di un
cluster autogestito. Tuttavia, cos come Amazon offre il supporto per Elastic
MapReduce, che amministra Hadoop come un servizio gestito, ci sono altri servizi
che vale la pena citare.
SimpleDB e DynamoDB
Per qualche tempo, AWS ha offerto SimpleDB come servizio hosted che forniva
un modello di dati di tipo HBase.
Oggi stato sopravanzato da un servizio pi recente, DynamoDB
(http://aws.amazon.com/dynamodb). Sebbene il suo modello di dati sia simile a quello di
SimpleDB e HBase, concepito per un tipo di applicazioni molto diverso.
Laddove SimpleDB ha unAPI di ricerca piuttosto ricca ma abbastanza limitato
in termini di dimensioni, DynamoDB fornisce unAPI pi vincolata ma in costante
evoluzione che offre una garanzia di scalabilit pressoch illimitata del servizio.
Il piano tariffario di DynamoDB particolarmente interessante; invece di
pagare per un certo numero di server che ospitano il servizio, allocate una
determinata capacit per le operazioni di lettura e scrittura, e DynamoDB gestir
le risorse necessarie a soddisfare la capacit indicata. Si tratta quindi di un
modello di servizio puro, nel quale il meccanismo di fornitura delle prestazioni
desiderate del tutto nascosto allutente. Date unocchiata a DynamoDB, ma solo
se vi occorre uno storage di dati pi grande di quello offerto da SimpleDB;
valutate attentamente le tariffe, perch lallocazione di una capacit molto elevata
pu diventare presto molto costosa. Amazon fornisce alcune best practice per
DynamoDB che illustrano come ridurre al minimo i costi del servizio possa
portare a unulteriore complessit del livello dellapplicazione
(http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/BestPractices.html).
NOTA
Ovviamente DynamoDB e SimpleDB presuppongono un modello di dati non relazionale; per un
database relazionale sul cloud fate riferimento ad Amazon Relational Database Service
(Amazon RDS).
Kinesis
Cos come EMR gira su Hadoop e DynamoDB ha delle analogie con un hosted
HBase, non stata una sorpresa, nel 2013, lannuncio di AWS riguardo a Kinesis,
un servizio hosted di dati in streaming. Lo trovate allindirizzo
http://aws.amazon.com/kinesis e ha una struttura concettualmente simile a quella dello
stack di Samza su Kafka. Kinesis fornisce una vista partizionata dei messaggi sotto
forma di uno stream di dati e unAPI per lesecuzione di callback allarrivo dei
messaggi. Come accade con la maggior parte dei servizi AWS, c una stretta
integrazione con altri servizi che facilita il recupero e la generazione di dati in
posti come S3.
Data Pipeline
Lultimo servizio AWS di cui parliamo Data Pipeline
(http://aws.amazon.com/datapipeline). Come si pu intuire dal nome, si tratta di un
framework per costruire job di elaborazione dei dati che implicano pi passi,
alcuni spostamenti dei dati e delle trasformazioni. Da un certo punto di vista si
sovrappone concettualmente a Oozie, ma con alcune differenze. Come prima cosa,
e come prevedibile, Data Pipeline si integra in profondit con molti altri servizi
AWS, consentendo di definire facilmente i workflow di dati che incorporano vari
repository come RDS, S3 e DynamoDB. Oltre a questo, ha la capacit di integrare
degli agent installati sullinfrastruttura locale, fornendo un percorso interessante
per creare workflow che si estendono sugli AWS e sugli ambienti di costruzione.
Fonti di informazione
Non vi servono solo nuove tecnologie e nuovi strumenti, per quanto stimolanti.
A volte un piccolo aiuto da una fonte pi esperta pu tiravi fuori dai pasticci. Da
questo punto di vista siete in una botte di ferro, perch la community di Hadoop
estremamente solida in molti ambiti.
Codice sorgente
In genere un aspetto che si trascura, ma Hadoop e tutti gli altri progetti Apache
sono open source. Il codice sorgente la fonte definitiva delle informazioni su
come il sistema lavora. Acquisendo familiarit con esso e tenendo traccia della
sua funzionalit si possono ottenere diverse indicazioni. Per non parlare di quando
incappate in qualche comportamento imprevisto
Gruppi di LinkedIn
Trovate alcuni gruppi di Hadoop o a essi correlati anche su LinkedIn. Potete
eseguire una ricerca per una determinata area di interesse, ma un buon punto di
partenza potrebbe essere il gruppo utenti generico di Hadoop allindirizzo
.
http://www.linkedin.com/groups/Hadoop-Users-988957
HUG
Se desiderate uninterazione pi faccia a faccia, allora cercate un Hadoop
User Group (HUG) nella vostra zona, la maggior parte dei quali elencata
allindirizzo http://wiki.apache.org/hadoop/HadoopUserGroups. Questi gruppi organizzano
abbastanza regolarmente degli incontri in cui combinano presentazioni di qualit,
la possibilit di discutere la tecnologia con altri individui che condividono lo
stesso interesse e spesso anche una pizza e qualche bevuta. Non ci sono HUG
vicino a voi? Potreste crearne uno!
Conferenze
Laddove in diversi settori ci vogliono decenni per riuscire a pianificare un
circuito di conferenze, Hadoop molto attivo nel campo, organizzando incontri
che coinvolgono i mondi accademico, commerciale e dellopen source. Eventi
come lHadoop Summit e Strata sono piuttosto importanti; trovate alcuni
riferimenti alla pagina http://strataconf.com/.
Riepilogo
In questo capitolo abbiamo compiuto una rapida cavalcata nellecosistema di
Hadoop, trattando quanto segue.
Perch esistono distribuzioni alternative di Hadoop e quali sono le pi
famose.
Altri progetti che forniscono funzionalit, estensioni o strumenti di supporto
ad Hadoop.
Modi alternativi per lavorare con Hadoop.
Risorse per entrare nella comunit di Hadoop.
Ora tocca voi creare qualcosa di entusiasmante!
Indice
Introduzione
Struttura del libro
Cosa serve per questo libro
Lo scopo del libro
Convenzioni
Codice degli esempi
Gli autori
I revisori
Capitolo 1 - Per iniziare
Una nota sulle versioni
Panoramica su Hadoop
Componenti di Hadoop
Hadoop 2: dov laffare?
Distribuzioni di Apache Hadoop
Un doppio approccio
AWS: infrastruttura on demand di Amazon
Come iniziare
Eseguire gli esempi
Elaborazione dei dati con Hadoop
Riepilogo
Capitolo 2 - Storage
Funzionamento interno di HDFS
Accedere al file system HDFS tramite riga di comando
Proteggere i metadati del file system
Apache ZooKeeper: un file system diverso
Failover automatico dei NameNode
Snapshot HDFS
File system di Hadoop
Gestire e serializzare i dati
Storage dei dati
Riepilogo
Capitolo 3 - Elaborazione: MapReduce e oltre
MapReduce
API Java per MapReduce
Scrivere programmi MapReduce
Panoramica sullesecuzione di un job di MapReduce
YARN
YARN nel mondo reale: il calcolo oltre MapReduce
Riepilogo
Capitolo 4 - Computazione in tempo reale con Samza
Elaborazione degli stream con Samza
Riepilogo
Capitolo 5 - Computazione iterativa con Spark
Apache Spark
Lecosistema di Spark
Elaborare i dati con Apache Spark
Spark e Samza Streaming a confronto
Riepilogo
Capitolo 6 - Analisi dei dati con Apache Pig
Panoramica su Pig
Per iniziare
Eseguire Pig
Fondamenti di Apache Pig
Programmare Pig
Estendere Pig (UDF)
Analizzare lo stream di Twitter
Riepilogo
Capitolo 7 - Hadoop e SQL
Perch SQL su Hadoop
Prerequisiti
Larchitettura di Hive
Hive e Amazon Web Services
Estendere HiveQL
Interfacce programmatiche
Liniziativa Stinger
Impala
Riepilogo
Capitolo 8 - Gestione del ciclo di vita dei dati
Cos la gestione del ciclo di vita dei dati
Costruire la capacit per lanalisi dei tweet
Le sfide dei dati esterni
Raccogliere dati supplementari
Assemblare il tutto
Riepilogo
Capitolo 9 - Facilitare il lavoro di sviluppo
Scegliere un framework
Hadoop Streaming
Kite Data
Apache Crunch
Riepilogo
Capitolo 10 - Eseguire un cluster Hadoop
Sono uno sviluppatore, le operations non mi interessano!
Cloudera Manager
Ambari, lalternativa open source
Le operations nel mondo di Hadoop 2
Condividere le risorse
Costruire un cluster fisico
Costruire un cluster su EMR
Raffinamento dei cluster
Sicurezza
Monitorare
Risoluzione dei problemi
Riepilogo
Capitolo 11 - Come proseguire
Distribuzioni alternative
Altri framework di calcolo
Altri progetti interessanti
Altre astrazioni di programmazione
Risorse per gli AWS
Fonti di informazione
Riepilogo