Hadoop / Big Data

Benjamin Renaut <renaut.benjamin@tokidev.fr>

MBDS 2016 - 2017

1 Introduction

Programme – Planning – Objectifs – TP/Évaluations

Introduction
1-1

Benjamin Renaut
Tokidev SAS
- Bureau d'étude
- Développement
- Consulting

http://www.tokidev.fr/

tokidev. Si un point n'est pas clair. prenez le réflexe d'effectuer une recherche – dans une majorité de cas. .fr Vous pouvez m'envoyer toute question par e-mail concernant le cours.fr/bigdata/ Disponible à cette adresse: la machine virtuelle utilisée pour les TPs.Avant de commencer 1-2 ● Posez des questions. ● Point de contact: ben@tokidev. quelqu'un d'autre aura eu le même problème que vous :-) ● Cours en ligne: http://cours. n'hésitez pas à poser des questions à tout moment. les TDs ou le partiel. ● Google est votre ami ! Si vous rencontrez un problème. les slides des cours.

Objectifs 1-4 ● Découvrir la méthodologie map/reduce. ● Apprendre à installer et utiliser Hadoop. ● Apprendre à utiliser Apache Spark. MongoDB. . ► Apprentissage basé sur la pratique.). ● Apprendre à rédiger et exécuter des programmes pour Hadoop. ● Découvrir diverses solutions complémentaires liées à Hadoop et aux problématiques « Big Data » en général (Pig. etc.

2 Le calcul distribué / Historique Hadoop .

● Tolérance aux pannes: une machine en panne faisant partie du cluster ne doit pas produire d'erreur pour le calcul dans son ensemble. l'implémentation différents langages. ● Extensibilité: on doit pouvoir ajouter de nouvelles machines pour le calcul si nécessaire. . Problématiques: ● Accès et partage des ressources pour toutes les machines. ● Transparence: le cluster dans son ensemble doit être utilisable comme une seule et même machine « traditionnelle ».Le calcul distribué 2-1 Désigne l'exécution d'un traitement informatique sur une multitude de machines différentes (un cluster de machines) de manière transparente. ● Hétérogénéité: les machines doivent pouvoir avoir différentes architectures.

Solution qui suppose qu'on trouve des volontaires susceptibles de partager leur puissance de calcul. On distingue historiquement deux approches/cas d'usage: ● Effectuer des calculs intensifs localement (recherche scientifique. . rendu 3D. etc.Le calcul distribué 2-2 Ces problématiques sont complexes et ont donné lieu à des années de recherche et d'expérimentation.) .on souhaite avoir un cluster de machines local pour accélérer le traitement. ● Exploiter la démocratisation de l'informatique moderne et la bonne volonté des utilisateurs du réseau pour créer un cluster distribué via Internet à moindre coût. Solution qui était jusqu'ici coûteuse et complexe à mettre en oeuvre.

l'étude de signaux radio astronomiques. . Il s'agit d'un cluster « local » (ne passant pas par Internet).Exemple : Blue Gene (1999) 2-3 Supercalculateur « classique ». le tout sous un contrôle centralisé pour assurer l'exécution de tâches distribuées. etc. L'architecture est ici spécifiquement construite pour le calcul distribué à grande échelle. Utilisé pour des simulations médicales. Premier supercalculateur à être commercialisé et produit (par IBM) en plusieurs exemplaires. Connecte 131072 CPUs et 32 tera-octets de RAM.

Il s'agit d'un cluster « distant » : distribué via Internet. Le GPU est nettement plus performant que le CPU pour certaines tâches comme le traitement du signal ou les calculs distribués sur les nombres flottants. Utilisé pour la simulation de protein folding (maladies à prion).net (2007) 2-4 Projet de l'université Pompeu Fabra (Espagne) permettant à des volontaires partout dans le monde de mettre à disposition le GPU de leur carte graphique pour le calcul distribué (via NVIDIA CUDA). et d'une manière générale des applications médicales.Exemple : GPUGRID. la simulation moléculaire. .

La NASA utilise ainsi un cluster Beowulf. Une machine maître distribue les tâches à une série de machines esclaves. L'ensemble du cluster ainsi formé apparaît au niveau du serveur maître comme une seule et même machine. . Généralement concrétisé via plusieurs machines sous GNU/Linux utilisant des interfaces standardisées. Permet à tout un chacun d'obtenir un système de calcul distribué à hautes performances à partir d'un matériel peu onéreux.Exemple: clusters Beowulf (1998) 2-5 Architecture logique définissant un moyen de connecter plusieurs ordinateurs personnels entre eux pour l'exécution de tâches distribuées sur un réseau local.

donnant lieu à de nombreux autres projets (Einstein@Home. Remarque: GPUGRID utilise désormais lui aussi le framework BOINC. etc. BOINC.Exemple: SETI@Home/BOINC 2-6 A l'origine. Objectif : détecter d'éventuels signes de civilisations extra-terrestres en analysant des signaux radios reçus par l'observatoire Arecibo au Porto Rico.). . un des premiers logiciels à permettre le calcul distribué via l'utilisation des ordinateurs de volontaires tout autour du monde. Folding@Home. Architecture et protocoles de communication ensuite standardisés et mis à disposition sous la forme d'un framework.

Cependant. ● Des solutions développées en interne: investissement initial très conséquent. mais complexe à mettre en œuvre pour beaucoup d'entreprises ou petites universités. . ● Architecture Beowulf: un début de réponse.Conclusions 2-7 Pour l'exécution de tâches distribuées distantes via la mise à disposition de machines tout autour du monde par des volontaires. et nécessitant là aussi un investissement initial assez conséquent. réservés aux grands groupes industriels. de nombreuses universités et entreprises ont des besoins d'exécution locale de tâches parallélisables sur des données massives. la création du framework BOINC a apporté une réponse efficace – de nombreux projets universitaires et scientifiques exploitant aujourd'hui la technologie. Les solutions qui étaient disponibles jusqu'ici: ● Des super calculateurs « classiques » comme Blue Gene: très onéreux. nécessite des compétences et une rigueur coûteuses. souvent trop puissants par rapport aux besoins requis.

L'outil en question devant être facile à déployer. facile à déployer. et pouvant permettre la création de clusters de taille variables extensibles à tout moment.Le problème 2-8 Le problème qui se posait jusqu'ici pour ce cas d'usage: Avoir un framework déjà disponible. . L'idée étant d'avoir un outil « off the shelf » qui puisse être installé et configuré rapidement au sein d'une entreprise/d'une université et qui permettre à des développeurs d'exécuter des tâches distribuées avec un minimum de formation requise. simple à supporter. et qui permette l'exécution de tâches parallélisables – et le support et le suivi de ces tâches – de manière rapide et simple à mettre en œuvre.

● Déployable très facilement (paquets Linux pré-configurés). comme l’accès et le partage des données.. ou encore la répartition des tâches aux machines membres du cluster : le programmeur a simplement à s'occuper du développement logiciel pour l'exécution de la tâche. . Python.La solution: Apache Hadoop 2-9 Avantages: ● Projet de la fondation Apache – Open Source. ● Modèle simple pour les développeurs: il suffit de développer des tâches map-reduce. C/C++. composants complètement ouverts.. la tolérance aux pannes. depuis des interfaces simples accessibles via des librairies dans des langages multiples (Java. ● S'occupe de toutes les problématiques liées au calcul distribué. tout le monde peut participer.). configuration très simple elle aussi.

. . .2006: Doug Cutting (désormais chez Yahoo) est en charge d'améliorer l'indexation du moteur de recherche de Yahoo.Historique (1/2) 2-10 . L'implémentation peut tourner seulement sur quelques machines et a de multiples problèmes. Il exploite le framework réalisé précédemment.2004: Doug Cutting et Mike Cafarella développent un framework (encore assez primitif) inspiré des papers de Google et portent leur projet Nutch sur ce framework.2002: Doug Cutting (directeur archive. notamment en ce qui concerne l'accés et le partage de fichiers.. .2003/2004: le département de recherche de Google publie deux whitepapers. le premier sur GFS (un système de fichier distribué) et le second sur le paradigme Map/Reduce pour le calcul distribué. .org) et Mike Cafarella (étudiant) développent Nutch. un moteur de recherche Open Source exploitant le calcul distribué.

2011: Hadoop est désormais utilisé par de nombreuses autres entreprises et des universités. . .Historique (2/2) 2-11 … et créé une nouvelle version améliorée du framework en tant que projet Open Source de la fondation Apache. qu'il nomme Hadoop (le nom d'un éléphant en peluche de son fils).2008: le développement est maintenant très abouti. etc. et le cluster Yahoo comporte 42000 machines et des centaines de peta- octets d'espace de stockage.  Hadoop est encore largement en développement – un cluster pouvait alors comporter au maximum 5 à 20 machines. et Hadoop est exploité par le moteur de recherche de Yahoo Ainsi que par de nombreuses autres divisions de l'entreprise. . A l'époque.

.Qui utilise Hadoop 2-12 … et des centaines d'entreprises et universités à travers le monde.

pour un marché de 813 millions de dollars en 2016 uniquement pour la vente de logiciels autour de Hadoop. . ● Pour le public. etc. et Hadoop offre une solution idéale et facile à implémenter au problème. Estimations IDC: croissance de 60% par an de l'industrie « Big Data ». Ces données doivent toutes être analysées.Une technologie en plein essort 2-13 ● De plus en plus de données produites par des systèmes d'information de plus en plus nombreux. … le domaine de recherche/industriel autour de la gestion et de l'analyse de ces données – et de Hadoop et les technologies associées – est communément désigné sous l'expression « Big Data ». l'informatisation au sein des villes (« smart cities ») et des administrations se développe de plus en plus et va produire des quantités massives de données. corrélées.

3 Le modèle Map/Reduce .

● MapReduce est un paradigme (un modèle) visant à généraliser les approches existantes pour produire une approche unique applicable à tous les problèmes. Scheme). généralisable à tous les problèmes et orientée calcul distribué est attribuable à un whitepaper issu du département de recherche de Google publié en 2004 (« MapReduce: Simplified Data Processing on Large Clusters »). ● MapReduce existait déjà depuis longtemps.Présentation 3-1 ● Pour exécuter un problème large de manière distribué. mais la présentation du paradigme sous une forme « rigoureuse ». il faut pouvoir découper le problème en plusieurs problèmes de taille réduite à exécuter sur chaque machine du cluster (stratégie algorithmique dite du divide and conquer / diviser pour régner). ● De multiples approches existent et ont existé pour cette division d'un problème en plusieurs « sous-tâches ». . notamment dans les langages fonctionnels (Lisp.

Présentation 3-2 MapReduce définit deux opérations distinctes à effectuer sur les données d'entrée: ● La première. Par ailleurs. cette opération doit être parallélisable: on doit pouvoir découper les données d'entrée en plusieurs fragments. Chacune des machines effectuera alors l'opération REDUCE pour cette clef. en lui donnant la liste des valeurs associées à la clef. on attribuera à chacune des machines du cluster une des clefs uniques produites par MAP. va transformer les données d'entrée en une série de couples clef/valeur. on aura un résultat pour chacune des clefs distinctes. va appliquer un traitement à toutes les valeurs de chacune des clefs distinctes produite par l'opération MAP. MAP. Elle va regrouper les données en les associant à des clefs. et faire exécuter l'opération MAP à chaque machine du cluster sur un fragment distinct. Ici. . Au terme de l'opération REDUCE. REDUCE. ● La seconde. choisies de telle sorte que les couples clef/valeur aient un sens par rapport au problème à résoudre.

valeur) par clef. ● Mapper chacun de ces fragments pour obtenir des couples (clef .Présentation 3-3 On distingue donc 4 étapes distinctes dans un traitement MapReduce: ● Découper (split) les données d'entrée en plusieurs fragments. on le rend parallélisable – chacune de ces tâches à l'exception de la première seront effectuées de manière distribuée. . valeur). ● Grouper (shuffle) ces couples (clef . En modélisant le problème à résoudre de la sorte. avec une valeur pour chacune des clefs distinctes. ● Réduire (reduce) les groupes indexés par clef en une forme finale.

). etc. ● Définir quelle CLEF utiliser pour notre problème. on devra donc: ● Choisir une manière de découper les données d'entrée de telle sorte que l'opération MAP soit parallélisable. groupement par clef distincte entre MAP et REDUCE. ● Écrire le programme pour l'opération MAP. .Présentation 3-4 Pour résoudre un problème via la méthodologie MapReduce avec Hadoop. … et Hadoop se chargera du reste (problématiques calcul distribué. ● Ecrire le programme pour l'opération REDUCE.

