Vous êtes sur la page 1sur 25

7 Programmation avec MapReduce

7.1 Hadoop Streaming

Dans cette section, nous allons vous montrer comment écrire un flux dans les applications MapReduce. Il
existe plusieurs façons de le faire en Python. Tout d'abord, vous pouvez utiliser Jython pour écrire du code

java sur Python.

Hadoop est écrit sur Java, donc cette approche fournira une interaction transparente entre deux langages.
D'une part, vous aurez plus de flexibilité pour intégrer la fonctionnalité Hadoop-Java. En revanche, il aura
quelques inconvénients. Pas étonnant, tout a ses avantages et ses inconvénients. Par exemple, l'un des
inconvénients est qu'il n'est pas possible d'utiliser les packages NumPy et SciPy, pour les calculs
numériques, car il n'y a aucun moyen d'interfacer avec des extensions écrites en C. Deuxièmement, vous
pouvez utiliser l'application Python en mode streaming. Hadoop offre la possibilité d'écrire des mappeurs,
des introducteurs dans n'importe quel langage, ce qui s'appelle le streaming. Je vais me concentrer sur la
deuxième approche.

Dans une application Hadoop MapReduce, vous disposez d'un flux de paires clé / valeur d'entrée. Vous
traitez ces données avec une fonction de mappage et transformez ces données en une liste de paires valeur
/ clé intermédiaire. Ces données sont agrégées par clés pendant la phase de mélange et de tri. Enfin, vous
traitez les données fournies dans la fonction de réduction. Si vous souhaitez brancher un programme
externe, il est naturel de communiquer via les canaux d'entrée et de sortie standard.

Ce serait exagéré de générer le processus externe pour chaque paire clé / valeur. C'est pourquoi les
développeurs Hadoop ont décidé de vous donner plus de flexibilité, et bien sûr, de responsabilité. Vous
devez implémenter vos propres mappeurs et réducteurs au lieu de simplement mapper et réduire les
fonctions. La fonctionnalité de mapper comprend l'analyse des données d'entrée, le traitement des
données et la sortie des données de manière à ce que le framework Hadoop reconnaisse les clés et les
valeurs. La fonctionnalité du réducteur comprend l'analyse des données d'entrée, l'agrégation des données
triées par clés, le traitement des données et enfin la sortie des données. Permettez-moi d'élaborer ces
points avec des exemples Shell CLI et Python.

L'un des exemples les plus simples consiste à compter le nombre de lignes dans un

ensemble de données. Wikipédia est généralement stocké dans HDFS dans le format, l'ID d'article, l'onglet
et le contenu de l'article suivants. Dans ce cas, le travail de comptage de lignes équivaut au nombre
d'articles sur Wikipedia. Ce nombre change tous les jours, car les gens ont désespérément besoin d'y
ajouter quelque chose de nouveau et de très fiable.

7.1.1 Hadoop Streaming - bigdatateam/hdfs-notebook

Reprenons l'image que nous avons téléchargé dans la section: 5.3.2

