文章目录
基于Python语言的Mapreduce任务编写与运行
如何使用Python 为 Hadoop编写一个简单的MapReduce程序?Hadoop 框架是使用Java编写的,其主流API也是Java的,但是我们有时候需要使用像C++、Python等语言来实现 Hadoop程序,此时应该怎么做?
利用Hadoop的一个工具——Hadoop Streaming,利用这个工具我们可以很简单的使用其他语言而非Java来实现MapReduce任务的编写与运行。这个工具的JAR文件通常随Hadoop一起安装了,不需要我们去额外的配置它,我们只需要专心于MapReduce的处理逻辑即可。了解Hadoop Streaming的参考文档:
下面我们将利用Hadoop Streaming,使用Python编程来实现一个MapReduce的WordCount计算实例。参考文档链接:
题外话:要想使用Python编程来实现MapReduce任务编写与运行,我们还可以利用一个PythonAPI——Pydoop来完成。本文只提出以供拓展和参考不涉及具体,Pydoop参考链接
1. 实验环境
代码编写操作系统:windows11家庭中文版__64位
编写代码IDE:jupyter notebook
代码运行环境:Linux虚拟机(Ubuntu20.04)
Python解释器版本:3.x版本
Hadoop集群版本:2.10.2
Hadoop集群运行模式:伪分布模式
2. 实验过程
实验任务:使用Python编程实现基于MapReduce的WordCount计算实例
2.1. 编写Mapper阶段处理脚本
下列的代码将保存在mapper.py中,它从stdin标准输入读取数据并将单词成行分隔开,生成一个键值列表,映射单词与发生次数的关系。这个脚本中,并不计算出单词出现的总数,计算是留给后来的Reduce步骤(或叫做程序)来实现
#!/usr/bin/env python3
# mapper.py
# 注意,第一行“#!/usr/bin/env python3”是指定运行的解释器及位置
import sys
# 输入来自标准输入stdin
for line in sys.stdin:
# 移除前后空白
line = line.strip()
# 分割单词
words = line.split()
# 输出每个单词及其出现次数(这里为1)
for word in words:
# print(f"{word}\t1")
print('%s\t%s'%(word, 1))
2.2. 编写Reducer阶段处理脚本
下列的代码将保存在reducer.py 中,这个脚本的作用是从mapper.py 的stdin中读取结果,然后计算每个单词出现次数的总和,并输出结果到stdout
#!/usr/bin/env python3
# reducer.py
# 注意,第一行“#!/usr/bin/env python3”是指定运行的解释器及位置
from operator import itemgetter
import sys
current_word = None
current_count = 0
word = None
# 输入来自标准输入stdin
for line in sys.stdin:
# 移除前后空白
line = line.strip()
# 解析当前单词和计数
word, count = line.split('\t', 1)
# 转换计数为整数
try:
count = int(count)
except ValueError:
# 如果计数不是一个整数,则忽略这一行
continue
# 如果当前单词与之前的不同
if current_word == word:
current_count += count
else:
if current_word:
# 输出上一个单词及其计数
# print(f"{current_word}\t{current_count}")
print('%s\t%s'%(current_word, current_count))
current_count = count
current_word = word
# 输出最后一个单词及其计数
if current_word == word:
# print(f"{current_word}\t{current_count}")
print('%s\t%s'%(current_word, current_count))
2.3. 本地运行测试
在运行MapReduce job测试前尝试手工测试mapper.py 和 reducer.py脚本,以免出现错误或得不到正确返回结果。如下测试假定我们的脚本文件存放位置为:${HADOOP_HOME}/
,即Hadoop的主目录下。我的${HADOOP_HOME}=/usr/local/hadoop2
2.3.1. Mapper脚本
# 切换到脚本存放目录
cd /usr/local/hadoop2
# 方式一
# 该方式我们可能得在运行脚本的代码内容头部指定解释器
# 在上面我们编写的mapper.py代码里已经指定了解释器
# 1. 给予mapper.py可执行权限
chmod +x ./mapper.py
# 2. 执行该命令进行测试
echo "foo foo quux labs foo bar quux" | ./mapper.py
# 方式二
echo "foo foo quux labs foo bar quux" | python3 ./mapper.py
# 倘若执行无误将得到如下结果
foo 1
foo 1
quux 1
labs 1
foo 1
bar 1
quux 1
出现的问题:第二种方式运行没有问题,但是第一种方式出现了文件格式问题。原因是代码文件是windous下写的但是运行是上传到Linux上运行,结果出现了文件编码格式问题
同样的编码格式,第一种方式出错但是第二种方式出错,其原因可能是第一种方式代码是交给Linux的执行程序执行,此时文件的编码格式必须要是Linux能够准确识别的;而第二种方式,代码是直接交给Python解释器解释执行,Python解释器可能对这种系统编码格式差异做了处理,以至于能够正确执行。但我们仍然应格外注意这种系统编码格式差异
2.3.2. Reducer脚本
# 切换到脚本存放目录
cd /usr/local/hadoop2
# 方式一
# 该方式我们可能得在运行脚本的代码内容头部指定解释器
# 在上面我们编写的reducer.py代码里已经指定了解释器
# 1. 给予reducer.py可执行权限
chmod +x ./reducer.py
# 2. 执行该命令
echo "foo foo quux labs foo bar quux" | ./mapper.py | sort | ./reducer.py
# 方式二
echo "foo foo quux labs foo bar quux" | python3 ./mapper.py | sort | python3 ./reducer.py
# 倘若执行无误将得到如下结果(一定会输出这几行,上下次序可能会有所不同)
bar 1
foo 3
labs 1
quux 2
出现的问题:同Mapper脚本的测试
2.3.3. 存在的问题及解决
- 文件格式问题
在github上download的程序或者其他其他系统上传的程序总是会有点问题,可能是因为不通过系统的文件其编码可能不一致。比如windows操作系统的换行符和Linux操作系统的换行符貌似是不一样的,windows的是\n而Linux的是\t\n
所以假如我们的代码文件的编写时操作系统与运行时操作系统不一致,那么我们就得注意文件编码格式问题了。下面是windous操作系统的代码上传到Linux操作系统运行时,在Linux中的文件格式问题的解决方案
# 1. 查看文件格式
# 用vim编辑器打开,在vim的命令模式输入`:set ff`按后回车,千万别少了“:”冒号
# 注意vim最下面的命令输出,输出可能是:fileformat=dos,即文件格式为:dos
# 2. 设置为unix格式
# 用vim编辑器打开,在vim的命令模式输入`:set ff=unix`后回车,千万别少了“:”冒号
# 重新查看文件格式,可以发现输出变为了:fileformat=unix
# 3. 保存后退出
# :wq
vim编辑器使用指南:Linux vi/vim
- 运行问题
如直接mapper.py
而不是python mapper.py
的方式运行脚本,我们可能得在mapper.py的头部指定解释器,并且赋予mapper.py可执行权限chmod +x ./mapper.py
2.4. 集群运行测试
注意:请确保已正确安装Hadoop,并且已经将mapper.py
和reducer.py
脚本放在了可访问的位置,倘若任务运行需要额外的数据,那么该数据也要是存在且可访问到的
-
将mapper与reducer脚本放入Hadoop主目录下(这样方便使用,其实在哪个目录都可以,只需要在后面的运行命令里对路径做适当的调整即可)
-
在Hadoop中创建一个存放任务输入数据文件的目录,将数据上传到其中
# 本次词频统计用到的数据文件假设为:book.txt # book.txt在本地的存放位置为:${HADOOP_HAME}/,即存放在/usr/local/hadoop2/目录下 # book.txt其中的内容为:foo foo quux labs foo bar quux # 切换到Hadoop主目录 cd /usr/local/hadoop2 # 创建目录 ./bin/hdfs dfs -mkdir -p /path/to/input/directory # 上传数据 ./bin/hdfs dfs -put ./book.txt /path/to/input/directory
-
开启任务,如下:
# 注意mapper.py与reducer.py是否具有执行权限 # 给予mapper.py可执行权限 chmod +x ./mapper.py # 给予reducer.py可执行权限 chmod +x ./reducer.py # 切换到Hadoop主目录 cd /usr/local/hadoop2 # 开启任务 hadoop jar /path/to/hadoop-streaming.jar \ -input /path/to/input/directory \ -output /path/to/output/directory \ -mapper mapper.py \ -reducer reducer.py
这条命令是用于在Hadoop集群上运行一个使用Streaming API的MapReduce作业的。下面是对每个部分的解释:
hadoop jar
: 这是Hadoop命令行工具的一部分,用于执行Hadoop作业。jar
子命令指定了要运行的Java归档文件(JAR)。/path/to/hadoop-streaming.jar
: 这是Hadoop Streaming JAR文件的路径。Hadoop Streaming是一个实用工具,允许你使用任何可执行文件或脚本作为mapper和reducer来创建和运行MapReduce作业。这个JAR文件通常随Hadoop一起安装。\
:是标识命令换行,一行写不下命令或者命令写成一行不便于阅读理解时使用-input /path/to/input/directory
: 这个选项指定了输入数据的路径。在Hadoop分布式文件系统(HDFS)中,且这应该是一个目录,其中包含了要处理的文件。-output /path/to/output/directory
: 这个选项指定了输出数据的路径。其位于Hadoop的文件系统中,且应该是一个尚不存在的目录,因为Hadoop会创建这个目录并将输出数据写入其中。-mapper mapper.py
: 这个选项指定了mapper脚本的路径,客户端本地的路径。在这个例子中,mapper.py
是使用Python编写的mapper脚本。-reducer reducer.py
: 这个选项指定了reducer脚本的路径,客户端本地的路径。在这个例子中,reducer.py
是使用Python编写的reducer脚本。
总的来说,这条命令告诉Hadoop使用指定的mapper和reducer脚本处理输入目录中的数据,并将结果输出到指定的输出目录中。
-
查询测试结果:
# 切换到Hadoop主目录 cd /usr/local/hadoop2 # 查看生成了多少数据文件 ./bin/hdfs dfs -ls /path/to/output/directory # 查看计算结果 ./bin/hdfs dfs -cat /path/to/output/directory/part-00000
遇到的问题:Hadoop Streaming JAR的路径错误或不知道在哪里
Hadoop Streaming JAR因为安装的hadoop版本不一样其jar包的名字与位置可能也会不一样。比如hadoop2.9.2,那么你相应的hadoop-streaming.jar包的名字可能叫hadoop-streaming-2.9.2.jar。总之你可以在你的hadoop安装目录下(即$HADOOP_HOME)搜索hadoop-streaming*.jar来确定它的位置。如下:
# 通过 find ${HADOOP_HOME}/ -name "hadoop-streaming*.jar" 这条命令来查找
# 假设Hadoop主目录路径${HADOOP_HOME}=/usr/local/hadoop2,则查找命令如下
find /usr/local/hadoop2/ -name "hadoop-streaming*.jar"
3. 实验总结
在实验过程中了解了MapReduce及其使用