.Exemple concret (1/8) 3-5 Imaginons qu'on nous donne un texte écrit en langue Française. nos données d'entrée sont constituées du contenu du texte. On souhaite déterminer pour un travail de recherche quels sont les mots les plus utilisés au sein de ce texte (exemple Hadoop très répandu). Chacune des lignes du texte sera un fragment de nos données d'entrée. Ici. Première étape: déterminer une manière de découper (split) les données d'entrée pour que chacune des machines puisse travailler sur une partie du texte. Notre problème est ici très simple – on peut par exemple décider de découper les données d'entrée ligne par ligne.

on va avant le découpage supprimer toute ponctuation et tous les caractères accentués. fragment) Fou Fou qui qui songe songe àà ses ses querelles querelles Pour simplifier les choses. . 1943. La rose et le Fou Fou qui qui fait fait le le délicat délicat Réséda.Exemple concret (2/8) 3-6 Nos données d'entrée (le texte): Celui Celui qui qui croyait croyait au au ciel ciel Celui Celui qui qui n'y n'y croyait croyait pas pas […] […] (Louis Aragon. On va également passer l'intégralité du texte en minuscules.

Exemple concret (3/8) 3-7 Après découpage: celui celui qui qui croyait croyait au au ciel ciel celui celui qui qui ny ny croyait croyait pas pas fou fou qui qui fait fait le le delicat delicat fou fou qui qui songe songe aa ses ses querelles querelles … on obtient 4 fragments depuis nos données d'entrée. .

on donne la valeur « 1 ». la clef qui s'impose logiquement dans notre cas est: le mot-lui même. pour chacun des mots. générer le couple clef/valeur: (MOT . et écrire le code de l'opération MAP elle-même. et qu'à terme on aura après l'opération REDUCE un résultat pour chacune des clefs distinctes. Puisqu'on s'intéresse aux occurrences des mots dans le texte. . elle sera elle aussi très simple: on va simplement parcourir le fragment qui nous est fourni et.puisqu'on a croisé le mot une fois. Quand à notre opération MAP. La valeur indique ici l’occurrence pour cette clef . 1).Exemple concret (4/8) 3-8 On doit désormais déterminer la clef à utiliser pour notre opération MAP.

