2 Hadoop与基准测试
2.1 创建Hadoop用户
- 增加一个Hadoop用户
su # 上述提到的以 root 用户登录
useradd -m hadoop -s /bin/bash # 创建新用户hadoop
passwd hadoop
为 hadoop 用户增加管理员权限,方便部署
visudo
2.2 安装与配置SSH
集群、单节点模式都需要用到 SSH 登陆(类似于远程登陆,你可以登录某台 Linux 主机,并且在上面运行命令),一般情况下,CentOS 默认已安装了 SSH client、SSH server,打开终端执行如下命令进行检验
rpm -qa | grep ssh
执行如下命令测试一下 SSH 是否可用
ssh localhost
此时会有如下提示(SSH首次登陆提示),输入 yes 。然后按提示输入密码 hadoop,这样就登陆到本机了
但这样登陆是需要每次输入密码的,我们需要配置成SSH无密码登陆比较方便。利用 ssh-keygen 生成密钥,并将密钥加入到授权中
exit # 退出刚才的 ssh localhost
cd ~/.ssh/ # 若没有该目录,请先执行一次ssh localhost
ssh-keygen -t rsa # 会有提示,都按回车就可以
cat id_rsa.pub >> authorized_keys # 加入授权
chmod 600 ./authorized_keys # 修改文件权限
此时再用 ssh localhost 命令,无需输入密码就可以直接登陆了,如下图所示
2.3 安装Java环境
-
Java版本选择
Java 环境可选择 Oracle 的 JDK,或是 OpenJDK,现在一般 Linux 系统默认安装的基本是 OpenJDK,如 CentOS 6.4 就默认安装了 OpenJDK 1.7。按 http://wiki.apache.org/hadoop/HadoopJavaVersions 中说的,Hadoop 在 OpenJDK 1.7 下运行是Good。
为了开发方便,我们还是需要通过 yum 进行安装 JDK,安装过程中会让输入 [y/N],输入 y 即可
sudo yum install java-1.7.0-openjdk java-1.7.0-openjdk-devel
接着需要配置一下 JAVA_HOME 环境变量,为方便,我们在 ~/.bashrc 中进行设置
vim ~/.bashrc
在文件最后面添加如下单独一行(指向 JDK 的安装位置),并保存
export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk
接着还需要让该环境变量生效,执行如下代码
source ~/.bashrc # 使变量设置生效
由于CentOS7默认安装Java1.8,我们需要将1.7设置为选中的Java版本
alternatives --config java
设置好后我们来检验一下是否设置正确
echo $JAVA_HOME # 检验变量值
java -version
$JAVA_HOME/bin/java -version # 与直接执行 java -version 一样
这样,Hadoop 所需的 Java 运行环境就安装好了。
2.4 安装Hadoop
- Hadoop 2 可以通过 http://mirror.bit.edu.cn/apache/hadoop/common/ 或者 http://mirrors.cnnic.cn/apache/hadoop/common/ 下载,选择版本2.10.2
- 将 Hadoop 安装至 /usr/local/ 中
sudo tar -zxf ~/下载/hadoop-2.10.2.tar.gz -C /usr/local # 解压到/usr/local中
cd /usr/local/
sudo mv ./hadoop-2.10.2/ ./hadoop # 将文件夹名改为hadoop
sudo chown -R hadoop:hadoop ./hadoop # 修改文件权限
Hadoop 解压后即可使用。输入如下命令来检查 Hadoop 是否可用,成功则会显示 Hadoop 版本信息
cd /usr/local/hadoop
./bin/hadoop version
2.5 Hadoop单机配置
Hadoop 默认模式为非分布式模式,无需进行其他配置即可运行。非分布式即单 Java 进程,方便进行调试。
们选择运行 grep 例子,我们将 input 文件夹中的所有文件作为输入,筛选当中符合正则表达式 dfs[a-z.]+ 的单词并统计出现的次数,最后输出结果到 output 文件夹中。
cd /usr/local/hadoop
mkdir ./input
cp ./etc/hadoop/*.xml ./input # 将配置文件作为输入文件
./bin/hadoop jar ./share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar grep ./input ./output 'dfs[a-z.]+'
cat ./output/* # 查看运行结果
执行成功的话会输出很多作业的相关信息,最后的输出信息如下图所示。作业的结果会输出在指定的 output 文件夹中,通过命令 cat ./output/* 查看结果,符合正则的单词 dfsadmin 出现了1次
2.6 Hadoop伪分布式配置
Hadoop 可以在单节点上以伪分布式的方式运行,Hadoop 进程以分离的 Java 进程来运行,节点既作为 NameNode 也作为 DataNode,同时,读取的是 HDFS 中的文件。
在设置 Hadoop 伪分布式配置前,我们还需要设置 HADOOP 环境变量,执行如下命令在 ~/.bashrc 中设置:
gedit ~/.bashrc
在文件最后面增加如下内容
# Hadoop Environment Variables
export HADOOP_HOME=/usr/local/hadoop
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin
保存后,不要忘记执行如下命令使配置生效:
source ~/.bashrc
这些变量在启动 Hadoop 进程时需要用到,不设置的话可能会报错(这些变量也可以通过修改 ./etc/hadoop/hadoop-env.sh 实现)。
Hadoop 的配置文件位于 /usr/local/hadoop/etc/hadoop/ 中,伪分布式需要修改2个配置文件 core-site.xml 和 hdfs-site.xml 。Hadoop的配置文件是 xml 格式,每个配置以声明 property 的 name 和 value 的方式来实现。
gedit ./etc/hadoop/core-site.xml
gedit ./etc/hadoop/hdfs-site.xml
配置完成后,执行 NameNode 的格式化:
./bin/hdfs namenode -format
接着开启 NaneNode 和 DataNode 守护进程
./sbin/start-dfs.sh
jps
启动hadoop集群后,查看进程发现名称节点的NameNode进程启动了,但是数据节点的DataNode进程没有启动的解决方法:
原因为多次格式化namenode导致的namenode与datanode之间的不一致导致。
停止集群
stop-all.sh
删除datanode存放的路径的数据 可以在hdfs-site.xml中查看存放的路径,如下
# rm -rf /usr/local/hadoop/tmp/data #三个节点都操作
重新格式化
./bin/hdfs namenode -format启动集群
./sbin/start-dfs.sh
查看进程
jps
成功启动后,可以访问 Web 界面 http://localhost:50070 查看 NameNode 和 Datanode 信息,还可以在线查看 HDFS 中的文件
2.7 Hadoop性能测试
2.7.1 TestDFSIO(Distributed i/o benchmark)
分布式的I/O基准。
#2.7.1.1 向HDFS上传10个100MB的文件
在/usr/local/hadoop/share/hadoop/mapreduce路径下
hadoop jar hadoop-mapreduce-client-jobclient-2.10.2-tests.jar TestDFSIO -write -nrFiles 10 -fileSize 100
测试结果
22/10/09 11:19:12 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
22/10/09 11:19:12 INFO fs.TestDFSIO: Date & time: Sun Oct 09 11:19:12 CST 2022
22/10/09 11:19:12 INFO fs.TestDFSIO: Number of files: 10
22/10/09 11:19:12 INFO fs.TestDFSIO: Total MBytes processed: 1000
22/10/09 11:19:12 INFO fs.TestDFSIO: Throughput mb/sec: 158.15 #吞吐量
22/10/09 11:19:12 INFO fs.TestDFSIO: Average IO rate mb/sec: 165.33 #平均IO速率
22/10/09 11:19:12 INFO fs.TestDFSIO: IO rate std deviation: 38.93 #IO率STD偏差
22/10/09 11:19:12 INFO fs.TestDFSIO: Test exec time sec: 8.62 #测试执行时间秒
22/10/09 11:19:12 INFO fs.TestDFSIO:
2.7.1.2 向HDFS上传10个100MB的文件
hadoop jar hadoop-mapreduce-client-jobclient-2.10.2-tests.jar TestDFSIO -read -nrFiles 10 -fileSize 100
测试结果:
22/10/12 20:08:49 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
22/10/12 20:08:49 INFO fs.TestDFSIO: Date & time: Wed Oct 12 20:08:49 CST 2022
22/10/12 20:08:49 INFO fs.TestDFSIO: Number of files: 10
22/10/12 20:08:49 INFO fs.TestDFSIO: Total MBytes processed: 1000
22/10/12 20:08:49 INFO fs.TestDFSIO: Throughput mb/sec: 132.96
22/10/12 20:08:49 INFO fs.TestDFSIO: Average IO rate mb/sec: 157.49
22/10/12 20:08:49 INFO fs.TestDFSIO: IO rate std deviation: 65.87
22/10/12 20:08:49 INFO fs.TestDFSIO: Test exec time sec: 33.93
22/10/12 20:08:49 INFO fs.TestDFSIO:
2.7.1.3 测试结束之后删除测试数据
hadoop jar hadoop-mapreduce-client-jobclient-2.10.2-tests.jar TestDFSIO -clean
2.7.2 nnberch
nnbench用于测试NameNode的负载,它会生成很多与HDFS相关的请求,给NameNode施加较大的压力。这个测试能在HDFS上模拟创建、读取、重命名和删除文件等操作。
使用12个mapper和6个reducer创建1000个文件
hadoop jar hadoop-mapreduce-client-jobclient-2.10.2-tests.jar nnbench -operation create_write -maps 12 -reduces 6 -blockSize 1 -bytesToWrite 0 -numberOFiles 10 -replicationFactorPerFile 3 -readFileAfterOpen true -baseDir /benchmarks/NNBench-'hostname -s'
测试结果:
22/10/12 20:15:45 INFO hdfs.NNBench: -------------- NNBench -------------- :
22/10/12 20:15:45 INFO hdfs.NNBench: Version: NameNode Benchmark 0.4
22/10/12 20:15:45 INFO hdfs.NNBench: Date & time: 2022-10-12 20:15:45,593
22/10/12 20:15:45 INFO hdfs.NNBench:
22/10/12 20:15:45 INFO hdfs.NNBench: Test Operation: create_write
22/10/12 20:15:45 INFO hdfs.NNBench: Start time: 2022-10-12 20:15:06,520
22/10/12 20:15:45 INFO hdfs.NNBench: Maps to run: 12
22/10/12 20:15:45 INFO hdfs.NNBench: Reduces to run: 6
22/10/12 20:15:45 INFO hdfs.NNBench: Block Size (bytes): 1
22/10/12 20:15:45 INFO hdfs.NNBench: Bytes to write: 0
22/10/12 20:15:45 INFO hdfs.NNBench: Bytes per checksum: 1
22/10/12 20:15:45 INFO hdfs.NNBench: Number of files: 1
22/10/12 20:15:45 INFO hdfs.NNBench: Replication factor: 3
22/10/12 20:15:45 INFO hdfs.NNBench: Successful file operations: 0
22/10/12 20:15:45 INFO hdfs.NNBench:
22/10/12 20:15:45 INFO hdfs.NNBench: # maps that missed the barrier: 0
22/10/12 20:15:45 INFO hdfs.NNBench: # exceptions: 12000
22/10/12 20:15:45 INFO hdfs.NNBench:
22/10/12 20:15:45 INFO hdfs.NNBench: TPS: Create/Write/Close: 0
22/10/12 20:15:45 INFO hdfs.NNBench: Avg exec time (ms): Create/Write/Close: Infinity
22/10/12 20:15:45 INFO hdfs.NNBench: Avg Lat (ms): Create/Write: NaN
22/10/12 20:15:45 INFO hdfs.NNBench: Avg Lat (ms): Close: NaN
22/10/12 20:15:45 INFO hdfs.NNBench:
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: AL Total #1: 0
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: AL Total #2: 0
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: TPS Total (ms): 81616
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: Longest Map Time (ms): 24683.0
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: Late maps: 0
22/10/12 20:15:45 INFO hdfs.NNBench: RAW DATA: # of exceptions: 12000
22/10/12 20:15:45 INFO hdfs.NNBench:
2.7.3 mrbench
mrbench会多次重复执行一个小作业,用于检查在机群上小作业的运行是否可重复以及运行是否高效。
hadoop jar hadoop-mapreduce-client-jobclient-2.10.2-tests.jar mrbench -numRuns 5
测试结果:
DataLines Maps Reduces AvgTime (milliseconds)
1 2 1 22018
#平均作业完成时间为22.018秒
2.8 MapReduce应用WordCount
2.8.1 WordCount执行流程
进入hadoop
cd /usr/local/hadoop
在HDFS中创建input目录
hadoop fs -mkdir /input
将LICENSE.txt放到input目录下
hadoop fs -put LICENSE.txt /input
执行wordcount程序
hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.10.2.jar wordcount /input /output
wordcount时间消耗
time hadoop jar /usr/local/hadoop/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.10.2.jar wordcount /input /output
mapreduce程序wordcount时间消耗情况
real 0m1.720s #实际时间 (real time): 从 command 命令行开始执行到运行终止的消逝时间
user 0m1.642s #用户 CPU 时间 (user CPU time): 命令执行完成花费的用户CPU时间,即命令在用户态中执行时间总和
sys 0m0.804s # 系统 CPU 时间 (system CPU time): 命令执行完成花费的系统CPU时间,即命令在核心态中执行时间总和。
查看wordcount执行结果(License.txt文档内容过大,之只展示部分wordcount结果)
hadoop fs -cat /output/part-r-00000
2.8.2 WordCount程序的实现(map和reducer)
- reducer
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Reducer;
import javax.xml.soap.Text;
import java.io.IOException;
public class WordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
super.reduce(key, values, context);
//在reduce里拿到的是mapper已经map好的数据
//现在数据的形式是这样的:
//atguigu(key),1(value)
//atguigu(key),1(value)
int sum=0;
//累计求和
for(IntWritable value: values)
{
sum+=value.get();//将intwrite对象转化为int对象
}
IntWritable v=new IntWritable();
v.set(sum);
//2.写出 atguigu 2
context.write(key,v);
//总结,这个程序看起来并没有起到分开不同单词,并对同一单词的value进行相加的作用啊
//唯一的功能则是统计仅有一个单词的字符之和,这有啥用......
}
}
- map
//现在我们开始编写wordcount的示例
public class WordcountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
//mapper后面的参数:
// 1.输入数据的key类型
// 2.输入数据的value类型
// 3.输出数据的key类型
// 4.输出数据的value的类型
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1.首先获取一行
String line=value.toString();
//2.将获取后的单词进行分割,按照空格进行分割
String[] words=line.split(" ");
//3.循环输出(不是输出到控制台上面,是输出到reducer里进行处理)
for(String word:words)
{
Text k=new Text();//定义我们输出的类型,肯定是Text,和整个类extends的顺序对应
k.set(word);
IntWritable v=new IntWritable();
v.set(1);//将value设置为1
context.write(k,v);
}
}
}
- main
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
public class wordcoundDriver {
//将mapper和reducer进行启动的类
//driver是完全格式固定的
public static void main(String[] args) throws Exception {
Configuration conf=new Configuration();
//1.获取Job对象
Job job=Job.getInstance(conf);
//2.设置jar储存位置
job.setJarByClass(wordcoundDriver.class);
//3.关联map和reduce类
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordCountReducer.class);
//4.设置mapper阶段输出数据的key和value类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//5.设置最终数据输出的key和value类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6.设置输入路径和输出路径
FileInputFormat.setInputPaths(job,new Path(args[0]));
FileInputFormat.setInputPaths(job,new Path(args[1]));
//7.提交Job
job.submit();
job.waitForCompletion(true);
}
}
参考资料
[1]https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/Benchmarking.html