Nous allons relancer le container via le plateforme en définissant les ports et son nom etc.:
XXXX:8888 permet de se connecter au Jupyter du conteneur sur 127.0.0.1:XXXX ou localhost: XXXX, XXXX
- un numéro de port du système local (1234 notre le cas d'exemple):

7.1.2 wc-l (wc (abréviation de word count) est une commande sous Unix)
Tout d'abord, vous devez définir le chemin d'accès au jar(.jar: est un format de fichier de package
généralement utilisé pour regrouper de nombreux fichiers de classe Java et les métadonnées et ressources
associées (texte, images, etc.) en un seul fichier pour distribution.) de streaming. L'emplacement de ce
fichier JAR dépend de l'installation de votre cluster.

Dans notre container d'exemple, le jar se trouve ici:

cd ../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/

Ensuite, vous allez exécuter l'application de YARN.

Appelons donc yarn, jar et path vers le jar de streaming, puis fournissons un certain nombre d'arguments.
Notre mappeur est à la commande bash wc-l. Pour l'instant, vous ne souhaitez exécuter aucun réducteur.
Ensuite, vous spécifiez le dossier ou le fichier HDFS que vous allez traiter. Dans notre cas, un fichier
d'exemple de Wikipédia se trouvait à cette adresse: /data/wiki/en_articles. Et enfin, vous spécifiez un
dossier HDFS pour la sortie. Gardez à l'esprit que si vous avez déjà ce dossier HDFS, vous devez le
supprimer au préalable. Sinon, vous ne pourrez pas lancer mapreduce jar.

HADOOP_STREAMING_JAR="hadoop-streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR\
-mapper 'wc -l' \
-numReduceTasks 0 \
-input /data/wiki/en_articles_part\
-output wc_mr
Jetons un coup d'œil aux internes du dossier HDFS après une exécution réussie.

!hdfs dfs -ls wc_mr

Dans notre cas, nous avons exécuté le travail mapreduce uniquement contre une petite tranche de
Wikipedia. En jetant un œil à ce dossier(wc_mr), vous pouvez dire que seuls deux mappeurs ont été
exécutés. Soit dit en passant, il est recommandé de valider votre application par rapport à un petit
ensemble de données. Sinon, vous perdrez votre temps personnel et vos ressources informatiques. Ces
deux fichiers contiennent des informations sur les articles traités. Si vous les résumez, vous obtenez 4 100
articles Wikipédia dans notre assemblage.

!hdfs dfs -text wc_mr/*


Mais faisons-le d'une manière correcte et rapide. Vous devez fournir un réducteur qui regroupe le nombre
d'articles de tous les mappeurs.

7.1.3 awk

Nous allons le faire faire avec awk, qui est un autre langage de programmation Shell utile, développé par
Aho, Kernigan et Winiberg. Pour chaque ligne d'entrée, je la résume à la variable appelée, nombre de
lignes, et à la fin, je l'imprime.

Veuillez également mentionner que j'ai spécifié exactement un réducteur. Dans ce cas, j'ai la garantie que
le seul réducteur fonctionnera. Et vous avez exactement une valeur dans la sortie. Laissez-moi vérifier ce
que nous obtenons.

HADOOP_STREAMING_JAR="hadoop-streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-mapper 'wc -l' \
-reducer "awk '{line_count += $1} END { print line_count }'" \
-numReduceTasks 1 \
-input /data/wiki/en_articles_part\
-output wc_mr

Comme vous auriez pu le mentionner, je dois échapper aux caractères spéciaux dans la carte Shell et
réduire les commandes. Pour vous simplifier la vie et vous simplifier la vie, vous feriez mieux de les utiliser
en tant que script Shell. Par exemple, si vous avez le script suivant appelé reducer.shell, alors d'abord, vous
n'avez pas besoin d'échapper. Et deuxièmement, vous ne pouvez pas l'exécuter de la manière suivante.
Vous exécutez le script Shell du réducteur pendant la phase de réduction et vous devez également dire que
vous souhaitez que le fichier, reducer.shell, soit copié à partir du stockage local pour être disponible pour
les travailleurs mapreduce.
Pour créer un script shell:

#! /usr/bin/env python
with open ('reducer.sh', 'w') as rsh:
rsh.write('''\
#!/usr/bin/env bash
awk '{line_count += $1} END { print line_count }'
''')

Lancer avec reducer.sh:

HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-mapper 'wc -l' \
-reducer './reducer.sh' \
-file reducer.sh \
-numReduceTasks 1 \
-input /data/wiki/en_articles_part \
-output wc_mr_with_reducer \
Dans cette section, vous avez appris à écrire et à appeler l'application de streaming MapReduce Shell. Dans
la section suivante, je vais vous montrer comment remplacer les scripts de streaming Shell par des scripts
Python.

7.2 Hadoop Streaming Python

Dans cette section, nous allons vous montrer une façon Python de compter les lignes dans un ensemble de
données distribué. Pour faire ce travail, vous devez fournir mapper.py qui lit les données depuis l'entrée
standard et les imprime sur la sortie standard.

7.2.1 mapper.py

Regardons de plus près.

Créons un nouveau fichier python: mapper.py sous le même répertoire que Demo.ipynb en créant via
Jupyter note book un fichier texte et en le renommant au mapper.py

J'importe un modèle sys et j'effectue une itération sur les lignes du flux d'entrée. Ceci est un exemple de
travail minimal de mappeur de streaming Python. Si vous l'exécutez en tant que mappeur dans ce travail,
vous obtenez une sortie vide. Veuillez mentionner que vous devez parcourir le flux d'entrée pour atteindre
la fin d'un flux.
#!/usr/bin/env python
import sys

for line in sys.stdin:


pass

!hdfs dfs -rm -r wc_mr_with_reducer


HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-files mapper.py,reducer.sh \
-input /data/wiki/en_articles_part \
-output wc_mr_with_reducer \
-mapper 'python mapper.py' \
-reducer './reducer.sh' \

Si vous avez plus d'un fichier à distribuer sur les nœuds de calcul, vous pouvez les spécifier sous forme de
liste séparée par des virgules. Et avec attention, vous pouvez le mentionner, que j'ai également inversé
l'ordre des arguments. L'ordre de certains arguments est important. Ce sont les options dites génériques.
Les fichiers Minus en font partie.
Le premier nombre en surbrillance est un certain nombre de répliques de fichiers. Le second est une taille
de fichier. Et il est naturel de voir une sortie vide en exécutant cette tâche mapReduce en continu.
Pour fournir la sortie de Python, vous devez appeler la fonction d'impression. J'ai également ajouté un en-
tête spécial pour que ce code fonctionne sur Python version deux et Python version trois. Nous allons donc
adapter le mapper.py. Il ne vous a pas fallu longtemps pour passer de cet exemple à un calendrier qui
fonctionne correctement, dans lequel vous parcourez les lignes d'entrée et en ajoutez une pour chacune
d'elles.

#!/usr/bin/env python
from __future__ import print_function
import sys

for line in sys.stdin:


print("1")


Ce que j'aime vraiment avec Python, c'est que vous pouvez toujours trouver une solution élégante à
presque tous les problèmes. Ici, vous pouvez compter les lignes d'entrée avec une seule ligne de
code.
7.2.2 reducer.py

Par souci de cohérence, remplaçons le script shell du réducteur par un script Python en continu. Vous
parcourez les lignes d'entrée standard, les transformez en entiers et les agrégez en une valeur de sortie.

#!/usr/bin/env python
from __future__ import print_function
import sys

line_count = sum (1 for _ in sys.stdin)

print(line_count)

#ou

#!/usr/bin/env python
from __future__ import print_function
import sys

line_count = 0
for line in sys.stdin:
line_count += 1

print(line_count)

Il vous suffit de faire quelques modifications mineures pour exécuter notre tâche de streaming mapReduce.
Vous remplacez le suffixe du shell par le suffixe py et mettez à jour la commande reducers.

!hdfs dfs -rm -r wc_mr_with_reducer


HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-files mapper.py,reducer.py \
-input /data/wiki/en_articles_part \
-output wc_mr_with_reducer \
-mapper 'python mapper.py' \
-reducer 'python reducer.py' \
-numReduceTasks 1 \
Dans cette section, vous avez écrit votre première application de streaming mapReduce entièrement sur
Python. Dans les prochaines sections, je vais vous montrer comment travailler avec des paires valeur / clé
dans les applications de streaming.

7.3 WordCount in Python

7.3.1 Mapper.py for WordCount

Dans cette section, nous allons apprendre à écrire MapReduce, l'application WordCount entièrement en
Python. Pour ce faire, vous devez apprendre à définir des paires valeur / clé pour les flux d'entrée et de
sortie.
Par défaut, le préfixe d'une ligne jusqu'au premier caractère de tabulation est la clé. Et le reste de la ligne à
l'exclusion du caractère de tabulation, sera leur valeur.

S'il n'y a pas de caractère de tabulation dans la ligne, une clé correspond à la ligne entière, tandis que la
valeur est aucune. Permettez-moi de diviser le texte en mots et de les compter. Pour chaque ligne d'entrée,
vous la divisez en clé et valeur où l'ID d'article est une clé et le contenu de l'article est une valeur.

Ensuite, vous divisez le contenu en mots, et finalement vous produisez des paires de valeurs clés
intermédiaires.
#!/usr/bin/env python
from __future__ import print_function
import sys

for line in sys.stdin:


article_id, content = line.split("\t", 1)
words = content.split()
for word in words:
print(word, 1, sep = "\t")

Validation du mappeur par rapport à un petit ensemble de données. Je ne veux exécuter aucun réducteur.
Donc, j'ai mis l'argument moins numReduceTasks à zéro.

!hdfs dfs -rm -r word_count


HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-files mapper.py \
-input /data/wiki/en_articles_part \
-output word_count \
-mapper 'python mapper.py' \
-numReduceTasks 0
Nous avons un échantillon de l'ensemble de données Wikipédia. Cela garantit que vous avez correctement
identifié la clé et la valeur. Dans le dossier de sortie, vous verrez plusieurs fichiers de sortie de Mapper.

En raison de la nature aléatoire, vous ne savez pas lequel des mappeurs a traité la première division de
données. Ensuite, jetons un coup d'œil aux deux.

Comme vous pouvez le voir, le premier bloc de données a été traité par le deuxième mappeur. Et il n'y a
pas d'ID d'article dans la sortie.

!hdfs dfs -text word_count/part-00000 |head -5


!hdfs dfs -text word_count/part-00001 |head -5

En continuant, si vous voyez un réducteur avec une implémentation par défaut qui ne fait rien, la phase de
lecture aléatoire et de tri sera exécutée. Et vous devriez voir la sortie triée.
Soyez prudent lorsque vous utilisez un seul réducteur avec de grands ensembles de données. Je l'utilise
car je sais que l'ensemble de données est petit. Jetons un œil à la sortie. Je pense qu'il serait préférable de
se débarrasser de tous les caractères de configuration.

Vous pouvez facilement le faire avec un module d'expression régulière Python. J'utilise ici W majuscule, ce
qui permet d'ignorer tous les caractères gras. Si j'utilise un petit W, vous vous débarrassez de tous les
caractères des mots, ce qui n'est pas ce que vous voulez obtenir.
#!/usr/bin/env python
from __future__ import print_function
import sys

for line in sys.stdin:


article_id, content = line.split("\t", 1)
words = re.split("\W+", content)
for word in words:
if word:
print(word, 1, sep = "\t")

Lorsque vous l'appelez à nouveau, vous verrez une image bien meilleure.

7.3.2 Reducer.py for WordCount

Dès que la paire fonctionne correctement, concentrons-nous sur la deuxième phase, le Reducer. Vous
disposez déjà de données agrégées par clé. Mais à quoi ressemble la lecture des données de streaming
d'entrée du côté du réducteur.
Si vous ne vous souvenez pas du niveau de responsabilité dans les applications de streaming, laissez-moi
vous rappeler qu'il est de votre devoir d'agréger les valeurs par clés. C'est facile à montrer sur l'exemple.

Lors de la phase de réduction, vous avez trié le flux de paires valeur / clé. Et il est important de mentionner
que le flux est trié par clés. Vous pouvez ensuite parcourir ligne par ligne et suivre les clés actuelles pour
agréger les valeurs.

Permettez-moi de détailler cet extrait de code:

from __future__ import print_function


import sys
current_word = None
word_count = 0

for line in sys.stdin:


word, counts = line.split("\t", 1)
counts = int(counts)
if word == current_word:
word_count += counts
else:
if current_word:
print(current_word, word_count, sep="\t")
current_word = word
word_count = counts

if current_word:
print(current_word, word_count, sep="\t")

Tout d'abord, vous initialisez current_word à None. Cette variable nous aidera à garder une trace de la clé
actuelle. Cette variable nous aidera à garder une trace de la clé actuelle. Ensuite, vous associez la paire
valeur / clé d'entrée.

Ensuite, vous analysez la paire clé / valeur d'entrée. Si vous voyez le même mot, augmentez simplement le
compteur. Sinon, vous devez afficher les statistiques agrégées pour le mot précédent et mettre à jour le
compteur pour une nouvelle clé. Et il y a une petite astuce pour se débarrasser de la clé par défaut qui n'est
aucune.

Et le dernier point, lorsque vous atteignez la fin de l'entrée, vous ne devez pas oublier de sortir les
statistiques accumulées pour la dernière clé.

7.3.3 Mapper.py & Reducer.py

Si vous exécutez l'ensemble du travail de réduction du mappeur avec un seul réducteur, vous n'obtiendrez
qu'un seul fichier dans la sortie.

!hdfs dfs -rm -r word_count


HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-files mapper.py,reducer.py \
-mapper 'python mapper.py' \
-reducer 'python reducer.py' \
-numReduceTasks 1 \
-input /data/wiki/en_articles_part \
-output word_count
Si vous regardez le contenu de ce fichier, vous voyez les données triées par clés et il n'y a qu'une seule
valeur pour chaque clé.

!hdfs dfs -text word_count/part-00000

Dans ce cas, supprimez les tâches non réduites de la liste des arguments. Ensuite, mon emploi précédent
aura un nombre arbitraire de réducteurs.

!hdfs dfs -rm -r word_count


HADOOP_STREAMING_JAR="../../opt/cloudera/parcels/CDH/lib/hadoop-mapreduce/hadoop-
streaming.jar"
!yarn jar $HADOOP_STREAMING_JAR \
-files mapper.py,reducer.py \
-mapper 'python mapper.py' \
-reducer 'python reducer.py' \
-input /data/wiki/en_articles_part \
-output word_count
Et vous verrez plusieurs fichiers dans le dossier de sortie HDFS. Dans chaque fichier, les données sont triées
par clés, mais les clés ne sont pas globalement triées car j'ai mélangé entre les réducteurs. Il est possible
d'utiliser une chose spéciale appelée TotalOrderPartitioner pour trier les clés entre les réducteurs. Mais
cette méthode ne sera pas abordée dans ce cours, je vous encouragerai de la vérifier sur internet.

Maintenant, vous savez ce qu'est MapReduce Streaming et comment cela fonctionne. Vous savez comment
écrire des applications MapReduce Bash et Python Streaming. Et maintenant, vous devriez être en mesure
de résoudre par vous-même, WordCount ou des problèmes similaires dans MapReduce en Python.

Vous aimerez peut-être aussi