LIGNE.1) (pas.1) (ses.1) (ny.1) (celui. valeur) générés seront donc: celui celui qui qui croyait croyait au au ciel ciel (celui.1) (qui.1) (pas. FAIRE: FAIRE: GENERER GENERER COUPLE COUPLE (MOT.1) (le.1) fou fou qui qui fait fait le le delicat delicat (fou.1) (fou.1) (qui. 1) 1) Pour chacun de nos fragments.1) (ny.1) (querelles.1) (a.1) .1) fou fou qui qui songe songe aa ses ses querelles querelles (fou.1) (qui.1) celui celui qui qui ny ny croyait croyait pas pas (celui. (MOT.1) (songe.1) (qui.1) (qui.1) (delicat.1) (croyait.1) (querelles.1) (ciel.1) (fou.1) (delicat.1) (qui.1) (au.1) (fait.1) (croyait.1) (le.1) (ciel. les couples (clef.1) (fait.1) (a.1) (croyait.1) (croyait.1) (au.1) (songe.1) (celui.1) (qui.Exemple concret (5/8) 3-9 Le code de notre opération MAP sera donc (ici en pseudo code): POUR POUR MOT MOT dans dans LIGNE.1) (qui.1) (ses.

1) (fou. là aussi.1) (qui.1) (croyait.1) (croyait.1) (le.1) (au.1) (fou.1) (qui.1) (delicat.1) (qui.1) (querelles.1) (croyait.1) (querelles. Elle est. on obtiendra les 15 groupes suivants: (celui.1) (ciel.1) . Cette opération est effectuée automatiquement par Hadoop.1) (songe.1) (ses.1) (fou.1) (a.1) (qui.1) (celui.1) (ny.1) (croyait.1) (pas.1) (celui.1) (qui.1) (au.1) (delicat. Après son exécution.1) (songe. effectuée de manière distribuée en utilisant un algorithme de tri distribué. Hadoop groupera (shuffle) tous les couples par clef commune. de manière récursive.1) (ses.1) (fait.1) (celui.1) (qui.1) (ciel.1) (a.1) (fait.1) (ny.1) (qui.Exemple concret (6/8) 3-10 Une fois notre opération MAP effectuée (de manière distribuée).1) (fou.1) (qui.1) (pas.1) (le.

GROUPE. elle va simplement consister à additionner toutes les valeurs liées à la clef spécifiée: TOTAL=0 TOTAL=0 POUR POUR COUPLE COUPLE dans dans GROUPE. FAIRE: FAIRE: TOTAL=TOTAL+1 TOTAL=TOTAL+1 RENVOYER RENVOYER TOTAL TOTAL .Exemple concret (7/8) 3-11 Il nous reste à créer notre opération REDUCE. Dans notre cas. qui sera appelée pour chacun des groupes/clef distincte.

ciel: ciel: 11 ny: ny: 11 pas: pas: 11 fait: fait: 11 […] […] . « croyait » et « fou ».Exemple concret (8/8) 3-12 Une fois l'opération REDUCE effectuée. on obtiendra donc une valeur unique pour chaque clef distincte. avec 4 occurrences. En l’occurrence. croyait: 22 fou: fou: 22 suivi de « celui ». notre résultat sera: qui: qui: 44 celui: On constate que le mot le plus utilisé dans celui: 22 croyait: notre texte est « qui ». avec au: au: 11 2 occurrences chacun.

L’intérêt du modèle MapReduce est qu'il nous suffit de développer les deux opérations réellement importantes du traitement: MAP et REDUCE. et son exécution aurait été instantanée même sur une machine unique. .conclusion 3-13 Notre exemple est évidemment trivial. et de bénéficier automatiquement de la possibilité d'effectuer le traitement sur un nombre variable de machines de manière distribuée. et obtenir ainsi un bon échantillon des mots les plus utilisés dans la langue Française.Exemple concret . mais il est d'ores et déjà utile: on pourrait tout à fait utiliser les mêmes implémentations de MAP et REDUCE sur l'intégralité des textes d'une bibliothèque Française.

Schéma général 3-14 .

php?id=4 [24/Oct/2013:18:13:12 [24/Oct/2013:18:13:12 +0200] +0200] /news.html /contact.php?id=5 /news. Ici.php?id=5 [24/Oct/2013:18:13:02 [24/Oct/2013:18:13:02 +0200] +0200] /news.... notre clef sera par exemple l'URL d’accès à la page. On dispose des fichiers de logs sous la forme suivante: /index.html [19/Oct/2013:18:45:03 [19/Oct/2013:18:45:03 +0200] +0200] /contact..Exemple – Statistiques web 3-15 Un autre exemple: on souhaite compter le nombre de visiteurs sur chacune des pages d'un site Internet. et nos opérations MAP et REDUCE seront exactement les mêmes que celles qui viennent d'être présentées: on obtiendra ainsi le nombre de vue pour chaque page distincte du site.html [19/Oct/2013:18:46:15 [19/Oct/2013:18:46:15 +0200] +0200] /news..php?id=4 /news. .etc.. .php?id=18 /news.html /index.php?id=18 [24/Oct/2013:18:14:31 [24/Oct/2013:18:14:31 +0200] +0200] .etc...

Exemple – Graphe social 3-16 Un autre exemple: on administre un réseau social comportant des millions d'utilisateurs. . On va donc développer des programmes MAP et REDUCE pour cette opération et exécuter le traitement toutes les nuits sur notre base de données. On souhaite afficher quand un utilisateur va sur la page d'un autre utilisateur une indication « Vous avez N amis en commun ». On ne peut pas se permettre d'effectuer une série de requêtes SQL à chaque fois que la page est accédée (trop lourd en traitement). Pour chaque utilisateur. on a dans notre base de données la liste des utilisateurs qui sont ses amis sur le réseau (via une requête SQL). en stockant le résultat dans une nouvelle table.

D. D. . B. C. B. On peut segmenter les données d'entrée là aussi par ligne. EE DD => => A. Par exemple.Exemple – Graphe social 3-17 Ici. C. C. B. A. C. DD BB => => A. la clef « A-B » désignera « les amis en communs des utilisateurs A et B ». C. EE CC => => A. C. D. nos données d'entrée sous la forme Utilisateur => Amis: AA => => B. B. D. B. on va choisir pour clef la concaténation entre deux utilisateurs. A. C. A. C. EE EE => => B. DD Puisqu'on est intéressé par l'information « amis en commun entre deux utilisateurs » et qu'on aura à terme une valeur par clef. B.

et va générer toutes les clefs distinctes possibles à partir de cette liste.valeur): les deux listes d'amis de chacun des utilisateurs qui composent la clef. deux couples (clef. pour chaque clef distincte.Exemple – Graphe social 3-18 Notre opération MAP va se contenter de prendre la liste des amis fournie en entrée. La valeur sera simplement la liste d'amis. On fait également en sorte que la clef soit toujours triée par ordre alphabétique (clef « B-A » sera exprimée sous la forme « A-B »). telle quelle. Ce traitement peut paraître contre-intuitif. . mais il va à terme nous permettre d'obtenir.

("A-D". FAIRE: FAIRE: SI SI UTILISATEUR UTILISATEUR << AMI: AMI: CLEF CLEF == UTILISATEUR+"-"+AMI UTILISATEUR+"-"+AMI SINON: SINON: CLEF CLEF == AMI+"-"+UTILISATEUR AMI+"-"+UTILISATEUR GENERER GENERER COUPLE COUPLE (CLEF. "B "B CC D") D") ("A-D". C. (CLEF.Exemple – Graphe social 3-19 Le pseudo code de notre opération MAP: UTILISATEUR UTILISATEUR == [PREMIERE [PREMIERE PARTIE PARTIE DE DE LA LA LIGNE] LIGNE] POUR POUR AMI AMI dans dans [RESTE [RESTE DE DE LA LA LIGNE]. "B "B CC D") D") . ("A-B". C. B. DD On obtiendra les couples (clef. ("A-C". "B "B CC D") D") ("A-C". [RESTE [RESTE DE DE LA LA LIGNE]) LIGNE]) Par exemple.valeur): ("A-B". LIGNE]. pour la première ligne: AA => => B.

("A-B". "A "A BB DD E") E") ("C-D".et ainsi de suite pour nos 5 lignes d'entrée. ("B-E". ("B-C". "A "A BB DD E") E") ("C-E". ("A-C". "A "A CC DD E") E") ("B-D". ("C-D". "A "A BB DD E") E") . B. C.. EE On aura: ("A-C". A. ("B-C". EE On obtiendra ainsi: ("A-B". . A. "A "A BB DD E") E") ("B-C". D. D. ("B-D". C. "A "A CC DD E") E") ("B-E". ("C-E".Exemple – Graphe social 3-20 Pour la seconde ligne: BB => => A. B.. D. D. "A "A CC DD E") E") Pour la troisième ligne: CC => => A. "A "A CC DD E") E") ("B-C".

Exemple – Graphe social
3-21

Une fois l'opération MAP effectuée, Hadoop va récupérer les couples
(clef;valeur) de tous les fragments et les grouper par clef distincte. Le
résultat sur la base de nos données d'entrée:
Pour
Pour la
la clef
clef "A-B":
"A-B": valeurs
valeurs "A
"A CC DD E"
E" et
et "B
"B CC D"
D"
Pour
Pour la
la clef
clef "A-C":
"A-C": valeurs
valeurs "A
"A BB DD E"
E" et
et "B
"B CC D"
D"
Pour
Pour la
la clef
clef "A-D":
"A-D": valeurs
valeurs "A
"A BB CC E"
E" et
et "B
"B CC D"
D"
Pour
Pour la
la clef
clef "B-C":
"B-C": valeurs
valeurs "A
"A BB DD E"
E" et
et "A
"A CC DD E"
E"
Pour
Pour la
la clef
clef "B-D":
"B-D": valeurs
valeurs "A
"A BB CC E"
E" et
et "A
"A CC DD E"
E"
Pour
Pour la
la clef
clef "B-E":
"B-E": valeurs
valeurs "A
"A CC DD E"
E" et
et "B
"B CC D"
D"
Pour
Pour la
la clef
clef "C-D":
"C-D": valeurs
valeurs "A
"A BB CC E"
E" et
et "A
"A BB DD E"
E"
Pour
Pour la
la clef
clef "C-E":
"C-E": valeurs
valeurs "A
"A BB DD E"
E" et
et "B
"B CC D"
D"
Pour
Pour la
la clef
clef "D-E":
"D-E": valeurs
valeurs "A
"A BB CC E"
E" et
et "B
"B CC D"
D"
… on obtient bien, pour chaque clef « USER1-USER2 », deux listes d'amis: les
amis de USER1 et ceux de USER2.

Exemple – Graphe social
3-22

Il nous faut enfin écrire notre programme REDUCE.
Il va recevoir en entrée toutes les valeurs associées à une clef. Son rôle
va être très simple: déterminer quels sont les amis qui apparaissent
dans les listes (les valeurs) qui nous sont fournies. Pseudo-code:
LISTE_AMIS_COMMUNS=[]
LISTE_AMIS_COMMUNS=[] //
// Liste
Liste vide
vide au
au départ.
départ.
SI
SI LONGUEUR(VALEURS)!=2,
LONGUEUR(VALEURS)!=2, ALORS:
ALORS: //
// Ne
Ne devrait
devrait pas
pas se
se produire.
produire.
RENVOYER
RENVOYER ERREUR
ERREUR
SINON:
SINON:
POUR
POUR AMI
AMI DANS
DANS VALEURS[0],
VALEURS[0], FAIRE:
FAIRE:
SI
SI AMI
AMI EGALEMENT
EGALEMENT PRESENT
PRESENT DANS
DANS VALEURS[1],
VALEURS[1], ALORS:
ALORS:
//
// Présent
Présent dans
dans les
les deux
deux listes
listes d'amis,
d'amis, on
on l'ajoute.
l'ajoute.
LISTE_AMIS_COMMUNS+=AMI
LISTE_AMIS_COMMUNS+=AMI
RENVOYER
RENVOYER LISTE_AMIS_COMMUNS
LISTE_AMIS_COMMUNS

Exemple – Graphe social
3-23

Après exécution de l'opération REDUCE pour les valeurs de chaque clef unique,
on obtiendra donc, pour une clef « A-B », les utilisateurs qui apparaissent dans
la liste des amis de A et dans la liste des amis de B. Autrement dit, on obtiendra
la liste des amis en commun des utilisateurs A et B. Le résultat:

"A-B":
"A-B": "C,
"C, D"
D"
"A-C": "B, D" On sait ainsi que A et B ont pour amis communs
"A-C": "B, D"
"A-D": "B, C" les utilisateurs C et D, ou encore que B et C ont
"A-D": "B, C"
"B-C": "A, D, E" pour amis communs les utilisateurs A, D et E.
"B-C": "A, D, E"
"B-D":
"B-D": "A,
"A, C,
C, E"
E"
"B-E":
"B-E": "C,
"C, D"
D"
"C-D":
"C-D": "A,
"A, B,
B, E"
E"
"C-E":
"C-E": "B,
"B, D"
D"
"D-E":
"D-E": "B,
"B, C"
C"

Mieux encore. un seul SELECT dans la base de données suffira pour obtenir la liste des amis en commun – avec un poids en traitement très faible pour le serveur. Ainsi. on a ainsi pu créer deux programmes très simples (nos programmes MAP et REDUCE) de quelques lignes de code seulement. notre traitement est parallélisable: même avec des dizaines de millions d'utilisateurs. lorsqu'un utilisateur visitera la page d'un autre utilisateur.Conclusion 3-24 En utilisant le modèle MapReduce. et de stocker les résultats dans une table. Pour notre réseau social. qui permettent d'effectuer un traitement somme toute assez complexe. le traitement sera effectué rapidement. . Pour aller plus vite. du moment qu'on a assez de machines au sein du cluster Hadoop. il suffira d'effectuer ce traitement toutes les nuits à heure fixe. il nous suffit de rajouter plus de machines.

4 HDFS .

Présentation 4-1 Pour stocker les données en entrée de nos tâches Hadoop. Ce mode d'intégration passe par le biais de ponts d'interconnexion.au même sens que les systèmes de fichiers FAT32. ainsi que les résultats de nos traitements. qui seront abordés plus loin dans le cours. NTFS ou encore Ext3FS. Remarque: Hadoop peut – et c'est le cas le plus fréquent – également communiquer directement avec une base de données (qu'elle soit « classique » comme MySQL ou PostGreSQL ou plus exotique comme MongoDB ou VoltDB). . à la différence qu'il est évidemment distribué. on va utiliser HDFS: Hadoop Distributed FileSystem Il s'agit du système de fichier standard de Hadoop .

HDFS va répliquer les données sur des racks différents. . ● Il est répliqué: si une des machines du cluster tombe en panne. HDFS peut aussi optimiser les transferts de données pour limiter la « distance » à parcourir pour la réplication (et donc les temps de transfert). même temporaire. ● Il est conscient du positionnement des serveurs sur les racks. pour être certain qu'une panne affectant un rack de serveurs entier (par exemple un incident d'alimentation) ne provoque pas non plus de perte de données. aucune donnée n'est perdue.Présentation 4-2 Les caractéristiques de HDFS: ● Il est distribué: les données sont réparties sur tout le cluster de machines. Par ailleurs.

si on a par exemple 6 fragments en entrée et 2 machines sur le cluster. le système de gestion des tâches de Hadoop. est en communication constante avec HDFS. qui distribue les fragments de données d'entrée au cluster pour l'opération MAP ou encore les couples (clef. et positionnera les fragments/distribuera les tâches de telle sortes que les machines y aient accès directement.valeur) pour l'opération REDUCE. . sans avoir besoin de les demander à une autre machine.Présentation 4-3 Par ailleurs. Ainsi. Il peut donc optimiser le positionnement des données à traiter de telle sorte qu'une machine puisse accéder aux données relatives à la tâche qu'elle doit effectuer localement. sans avoir à effectuer d’accès sur le réseau. Hadoop estimera que chacune des 2 machines traitera probablement 3 fragments chacune.

C'est ce serveur qui. etc. indiquer quels blocs sont contenus sur le DataNode.. par exemple. et ils sont en communication constante avec le NameNode pour recevoir de nouveaux blocs. qui stocke les informations relatives aux noms de fichiers. Il y a un seul NameNode dans tout le cluster Hadoop. les données sont divisées en blocs de 64KB (configurable). Il y a un DataNode pour chaque machine au sein du cluster. ● Le DataNode. va savoir que le fichier « livre_5321 » dans le répertoire « data_input » comporte 58 blocs de données. . qui stocke les blocs de données eux-mêmes.. L'implémentation de HDFS a son origine dans un whitepaper issu du département de recherche de Google (« The Google File System ». un système de fichier distribué conçu par Google.Architecture 4-4 HDFS repose sur deux serveurs (des daemons): ● Le NameNode. signaler des erreurs. et qui sait où ils se trouvent. Hadoop est inspiré de GFS. 2003). Par défaut.

Il va ensuite annoncer au NameNode: « Je souhaite stocker ce fichier au sein de HDFS. sous le nom page_livre. Par ailleurs.Écriture d'un fichier 4-5 Si on souhaite écrire un fichier au sein de HDFS. Mettons qu'on souhaite stocker le fichier page_livre. et le bloc 2 sur le DataNode numéro 1.txt sur HDFS. . Le client hadoop va alors contacter directement les DataNodes concernés et leur demander de stocker les deux blocs en question. Le programme va diviser le fichier en blocs de 64KB (ou autre. Le NameNode va alors indiquer au programme qu'il doit stocker le bloc 1 sur le DataNode numéro 3. les DataNodes s'occuperont – en informant le NameNode – de répliquer les données entre eux pour éviter toute perte de données. selon la configuration) – supposons qu'on ait ici 2 blocs. avec l'option fs. on va utiliser la commande principale de gestion de Hadoop: hadoop.txt ».

Écriture d'un fichier 4-6 .

En cas d'erreur/non réponse d'un des DataNode. on utilise là aussi le client Hadoop. il passe au suivant dans la liste fournie par le NameNode. le programme contactera les DataNodes directement et leur demandera de lui transmettre les blocs concernés. Le premier est disponible sur le DataNode 3 et 2. . Mettons qu'on souhaite lire le fichier page_livre.Lecture d'un fichier 4-7 Si on souhaite lire un fichier au sein de HDFS. Le client va contacter le NameNode.txt. Le NameNode lui répondra par exemple « Il est composé de deux blocs. Là aussi. et lui indiquer « Je souhaite lire le fichier page_livre. le second sur le DataNode 1 et 3 ».txt ».

Lecture d'un fichier 4-8 .

● hadoop fs -get /data_input/livre.txt Pour stocker le fichier livre.txt livre.txt Pour supprimer le fichier /data_input/livre.txt /data_input/livre. avec l'option fs. -cp. et est très simple à utiliser: ● hadoop fs -put livre.txt sur HDFS dans le répertoire /data_input.txt.. -rmr. . ● hadoop fs -mkdir /data_input Pour créer le répertoire /data_input ● hadoop fs -rm /data_input/livre. etc. -du.La commande hadoop fs 4-9 ● Comme indiqué plus haut.txt Pour obtenir le fichier /data_input/livre. Il réplique globalement les commandes systèmes standard Linux.txt d'autres commandes usuelles: -ls.. la commande permettant de stocker ou extraire des fichiers de HDFS est l'utilitaire console hadoop.txt de HDFS et le stocker dans le fichier local livre.

on devra extraire le ou les fichiers des résultats depuis HDFS. et les ré-importer dans la base de données. ● Hadoop réplique lui-même les données: les fichiers sont disponibles à tout moment sur plusieurs DataNodes. De même. ● Si les données proviennent d'une base de données (par exemple dans le cas de l'exemple des « amis en commun » vu précédemment). on sera obligé avant d'exécuter le traitement Hadoop d'extraire les données de la base via des requêtes SQL et de les stocker sur HDFS. .Remarques 4-10 ● La gestion du stockage est assurée par les daemons Hadoop – on a pas à se soucier d'où sont stockées les données. pour récupérer les résultats au sein d'une base de données. on a toujours accés aux données grâce à la réplication. et si une machine tombe en panne. C'est dans ce cas que les bridges de connexion aux bases de données évoquées plus haut prennent tout leur sens.

HDFS est optimisé pour des lecture concurrentes: écrire de manière concurrente est nettement moins performant. si la machine hébergeant le NameNode tombe en panne. Pour y accéder. le cluster est incapable d'accéder aux fichiers le temps que le problème soit corrigé. En conséquence. Une implémentation est en cours pour un basculement automatique. Remarque: ce n'est plus entièrement vrai – plusieurs projets FUSE existent désormais pour assurer des points de montage classiques. on est obligé de passer par l'utilitaire hadoop (ou par des APIs). ● Enfin. avec un BackupNameNode qui peut être basculé manuellement en cas de problème. . Ce problème est partiellement mitigé dans les versions récentes (beta) de Hadoop.Inconvénients 4-11 ● Le NameNode est unique – on ne peut pas en avoir plusieurs. ● Le système de fichier ne peut pas être monté de manière « classique ».

N'est pas conscient du positionnement par rapport aux racks. Peu performant. Par exemple: ● Amazon S3 filesystem: le système de fichier Amazon utilisé sur les solutions de cloud de amazon. Conscient des racks. Il existe également des alternatives à HDFS qui conservent une logique de systèmes de fichiers. . ● FTP: une alternative haut niveau qui transfère les blocs manuellement via FTP.com.Alternatives 4-12 On a indiqué plus haut qu'on peut également brancher Hadoop directement par le biais de ponts sur une base de données pour en extraire des données d'entrée/y stocker des résultats. ● HTTP/HTTPS en lecture seule. ● CloudStore: une alternative proposée par Kosmix.

5 L'architecture Hadoop .

Il y a un TaskTracker sur chaque machine du cluster.jar Java).Présentation 5-1 Comme pour HDFS. Il y a un seul JobTracker sur une seule machine du cluster Hadoop. ainsi que les données d'entrées (nom des fichiers stockés sur HDFS) et le répertoire où stocker les données de sortie (toujours sur HDFS). qui est en communication constante avec le JobTracker et va recevoir les opérations simples à effectuer (MAP/REDUCE) ainsi que les blocs de données correspondants (stockés sur HDFS). ● Le TaskTracker. Le JobTracker est en communication avec le NameNode de HDFS et sait donc où sont les données. . qui va directement recevoir la tâche à exécuter (un . la gestion des tâches de Hadoop se base sur deux serveurs (des daemons): ● Le JobTracker.

il peut facilement déterminer les meilleures machines auxquelles attribuer les sous-tâches (celles où les blocs de données correspondants sont stockés). on va donc stocker nos données d'entrée sur HDFS. créer un répertoire où Hadoop stockera les résultats sur HDFS. ● Pour effectuer un traitement Hadoop. et le .Présentation 5-2 ● Comme le JobTracker est conscient de la position des données (grâce au NameNode).jar lui-même au JobTracker: il s'occupera du reste (et notamment de transmettre les programmes MAP et REDUCE aux serveurs TaskTracker des machines du cluster).jar Java. . ● On soumettra alors le nom des fichiers d'entrée. le nom du répertoire des résultats. et compiler nos programmes MAP et REDUCE au sein d'un .

Présentation 5-3 .

Il va également soumettre le nom des fichiers d'entrée. Le JobTracker communique avec le NameNode HDFS pour savoir où se trouvent les blocs correspondant aux noms de fichiers donnés par le client. Le JobTracker.jar implémentant les opérations Map et Reduce. à partir de ces informations. détermine quels sont les nœuds TaskTracker les plus appropriés. 3. . c'est à dire ceux qui contiennent les données sur lesquelles travailler sur la même machine. et l'endroit où stocker les résultats.Le JobTracker 5-4 Le déroulement de l'exécution d'une tâche Hadoop suit les étapes suivantes du point de vue du JobTracker: 1. ou le plus proche possible (même rack/rack proche). 2. Le client (un outil Hadoop console) va soumettre le travail à effectuer au JobTracker: une archive java .

). Pour chaque « morceau » des données d'entrée. données incorrectes. il pourra même blacklister le TaskTracker concerné comme non-fiable dans certains cas. etc. le JobTracker envoie au TaskTracker sélectionné le travail à effectuer (MAP/REDUCE. Le JobTracker communique avec les nœuds TaskTracker en train d'exécuter les tâches. 5. marquer les données concernées comme invalides. demander au même TaskTracker de ré-essayer. le JobTracker considère la tâche comme ayant échouée et donne le même travail à effectuer à un autre TaskTracker. 6. . le TaskTracker va signaler au JobTracker que la tâche n'a pas pû être exécuté. Si aucun heartbeat n'est reçu dans une période donnée. Ils envoient régulièrement un « heartbeat ». code Java) et les blocs de données correspondant. un message signalant qu'ils travaillent toujours sur la sous-tâche reçue. etc. Le JobTracker va alors décider de la conduite à adopter: redonner la sous-tâche à un autre TaskTracker.Le JobTracker 5-5 4. Si par hasard une tâche échoue (erreur java.

on peut également obtenir à tout moment de la part du JobTracker des informations sur les tâches en train d'être effectuées: étape actuelle (MAP. etc. REDUCE). le JobTracker marque la tâche comme « effectuée ». etc. Par ailleurs.).Le JobTracker 5-6 7. Une fois que toutes les opérations envoyées aux TaskTracker (MAP + REDUCE) ont été effectuées et confirmées comme effectuées par tous les nœuds. pourcentage de complétion. l'obtention de ces informations. . SHUFFLE.jar. et d'une manière générale toutes les opérations liées à Hadoop s'effectuent avec le même unique client console vu précédemment: hadoop (avec d'autres options que l'option fs vu précédemment). La soumission du . TaskTracker ayant posé problème. Des informations détaillées sont disponibles (statistiques.

le TaskTracker va démarrer une nouvelle instance de Java avec le fichier . Ainsi.jar fourni par le JobTracker.Le TaskTracker 5-7 ● Le TaskTracker dispose d'un nombre de « slots » d'exécution. A chaque « slot » correspond une tâche exécutable (configurable). ces messages indiquent également le nombre de slots disponibles sur le TaskTracker concerné. REDUCE. en appelant l'opération correspondante. SHUFFLE) depuis le JobTracker. ● Lorsqu'il reçoit une nouvelle tâche à effectuer (MAP. ● Une fois la tâche démarrée. il enverra régulièrement au JobTracker ses messages heartbeats. une machine ayant par exemple un processeur à 8 cœurs pourrait avoir 16 slots d'opération configurés. . En dehors d'informer le JobTracker qu'il est toujours fonctionnels.

.Le TaskTracker 5-8 ● Lorsqu'une sous-tâche est terminée. le TaskTracker envoie un message au JobTracker pour l'en informer. que la tâche se soit bien déroulée ou non (il indique évidemment le résultat au JobTracker).

Cette machine particulière au sein du cluster (qui contient les deux « gestionnaires ». des résolutions aux problème sont prévues dans la roadmap Hadoop. le cluster tout entier ne peut plus effectuer de tâches. Les autres nœuds (contenant TaskTracker + DataNode) sont communément appelés nœuds esclaves (« slave node »). on place le JobTracker et le NameNode HDFS sur la même machine (une machine plus puissante que les autres). . Là aussi. ● Généralement. il n'y a qu'un seul JobTracker et s'il tombe en panne. sans y placer de TaskTracker/DataNode HDFS pour limiter la charge. de tâches et de fichiers) est communément appelée le nœud maître (« Master Node »). mais pas encore disponibles.Remarques 5-9 ● De manière similaire au NameNode de HDFS.

. tout changement de configuration Hadoop peut s'effectuer facilement simplement en changeant la configuration sur la machine où sont situés les serveurs NameNode et JobTracker: ils répliquent les changement de configuration sur tout le cluster automatiquement. ● Enfin. le « client » qui envoie la tâche au JobTracker initialement peut être exécuté sur n'importe quelle machine du cluster – comme les TaskTracker sont présents sur la machine. ● La même remarque est valable pour l'accés au système de fichiers: les DataNodes indiquent au client comment accéder au NameNode.Remarques 5-10 ● Même si le JobTracker est situé sur une seule machine. ils indiquent au client comment joindre le JobTracker.

Architecture générale 5-11 .

6 Utilisation Hadoop .

JobTracker offre une interface web également. etc. qui permet de lire les blocs des fichiers stockés (ou les fichiers stockés dans leur intégralité). de voir l'état des nodes TaskTracker et si oui ou non ils sont parvenus à exécuter les tâches qui leur ont été affecté. appelée par l'interface de NameNode.Interfaces utilisateur 6-1 Hadoop est essentiellement piloté par des outils consoles (CLI). ● Enfin. ● DataNode expose une interface web similaire. d'avoir des statistiques sur l'exécution des tâches. mais quelques interfaces utilisateurs sont mises à disposition de l'utilisateur: ● NameNode expose une interface web (via un serveur web intégré) qui permet de parcourir les fichiers et répertoires stockés sur HDFS. qui permet de consulter les tâches en cours. .

par exemple le projet Hue (Open Source). . des interfaces utilisateur tierces ont été développées pour simplifier l'utilisation de Hadoop.Remarques 6-2 Par ailleurs.

.Remarques 6-3 Il est néanmoins vital de savoir de servir des commandes de base (qui pourraient par exemple être appelées depuis des scripts shell).

● Une classe MAP (qui effectuera l'opération MAP). Un programme Hadoop se compile au sein d'un . . Les tâches MAP/REDUCE sont donc implémentables par le biais d'interfaces Java (il existe cependant des wrappers très simples permettant d'implémenter ses tâches dans n'importe quel langage). Pour développer un programme Hadoop. ● Une classe REDUCE (qui effectuera l'opération REDUCE).Programmation Hadoop 6-4 Comme indiqué précédemment. des classes se chargeant des opérations MAP et REDUCE. Cette classe se chargera d'informer Hadoop des types de données clef/valeur utilisées. on va créer trois classes distinctes: ● Une classe dite « Driver » qui contient la fonction main du programme. et des fichiers HDFS à utiliser pour les entrées/sorties.jar. Hadoop est développé en Java.

On va également récupérer les arguments supplémentaires pour s'en servir. qui est nécessaire pour permettre à Hadoop d'obtenir la configuration générale du cluster. on va effectuer les opérations suivantes: ● Créer un objet Configuration de Hadoop. on souhaite que l'utilisateur puisse préciser le nom du fichier d'entrée et le nom du répertoire de sortie HDFS pour nos tâches Hadoop grâce à la ligne de commande. . L'objet en question pourrait aussi nous permettre de récupérer nous-même des options de configuration qui nous intéressent. Au sein du main() en question. ● Permettre à Hadoop de récupérer d'éventuels arguments génériques disponibles sur la ligne de commande (par exemple le nom du package de la tâche à exécuter si le .jar en contient plusieurs).Programmation Hadoop – Classe Driver 6-5 La classe Driver contient le main de notre programme.

MAP et REDUCE. ● Informer Hadoop des fichiers d'entrée/sortie pour notre tâche sur HDFS. qui désigne une tâche Hadoop. utiliser l'objet Job créé précédemment pour déclencher le lancement de la tâche via le cluster Hadoop. ● Utiliser cet objet Job pour informer Hadoop du nom de nos classes Driver. On reprend ici l'exemple du compteur d’occurrences de mots décrit précédemment.valeur) MAP et REDUCE. .Programmation Hadoop – Classe Driver 6-6 ● Créer un nouvel objet Hadoop Job. ● Enfin. ● Utiliser le même objet pour informer Hadoop des types de données utilisés dans notre programme pour les couples (clef.

● Avant toute chose. Configuration().apache.conf.Configuration .Programmation Hadoop – Classe Driver 6-7 ● Le prototype de notre fonction main(): public public static static void void main(String[] main(String[] args) args) throws throws Exception Exception On se sert de args pour récupérer les arguments de la ligne de commande. Configuration Configuration conf=new conf=new Configuration(). Hadoop. on créé dans notre main un nouvel objet Configuration Hadoop: // // Créé Créé un un objet objet de de configuration configuration Hadoop. Le package à importer est: org. Plusieurs fonctions Hadoop appelées au sein du main sont susceptibles de déclencher des exceptions – on l'indique donc lors de la déclaration.hadoop.

On utilise pour ce faire un objet Hadoop GenericOptionsParser. args).GenericOptionsParser La fonction getRemainingArgs() de notre objet nous permet par ailleurs de récupérer les arguments non exploités par Hadoop au sein d'un tableau ourArgs – ce qui nous permettra de les utiliser par la suite.getRemainingArgs(). dont le package est: org. .apache. On peut ainsi rendre paramétrable facilement une tâche Hadoop lors de l'exécution.getRemainingArgs(). GenericOptionsParser(conf.hadoop.Programmation Hadoop – Classe Driver 6-8 ● Ensuite.util. par le biais des arguments de la ligne de commande. on passe à Hadoop les arguments de la ligne de commande pour lui permettre de récupérer ceux qui sont susceptibles de lui être adressés: String[] String[] ourArgs=new ourArgs=new GenericOptionsParser(conf. args).

Map et Reduce de notre programme Hadoop.mapreduce.getInstance(conf. il s'agira respectivement des classes WCount.Programmation Hadoop – Classe Driver 6-9 ● On créé ensuite un nouvel objet Hadoop Job: Job Job job=Job. ainsi qu'une description textuelle courte du programme Hadoop. v1.0").0"). .mbds. Dans notre cas. job=Job. il faut indiquer à Hadoop – par le biais de l'objet Job nouvellement créé – quelles sont les classes Driver. ● Ensuite.apache.Job On passe au constructeur notre objet Configuration. WCountMap et WCountReduce du package org.getInstance(conf.wordcount. Le package à importer est le suivant: org. "Compteur "Compteur de de mots mots v1.hadoop.hadoop.

on souhaite utiliser des chaînes de caractères pour les clefs (nos mots) et des entiers pour nos occurrences. job. Remarque: on ne doit pas utiliser les types classiques Int et String Java pour désigner nos types.class).setJarByClass(WCount. ● Il faut ensuite indiquer à Hadoop quels sont les types de données que l'ont souhaite utiliser pour les couples (clef.class). job.class). job. les classes IntWritable et Text.class). job. .setMapperClass(WCountMap.setReducerClass(WCountReduce.Programmation Hadoop – Classe Driver 6-10 On utilise pour ce faire les fonctions suivantes: job.setJarByClass(WCount. Dans le cas de notre compteur d’occurrences de mots.class).setReducerClass(WCountReduce. mais des classes qui leur correspondent et qui sont propres à Hadoop.setMapperClass(WCountMap. job.class). Dans notre cas.valeur) de nos opérations map et reduce.

apache. on doit indiquer où se situent nos données d'entrée et de sortie dans HDFS. ● Ensuite. . On utilise pour ce faire les classes Hadoop FileInputFormat et FileOutputFormat.io.class).class).hadoop.setOutputValueClass(IntWritable. Ces classes sont implémentées suivant un design pattern Singleton – il n'est pas nécessaire de les instancier dans le cas qui nous intéresse (dans des cas plus complexe.apache. job. on étendra parfois la classe en question dans une nouvelle classe qui nous est propre).hadoop.setOutputValueClass(IntWritable. job.Programmation Hadoop – Classe Driver 6-11 job. job.apache.io.IntWritable et org.setOutputKeyClass(Text.class).class).hadoop.*.io. Les packages des types en question: org.Text Il en existe beaucoup d'autres dans org.setOutputKeyClass(Text.

Path(ourArgs[0])).apache.addInputPath(job.lib.mapreduce.FileOutputFormat et org.lib.mapreduce. new new Path(ourArgs[1])). Les packages à utiliser: org.setOutputPath(job.hadoop. Path(ourArgs[1])).FileInputFormat org.fs.input. FileOutputFormat. Le code est ici simplifié – on devrait en théorie vérifier la taille du tableau ourArgs pour éviter d'éventuelles erreurs.Path On utilise les arguments restants après ceux qu'a utilisé Hadoop.setOutputPath(job. new new Path(ourArgs[0])).apache. FileOutputFormat.apache.Programmation Hadoop – Classe Driver 6-12 On procède de la manière suivante: FileInputFormat.addInputPath(job. .hadoop.hadoop.input. FileInputFormat.

exit(-1). .Programmation Hadoop – Classe Driver 6-13 Enfin.waitForCompletion(true)) if(job. System. il reste à lancer l'exécution de la tâche par le biais du cluster Hadoop.exit(-1).exit(0). System. La fonction waitForCompletion de l'objet job va exécuter la tâche et attendre la fin de son exécution. On procède ainsi: if(job. Elle prend un argument: un booléen indiquant à Hadoop si oui ou non il doit donner des indications sur la progression de l'exécution à l'utilisateur sur la sortie standard (stdout). ici. et -1 en cas de problème (codes de retour unix standards). Elle renvoie true en cas de succés. on terminera l'exécution du programme en renvoyant 0 si tout s'est bien passé.waitForCompletion(true)) System. System.exit(0).

IntWritable. org.hadoop.apache.input. org. import import org.hadoop.mbds.Configuration.mapreduce.apache.apache.Path.Job.apache.apache. org.mapreduce.apache.hadoop. org.wordcount.lib.conf.io.hadoop.mbds.lib.apache.hadoop. public public class class WCount WCount {{ .io.lib.IntWritable.hadoop.hadoop.mapreduce.FileInputFormat. Hadoop).input. // // Notre Notre classe classe Driver Driver (contient (contient le le main main du du programme programme Hadoop).io.util.Programmation Hadoop – Classe Driver 6-14 Le code complet de notre classe Driver: package package org.Text. org. org.mapreduce.GenericOptionsParser.conf.hadoop.wordcount.apache. org.mapreduce.hadoop.Text.FileOutputFormat.output.FileOutputFormat.hadoop. import import org.apache.fs.output. org.io.apache.Path. import import org.hadoop.fs.GenericOptionsParser.Configuration.apache.hadoop. import import org.Job.apache.hadoop.apache. org.hadoop.hadoop.lib.util.apache.apache.hadoop.apache.mapreduce. import import org. import import org.hadoop. import import org. import import org.FileInputFormat.hadoop.

GenericOptionsParser(conf. // // récupère récupère les les arguments arguments restants restants dans dans ourArgs. Configuration Configuration conf=new conf=new Configuration().getInstance(conf. programme. Configuration(). String[] String[] ourArgs=new ourArgs=new GenericOptionsParser(conf. . Hadoop. On On // // fourni fourni la la configuration configuration Hadoop Hadoop ainsi ainsi qu'une qu'une description description // // textuelle textuelle de de la la tâche. tâche. args).getInstance(conf. v1. // // Permet Permet àà Hadoop Hadoop de de lire lire ses ses arguments arguments génériques. // // Obtient Obtient un un nouvel nouvel objet objet Job: Job: une une tâche tâche Hadoop.0"). "Compteur "Compteur de de mots mots v1. ourArgs.getRemainingArgs().Programmation Hadoop – Classe Driver 6-15 // // Le Le main main du du programme.getRemainingArgs(). args). Job Job job=Job. Hadoop. génériques.0"). job=Job. public public static static void void main(String[] main(String[] args) args) throws throws Exception Exception {{ // // Créé Créé un un object object de de configuration configuration Hadoop.

setMapperClass(WCountMap. FileOutputFormat. newnew Path(ourArgs[0])).setOutputKeyClass(Text. // // Défini Défini types types clefs/valeurs clefs/valeurs de de notre notre programme programme Hadoop. Path(ourArgs[0])).class).class). job.Programmation Hadoop – Classe Driver 6-16 // // Défini Défini les les classes classes driver. job. new new Path(ourArgs[1])).setOutputPath(job. On On se se sert sert du du premier premier et et du du // // deuxième deuxième argument argument restants restants pour pour permettre permettre àà // // l'utilisateur l'utilisateur de de les les spécifier spécifier lors lors de de l'exécution. .setReducerClass(WCountReduce.setMapperClass(WCountMap.class). map map et et reduce.class). FileInputFormat. reduce. résultats. l'exécution. driver. job.class). Hadoop.setJarByClass(WCount. job.class). job.addInputPath(job. job. job.class).addInputPath(job.class). job.setOutputValueClass(IntWritable.setReducerClass(WCountReduce. FileOutputFormat.setOutputValueClass(IntWritable.setOutputKeyClass(Text. job. // // Défini Défini les les fichiers fichiers d'entrée d'entrée du du programme programme et et le le // // répertoire répertoire des des résultats.class).setOutputPath(job. job.class). FileInputFormat.setJarByClass(WCount. Path(ourArgs[1])).

System. }} }} .waitForCompletion(true)) if(job.exit(-1). System. Sinon.Programmation Hadoop – Classe Driver 6-17 // // On On lance lance la la tâche tâche Hadoop. if(job. Hadoop. correctement.exit(0). Si Si elle elle s'est s'est effectuée effectuée // // correctement. System.exit(-1).exit(0). on on renvoie renvoie 0. on on renvoie renvoie -1.waitForCompletion(true)) System. -1. Sinon. 0.

auquel cas notre opération map recevra en entrée des couples (clef.mapreduce. Elle doit étendre la classe Hadoop org. nous n'utiliserons pas cette possibilité. comme la possibilité d'effectuer plusieurs opérations MAP les unes à la suite des autres. Le type keyin est notamment utile lorsqu'on utilise des fonctionnalités plus avancées. .apache.Programmation Hadoop – Classe MAP 6-18 La classe MAP va être en charge de l'opération MAP de notre programme.hadoop. Dans notre cas. ● Un type keyout: le type de clef de sortie.valeur). on utilisera donc le type Java Object comme type keyin. ● Un type valuein: le type de valeur d'entrée. Il s'agit d'une classe générique qui se paramétrise avec quatre types: ● Un type keyin: le type de clef d'entrée. ● Un type valueout: le type de valeur de sortie.Mapper.

on utilise les types Hadoop et non les types natifs Java (Int et String). Text. IntWritable> IntWritable> On utilise ici comme types: ● Text pour le type valuein. notre classe Map sera déclarée ainsi: public public class class WCountMap WCountMap extends extends Mapper<Object. ● IntWritable pour le type valueout. Ici aussi. puisque notre valeur pour les couples (clef. puisque notre valeur de clef pour les couples (clef.valeur) de la fonction Map est un entier (le nombre d'occurences). puisque notre valeur d'entrée à la fonction Map est une chaîne de caractères (une ligne de texte).Programmation Hadoop – Classe MAP 6-19 Dans notre exemple. . Mapper<Object. Text. Text. ● Text pour le type keyout. Text.valeur) de la fonction Map est également une chaîne de caractères (le mot dont on compte les occurrences).

et un Context Java qui représente un handle Hadoop et nous permettra de retourner les couples (clef. key. Le prototype de notre fonction map: protected protected void void map(Object map(Object key. IOException. .Programmation Hadoop – Classe MAP 6-20 Au sein de la classe Mapper. C'est la seule qu'on doit absolument implémenter. InterruptedException InterruptedException Comme pour la fonction main. Elle prends trois arguments: la clef d'entrée keyin (qu'on ignore dans notre cas). Text Text value. value. la valeur d'entrée valuein (la ligne de texte dont on souhaite compter les mots). la fonction map appellera des fonctions susceptibles de déclencher des exceptions (notamment concernant l'interruption de l'exécution Hadoop ou des problèmes d’accès HDFS) – on le précise donc dans sa déclaration.valeur) résultant de notre opération Map. c'est la fonction map qui va s'occuper d'effectuer la tâche MAP. Context Context context) context) throws throws IOException.

une fois pour chacun des couples (clef. context.valeur) qu'on souhaite renvoyer. .Programmation Hadoop – Classe MAP 6-21 Au sein de la méthode map. Ce couple devra avoir pour clef le mot en question.write("ciel". 1).write("ciel". et renvoyer un couple (clef.valeur) pour chacun des mots. Par exemple: context. Elle peut être appelée autant de fois que nécessaire. 1). on utilise la fonction write de notre objet Context. Il faut évidemment que la clef et la valeur renvoyées ainsi correspondent aux types keyout et valueout de notre classe Mapper. Dans la fonction map.valeur). et pour valeur l'entier « 1 ». afin d'indiquer à Hadoop qu'on souhaite renvoyer un couple (clef. on va donc effectuer la tâche MAP de notre programme MAP/REDUCE. la fonction devra parcourir la ligne de texte fournie en entrée. Dans le cadre de notre exemple.

Job.hadoop. . org.util.hadoop. Mapper<Object. // // Notre Notre classe classe MAP.io.IntWritable.hadoop.IOException.StringTokenizer.mbds. Text.apache.io.io. 1.io.Text. IntWritable(1). import import java. org. org.hadoop. Text.mbds.wordcount. private private static static final final IntWritable IntWritable ONE=new ONE=new IntWritable(1). import import java. MAP. IntWritable> IntWritable> {{ // // IntWritable IntWritable contant contant de de valeur valeur 1. Text.Mapper.mapreduce.hadoop.apache. org. Text.apache.util. import import org. import import org. import import org.apache.io.hadoop.mapreduce.IOException. java.apache.apache.Job.IntWritable.Mapper.mapreduce.Programmation Hadoop – Classe MAP 6-22 Notre classe Map d'exemple en intégralité: package package org.wordcount.hadoop. public public class class WCountMap WCountMap extends extends Mapper<Object.Text.apache. java.apache.hadoop. org.io. import import org.hadoop.mapreduce.hadoop.StringTokenizer.

protected protected void void map(Object map(Object offset. context. IOException. offset. Text Text value.toString(). context.write(word.nextToken()). Text(tok. ONE). elle-même. value. ONE).nextToken()).write(word.valeur): le le mot mot courant courant suivi suivi // // de de la la valeur valeur 11 (définie (définie dans dans la la constante constante ONE). // // On On renvoie renvoie notre notre couple couple (clef.toString().hasMoreTokens()) while(tok. MAP.Programmation Hadoop – Classe MAP 6-23 // // La La fonction fonction MAP MAP elle-même. StringTokenizer(value. StringTokenizer StringTokenizer tok=new tok=new StringTokenizer(value.valeur): (clef. InterruptedException InterruptedException {{ // // Un Un StringTokenizer StringTokenizer va va nous nous permettre permettre de de parcourir parcourir chacun chacun des des // // mots mots de de la la ligne ligne qui qui est est passée passée àà notre notre opération opération MAP. ONE). Context Context context) context) throws throws IOException. }} }} }} . "). while(tok. "" ").hasMoreTokens()) {{ Text Text word=new word=new Text(tok.

Il s'agit là aussi d'une classe générique qui se paramétrise avec les mêmes quatre types que pour la classe Mapper: keyin. Elle doit étendre la classe Hadoop org. on recevra en entrée une valeur unique pour la clef. keyout et valueout.hadoop.apache. associée à toutes les valeurs pour la clef en question. suivi de toutes les valeurs qui ont été rencontrées à la sortie de l'opération MAP pour la clef « ciel » (par exemple cinq fois la valeur « 1 » si le mot « ciel » était présent cinq fois dans notre texte d'exemple). valuein.mapreduce. Dans le cas du compteur d’occurrences de mots.Programmation Hadoop – Classe REDUCE 6-24 La classe REDUCE va être en charge de l'opération REDUCE de notre programme.Reducer. . par exemple « ciel ». On rappelle que l'opération REDUCE recevra en entrée une clef unique.

On utilise ici Text – on renverra le nombre total d’occurrences pour le mot concerné sous la forme d'une chaîne de caractères (on pourrait également utiliser IntWritable ici). IntWritable. ● Text pour valueout: le type de valeur de sortie. un entier). ● IntWritable pour valuein: le type de nos valeurs associées à cette clef (le nombre d'occurences. Nous ne modifierons pas la clef. . Text. on utilise les types de données propres à Hadoop.Programmation Hadoop – Classe REDUCE 6-25 Dans notre exemple. il s'agira toujours du mot unique concerné – on utilise donc Text. Text> Text> On utilise pour chacun des types paramétrables: ● Text pour keyin: il s'agit de notre clef unique d'entrée – le mot concerné. ● Text pour keyout: le type de clef de sortie. Là aussi. IntWritable. Reducer<Text. la classe Reduce sera définie comme suit: public public class class WCountReduce WCountReduce extends extends Reducer<Text. Text.

Programmation Hadoop – Classe REDUCE
6-26

Au sein de la classe Reducer, c'est la fonction reduce qui va effectuer
l'opération REDUCE. C'est la seule qu'on doive implémenter.

Elle prends trois arguments: la clef concernée, un Iterable java (une liste) de
toutes les valeurs qui lui sont associées et qui ont été renvoyées par
l'opération MAP, et enfin un objet Context java similaire à celui de la fonction
map de la classe Mapper, et qui nous permettra de renvoyer notre valeur finale,
associée à la clef.

Dans notre exemple, la déclaration de la fonction reduce:
public
public void
void reduce(Text
reduce(Text key,
key, Iterable<IntWritable>
Iterable<IntWritable> values,
values, Context
Context context)
context)
throws
throws IOException,
IOException, InterruptedException
InterruptedException

Comme pour map, la fonction fait appel à des fonctions Hadoop susceptibles
de provoquer des exceptions – on l'indique ici aussi.

Programmation Hadoop – Classe REDUCE
6-27

Au sein de la fonction Reduce, on pourra renvoyer un couple (clef;valeur) en
résultat exactement de la même manière que pour la fonction map, par le biais
d'un appel à la fonction write de notre objet Context.

Par exemple:
context.write("ciel",
context.write("ciel", "5
"5 occurences");
occurences");

Contrairement à la fonction map, en revanche, on cherche à ne produire qu'une
et une seule valeur de retour pour la clef concernée. On n'appellera donc la
fonction write qu'une seule fois.

Remarque: en théorie et dans des cas plus complexes, on pourrait là aussi
appeler la fonction à plusieurs reprises, pour renvoyer plusieurs couples
(clef;valeur). En revanche, Hadoop n'appliquera aucun traitement dessus et les
considérera simplement comme plusieurs résultats finals.

Programmation Hadoop – Classe REDUCE
6-28

Notre classe Reduce d'exemple en intégralité:
package
package org.mbds.hadoop.wordcount;
org.mbds.hadoop.wordcount;

import
import org.apache.hadoop.io.Text;
org.apache.hadoop.io.Text;
import
import org.apache.hadoop.io.IntWritable;
org.apache.hadoop.io.IntWritable;
import
import org.apache.hadoop.mapreduce.Reducer;
org.apache.hadoop.mapreduce.Reducer;
import
import java.util.Iterator;
java.util.Iterator;
import
import java.io.IOException;
java.io.IOException;

//
// Notre
Notre classe
classe REDUCE
REDUCE -- paramétrée
paramétrée avec
avec un
un type
type Text
Text pour
pour la
la clef,
clef, un
un
//
// type de valeur IntWritable, et un type de retour (le retour final de
type de valeur IntWritable, et un type de retour (le retour final de
//
// la
la fonction
fonction Reduce)
Reduce) Text.
Text.
public
public class WCountReduce extends
class WCountReduce extends Reducer<Text,
Reducer<Text, IntWritable,
IntWritable, Text,
Text, Text>
Text>
{{

apache. context) org.IntWritable.hadoop.util.io.IOException.hadoop.next()..iterator(). key. i=values.Iterator. import // // Pour Pour parcourir parcourir toutes import java.apache. IntWritable.valeur) leText. org. Iterator<IntWritable> Iterator<IntWritable> i=values. org. import throws throws IOException.. Text> Text> {{ context.Reducer.")). // type de valeur et un IntWritable.paramétrée paramétrée chaque Pour avec un valeur.mapreduce. // // Notre Notre total total pour pour le le mot mot concerné.io.util. occurences.get().on l'ajoute retour (le l'ajoute de retour au total. IntWritable. elle-même. clef.io.. au et WCountReduce WCountReduce format format Text. Text.hasNext()) // // Pour REDUCE -.wordcount. public org.mbds.mapreduce.IntWritable.apache. pour lala clef. et un type // type..hadoop.hadoop. retour final final de de // // la // // On On renvoie la fonction fonction Reduce) renvoie Reduce) le couple couple (clef. count+=i. concerné. import Context Context context) import org.hadoop. toutes lesles valeurs valeurs associées associées àà la la clef clef fournie. un un // // Iterable Iterable de de toutes toutes les les valeurs valeurs quiqui sont sont associées associées àà lala clef clef en en // // question. package package et et le le contexte contexte Hadoop org.io. retour (le au total. IOException. import import java.mbds. total. org.Reducer. int int count=0.on de // .")).Text. Text.apache.iterator(). (clef.next().hadoop. count=0.IOException.get(). context.write(key.apache. // renvoyer le résultat à Hadoop). InterruptedException import org.Iterator. Reducer<Text. d'exemple Les Les arguments: arguments: la en intégralité: la clef clef key. valeur IntWritable.Programmation Hadoop – Classe REDUCE 6-29 // La La fonction //classe Notre ReduceREDUCE fonction REDUCE elle-même.hadoop. Text. // // Notre Notre while(i.. public import import void void reduce(Text reduce(Text key.hasNext()) classe classe REDUCE while(i. Hadoop (un (un handle handle quiqui nous nous permet permet de de // renvoyer le résultat à Hadoop). new new Text(count+" Text(count+" occurences. InterruptedException {{ java.key.. Iterable<IntWritable> Iterable<IntWritable> values..Text..hadoop.io.apache. Text. fournie. un chaque avec type type Text Text pour valeur.valeur) constitué constitué de de notre notre clef clef key key public // // et public class class du du total. }} }} . au extends extends Reducer<Text. java.wordcount. un un // type de count+=i. question.io.write(key.values.

implémenter sa propre classe FileInputFormat pour des traitements plus complexes sur les données d'entrée. ● Enfin. la distribution Hadoop comprend de nombreux exemples de programmes Map/Reduce. se référer à la documentation de l'API Hadoop: https://hadoop. ● Même si l'API change régulièrement.. mis à jour afin d'utiliser l'API la plus récente.org/docs/ . d'une manière générale. Par ailleurs.Remarques 6-30 ● Hadoop est une plate-forme récente.. il existe de nombreuses autres possibilités offertes par l'API: extraire des paramètres de configurations globaux du cluster. Il faut toujours s'informer sur les dernières modifications afin de rester à jour des dernières modifications.apache. les anciennes manières de procéder restent généralement disponibles pendant plusieurs mois après une mise à jour. en développement constant: l'API Java change régulièrement. etc.

jar ● hadoop-mapreduce-client-common. .jar. il suffit d'utiliser le compilateur javac comme pour tout autre programme Java. sous la forme de fichiers . Elles sont disponibles avec les distributions de Hadoop. à inclure en fonction des besoins.Compilation 6-31 ● Pour compiler un programme Hadoop.jar … il en existe d'autres pour des cas d'utilisation plus complexes. ● Il est cependant nécessaire d'inclure les librairies appropriées. après compilation.jar ● hadoop-mapreduce-client-core. Les librairies principales à utiliser: ● hadoop-common. on package le programme Hadoop à l'intérieur d'un fichier . ● Enfin.jar ● commons-cli.jar pour son exécution.

WCount org.hadoop.jar wcount_mbds.jar org. .hadoop.mbds.txt /results /results … la commande demandera à Hadoop d'exécuter le programme Hadoop de la classe Driver org.hadoop.wordcount du fichier wcount_mbds. on utilise là aussi le programme en ligne de commande hadoop.wordcount. et stocker les résultats dans le répertoire « results » sur HDFS.jar.Exécution 6-32 ● Pour exécuter un programme Hadoop. La syntaxe est la suivante: hadoop hadoop jar jar [JAR [JAR FILE] FILE] [DRIVER [DRIVER CLASS] CLASS] [PARAMETERS] [PARAMETERS] Par exemple pour notre compteur d’occurrences de mots: hadoop hadoop jar jar wcount_mbds.WCount \ input/poeme.wordcount.mbds. en lui indiquant qu'il doit lire son texte dans le fichier « input/poeme.txt » sur HDFS.mbds.txt input/poeme.

On peut également demander à Hadoop d'effectuer uniquement les opérations MAP (par exemple pour du debug).Remarques 6-33 ● Hadoop stocke les résultats dans une série de fichiers part-r-XXXX. Le nom du fichier est paramétrable dans la configuration Hadoop. Le « r » au sein du nom signifie « Reduce ». ● Un fichier _SUCCESS (vide) est également créé dans le répertoire des résultats en cas de succès. ● On a un fichier « part-r » par opération REDUCE exécutée. qui seront en conséquences distribués sur tout le cluster HDFS. Cela permet de contrôler que tout s'est bien passé rapidement (avec un simple hadoop fs -ls sur HDFS). où XXXX est un compteur incrémental. auquel cas on aura une série de fichiers « part-m ». L'idée est ici de stocker de grandes quantités de résultats dans de nombreux fichiers différents. .

Ce .jar qui est capable de prendre en argument des programmes ou scripts définissant les tâches MAP et REDUCE. où VERSION est la version de Hadoop concernée. Il s'agit en réalité d'un programme Hadoop Java « classique ». Il s'agit d'un . mais qui appelle les tâches MAP et REDUCE par le biais du système. par exemple en C. Pour ce faire. .jar est disponible dans le répertoire d'installation Hadoop et porte le nom hadoop-streaming-VERSION. et d'exécuter ainsi la tâche spécifiée sur le cluster. en Python ou encore en bash (shell scripting). ainsi que les fichiers d'entrée et le répertoire de sortie HDFS.Autres langages: streaming 6-34 Au delà de Java. un outil est distribué avec Hadoop: streaming. Hadoop permet également l'exécution d'un programme Hadoop écrit dans d'autres langages.jar.

on aura une série de lignes: nos données d'entrée (par exemple dans le cas du compteur d’occurrences de mots. les données d'entrée doivent être lues sur l'entrée standard (stdin) et les données de sorties doivent être envoyées sur la sortie standard (stdout). des lignes de notre texte).valeur).Streaming .valeur) au format: CLEF[TABULATION]VALEUR … avec une ligne distincte pour chaque (clef. . ● En sortie du script ou programme MAP. on doit écrire sur stdout notre série de couples (clef.MAP 6-35 Lorsqu'on développe un programme MAP Hadoop dans un autre langage pour son utilisation avec l'outil streaming. ● En entrée du script ou programme MAP.

● En entrée du script ou programme REDUCE. on écrira évidemment un seul couple (clef. .valeur) par clef distincte. on doit écrire des couples (clef.valeur) au format: CLEF[TABULATION]VALEUR Les couples seront triés par clef distincte. les données d'entrée et de sortie doivent être là aussi lues/écrites sur stdin et stdout (respectivement). Par ailleurs. on est susceptible d'avoir des clefs différentes au sein d'une seule et même exécution du programme reduce ! ● En sortie du script ou programme REDUCE. on aura une série de lignes: des couples (clef.Streaming . et la clef répétée à chaque fois.valeur).REDUCE 6-36 Lorsqu'on développe un programme REDUCE Hadoop dans un autre langage pour son utilisation avec l'outil streaming. Remarque: d'ordinaire. toujours au format CLEF[TABULATION]VALEUR.

clef. for for line line in in sys.valeur: le le mot mot comme comme clef. la clef et la valeur. (word.stdin: ## Supprimer Supprimer les les espaces espaces autour autour de de la la ligne. ligne. l'entier l'entier "1" "1" comme comme ## valeur. valeur. ## On On renvoie renvoie chaque chaque couple couple sur sur une une ligne. 1) 1) .Exemple (Python) 6-37 Occurences de mots version Python – opération MAP: import import sys sys ## Pour Pour chaque chaque ligne ligne d'entrée.stdin: sys.strip() ## Pour Pour chaque chaque mot mot de de la la ligne. words=line. avec avec une une tabulation tabulation entre entre ## la clef et la valeur. ligne. d'entrée.split() for for word word in in words: words: ## Renvoyer Renvoyer couple couple clef. line=line.split() words=line.valeur: clef. print print "%s\t%d" "%s\t%d" %% (word.strip() line=line. ligne.

valeur. int. ## Notre Notre total total pour pour le le mot mot courant. lastword=None lastword=None ## Pour Pour chaque chaque ligne ligne d'entrée. ligne. word. 1) count=int(count) count=int(count) .stdin: ## Supprimer Supprimer les les espaces espaces autour autour de de la la ligne. 1) word. courant. convertir convertir la la valeur valeur en en int.split('\t'. rencontré. for for line line in in sys. d'entrée.Exemple (Python) 6-38 Occurences de mots version Python – opération REDUCE: import import sys sys total=0.stdin: sys.split('\t'. count=line. line=line. count=line. ## Contient Contient le le dernier dernier mot mot rencontré. total=0.strip() ## Récupérer Récupérer la la clef clef et et la la valeur.strip() line=line.

(clef. total=0.Exemple (Python) 6-39 ## On On change change de de mot mot (test (test nécessaire nécessaire parce parce qu'on qu'on est est susceptible susceptible d'avoir d'avoir ## en en entrée plusieurs clefs distinctes différentes pour une seule et entrée plusieurs clefs distinctes différentes pour une seule et ## même même exécution exécution du du programme programme –– Hadoop Hadoop triera triera les les couples couples par par clef clef ## distincte). print print "%s\t%d "%s\t%d occurences" occurences" %% (lastword. lastword=word lastword=word total=total+count total=total+count ## Ajouter Ajouter la la valeur valeur au au total total ## Ecrire Ecrire le le dernier dernier couple couple (clef.valeur). distincte). total) total) total=0. (lastword. if if word!=lastword word!=lastword and and lastword!=None: lastword!=None: print print "%s\t%d "%s\t%d occurences" occurences" %% (lastword.valeur). (lastword. total) total) .

line. while while read read line. for for word word in in $line. ligne. Pour chaque mot de la ligne. valeur. d'entrée. do do ## Supprimer Supprimer les espaces les espaces autour autour de de la la ligne.Exemple (Bash) 6-40 Occurences de mots version Bash – opération MAP: ## Pour Pour chaque chaque ligne ligne d'entrée. line=$(echo line=$(echo "$line"|sed "$line"|sed 's/^\s*\(. do do ## Renvoyer Renvoyer couple couple clef. $line. ligne. ## On On renvoie renvoie chaque chaque couple couple sur sur une une ligne.valeur: le le mot mot comme comme clef. l'entier l'entier "1" "1" comme comme ## valeur. valeur.\)\s*$//') ## Pour chaque mot de la ligne.valeur: clef. clef.\)\s*$//') 's/^\s*\(. avec avec une une tabulation tabulation entre entre ## la la clef clef et et la la valeur. echo echo -e -e $word"\t"1 $word"\t"1 done done done done .

ligne. $2}'). while while read read line. line=$(echo line=$(echo "$line"|sed "$line"|sed 's/^\s*\(. $1}'). d'entrée. clef clef et et valeur) valeur) word=$(echo word=$(echo "$line"|awk "$line"|awk -F -F "\t" "\t" '{print '{print $1}'). line.\)\s*$//') 's/^\s*\(. nb=$(echo nb=$(echo "$line"|awk "$line"|awk -F -F "\t" "\t" '{print '{print $2}'). (IE.\)\s*$//') ## Recuperer Recuperer mot mot et et occurrence occurrence (IE. do do ## Supprimer Supprimer les les espaces espaces autour autour de de la la ligne.Exemple (Bash) 6-41 Occurences de mots version Bash – opération REDUCE: lastword="" lastword="" total=0 total=0 ## Pour Pour chaque chaque ligne ligne d'entrée. .

valeur)." occurences. total=0.Exemple (Bash) 6-42 ## On On change change de de mot mot (test (test nécessaire nécessaire parce parce qu'on qu'on est est susceptible susceptible d'avoir d'avoir ## en en entrée plusieurs clefs distinctes différentes pour une seule et entrée plusieurs clefs distinctes différentes pour une seule et ## même même exécution exécution du du programme programme –– Hadoop Hadoop triera triera les les couples couples par par clef clef ## distincte). echo echo -e -e $lastword"\t"$total" $lastword"\t"$total" occurences." . fi fi lastword="$word" lastword="$word" total=$[ total=$[ $total $total ++ $nb $nb ]] ## Ajouter Ajouter la la valeur valeur au au total. if if [[ "$word" "$word" != != "$lastword" "$lastword" ]] && && [[ "$lastword" "$lastword" != != "" "" ]." total=0. done done ## Ecrire Ecrire le le dernier dernier couple couple (clef. total. (clef. distincte)." occurences. then then echo echo -e -e $lastword"\t"$total" $lastword"\t"$total" occurences. ].valeur).

jar hadoop-streaming-X.txt /poeme.Z.py Après quoi la tâche Hadoop s'exécutera exactement comme une tâche standard./map.Y./reduce.py -reducer -reducer .Z.Y. . Syntaxe: hadoop hadoop jar jar hadoop-streaming-X.txt \ -output -output /results /results -mapper -mapper .py .Z.jar -input -input [HDFS [HDFS INPUT INPUT FILES] FILES] \ -output -output [HDFS [HDFS OUTPUT OUTPUT FILES] FILES] \ -mapper -mapper [MAP [MAP PROGRAM] PROGRAM] \ -reducer -reducer [REDUCE [REDUCE PROGRAM] PROGRAM] Par exemple: hadoop hadoop jar jar hadoop-streaming-X.py .jar hadoop-streaming-X.Exécution 6-43 Streaming est un programme Hadoop standard. C'est dans ses options de ligne de commande qu'on va indiquer les scripts ou programmes Hadoop map et reduce à utiliser./map.jar -input -input /poeme.Y. on l'exécutera donc avec la même commande hadoop jar qu'un programme Hadoop Java habituel.Z./reduce.Y.