本文主要记录Mac下如何进行Hadoop伪分布模式安装,并通过词频统计Demo程序(WordCount)理解MapReduce的原理。
Hadoop和Spark是两种不同的大数据处理框架,如下图所示。
Hadoop 通常包括2个部分:存储和处理。存储部分就是Hadoop的分布式文件系统(HDFS),处理指的是MapReduce(MP)。
Hadoop 安装模式分为3种,分别是单机模式,伪分布模式和全分布模式。默认安装是单机模式。可以通过配置文件 core-site.xml
,将默认的单机模式更改为伪分布模式。
关于Hadoop 3种安装模式和如何使用虚拟机进行分布式安装,可以参考《Hadoop应用技术详解》书籍的第2章节——Hadoop安装。
Hadoop 的运行方式是由配置文件决定的,因此如果需要从伪分布式模式切换回非分布式模式,需要删除
core-site.xml
中的配置项。
下面简单记录,如何通过修改配置文件,在 Mac 上搭建伪分布模式 Hadoop 环境。
Hadoop的安装和配置步骤如下(具体细节参考上述参考链接)
ssh localhost
进行验证。hadoop 2.10.0
。将下载的 .tar.gz
压缩包解压并放置到 /Library/hadoop-2.10.0
路径。(1) 打开配置文件
vim ~/.bash_profile 复制代码
(2) 设置环境变量
HADOOP_HOME=/Library/hadoop-2.10.0 PATH=$PATH:${HADOOP_HOME}/bin HADOOP_CONF_DIR=/Library/hadoop-2.10.0/etc/hadoop HADOOP_COMMON_LIB_NATIVE_DIR=/Library/hadoop-2.10.0/lib/native export HADOOP_HOME export PATH export HADOOP_CONF_DIR export HADOOP_COMMON_LIB_NATIVE_DIR 复制代码
(3) 使配置文件生效,并验证Hadoop版本号
source ~/.bash_profile hadoop version 复制代码
需要修改的 Hadoop 配置文件都在目录 etc/hadoop
下,包括
hadoop-env.sh
core-site.xml
hdfs-site.xml
mapred-site.xml
yarn-site.xml
下面逐步进行修改
(1) 修改 hadoop-env.sh
文件
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home export HADOOP_HOME=/Library/hadoop-2.10.0 export HADOOP_CONF_DIR=/Library/hadoop-2.10.0/etc/hadoop 复制代码
(2) 修改 core-site.xml
文件
设置 Hadoop 的临时目录和文件系统,localhost:9000
表示本地主机。如果使用远程主机,要用相应的 IP 地址来代替,填写远程主机的域名,则需要到 /etc/hosts
文件中做 DNS 映射。
<configuration> <!--localhost:9000 表示本地主机-->> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> </property> <!--用来指定hadoop运行时产生文件的存放目录 自己创建--> <property> <name>hadoop.tmp.dir</name> <value>/Users/lbs/devfiles/hadoop/hadoop-2.10.0/tmp</value> <description>Directories for software develop and save temporary files.</description> </property> </configuration> 复制代码
(3) 修改 hdfs-site.xml
文件
hdfs-site.xml
指定了 HDFS 的默认参数副本数,因为仅运行在一个节点上(伪分布模式),所以这里的副本数为1。
<configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <!--不是root用户也可以写文件到hdfs--> <property> <name>dfs.permissions</name> <value>false</value> <!--关闭防火墙--> </property> <!--把路径换成本地的name位置--> <property> <name>dfs.namenode.name.dir</name> <value>/Users/lbs/devfiles/hadoop/hadoop-2.10.0/tmp/dfs/name</value> </property> <!--在本地新建一个存放hadoop数据的文件夹,然后将路径在这里配置一下--> <property> <name>dfs.datanode.data.dir</name> <value>/Users/lbs/devfiles/hadoop/hadoop-2.10.0/tmp/dfs/data</value> </property> </configuration> 复制代码
(4) 修改 mapred-site.xml
文件
复制 mapred-site.xml.template
模板文件,并修改为 mapred-site.xml
文件,然后将 yarn
设置成数据处理框架,并设置 JobTracker 的主机名与端口。
<configuration> <property> <!--指定mapreduce运行在yarn上--> <name>mapreduce.framework.name</name> <value>yarn</value> </property> </configuration> 复制代码
(5) 修改 yarn-site.xml
文件
配置数据的处理框架 yarn
<configuration> <!-- Site specific YARN configuration properties --> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.address</name> <value>localhost:9000</value> </property> </configuration> 复制代码
(1) 第一次启动Hadoop,需要对 NameNode 进行格式化,后续启动不再需要执行此步骤。
hadoop namenode -format 复制代码
(2) 启动 HDFS:进入Hadoop 安装目录下的 sbin
目录,并启动HDFS(需要设置Mac允许远程登录,过程中共需要3次输入密码)
Tip: 初次安装和启动时,可以执行
./start-all.sh
,进行必要的初始化安装
cd /Library/hadoop-2.10.0/sbin ./start-dfs.sh 复制代码
若出现下述信息,表示启动成功
lbsMacBook-Pro:sbin lbs$ ./start-dfs.sh Starting namenodes on [localhost] Password: localhost: namenode running as process 12993. Stop it first. Password: localhost: datanode running as process 32400. Stop it first. Starting secondary namenodes [0.0.0.0] Password: 0.0.0.0: Connection closed by 127.0.0.1 port 22 复制代码
需要注意的是,在log
中会显示警告
WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicabled的 复制代码
上述提醒是关于 Hadoop 本地库的——Hadoop本地库是为了提高效率或者某些不能用Java实现的功能组件库。可以参考 Mac OSX 下 Hadoop 使用本地库提高效率 了解详情。
停止 Hadoop 方法如下
cd /Library/hadoop-2.10.0/sbin ./sbin/stop-dfs.sh 复制代码
(3) 在终端执行 jps
,若看到如下信息,证明 Hadoop 可以成功启动。看到 DataNode
,NameNode
和 SecondaryNameNode
信息,表明启动的是一个伪分布模式Hadoop。
lbsMacBook-Pro:sbin lbs$ jps 32400 DataNode 12993 NameNode 30065 BootLanguagServerBootApp 13266 SecondaryNameNode 30039 org.eclipse.equinox.launcher_1.5.700.v20200207-2156.jar 35019 ResourceManager 35117 NodeManager 32926 RunJar 35199 Jps 复制代码
也可以访问 http://localhost:50070/dfshealth.html#tab-overview
来查看 Hadoop的启动情况。看到 Live Node
参数,证明伪分布模式 Hadoop 启动成功。
(4) 启动 yarn:进入Hadoop 安装目录下的 sbin
目录,并启动 yarn
cd /Library/hadoop-2.10.0/sbin ./start-yarn.sh 复制代码
至此,Hadoop的安装,配置和启动就完成啦!接下来可以通过一些 shell 命令来操作 Hadoop 下的文件了,例如
hadoop fs -ls /        查看根目录下的文件及文件夹 hadoop fs -mkdir /test 在根目录下创建一个文件夹 testdata hadoop fs -rm /.../... 移除某个文件 hadoop fs -rmr /... 移除某个空的文件夹 复制代码
在启动 HDFS时,若看到如下警告
./start-dfs.sh 复制代码
lbsMacBook-Pro:~ lbs$ cd /Library/hadoop-2.10.0/sbin lbsMacBook-Pro:sbin lbs$ ./start-dfs.sh 20/03/23 08:46:43 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable Starting namenodes on [localhost] Password: localhost: namenode running as process 93155. Stop it first. Password: localhost: datanode running as process 93262. Stop it first. Starting secondary namenodes [0.0.0.0] Password: 0.0.0.0: secondarynamenode running as process 93404. Stop it first. 复制代码
上述提醒是关于 Hadoop 本地库的——Hadoop本地库是为了提高效率或者某些不能用Java实现的功能组件库。可以参考 Mac OSX 下 Hadoop 使用本地库提高效率 了解详情。
pom.xml
中添加如下依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.lbs0912</groupId> <artifactId>wordcount</artifactId> <version>1.0-SNAPSHOT</version> <!--添加 apache 镜像源--> <repositories> <repository> <id>apache</id> <url>http://maven.apache.org</url> </repository> </repositories> <!--添加如下依赖--> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-core</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>2.7.2</version> </dependency> </dependencies> </project> 复制代码
WordMapper
类package wordcount; import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; public class WordMapper extends Mapper<Object, Text, Text, IntWritable> { IntWritable one = new IntWritable(1); Text word = new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { StringTokenizer itr = new StringTokenizer(value.toString()); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); context.write(word, one); } } } 复制代码
WordReducer
类package wordcount; import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable>{ private IntWritable result = new IntWritable(); public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException,InterruptedException { int sum = 0; for(IntWritable val:values) { sum += val.get(); } result.set(sum); context.write(key,result); } } 复制代码
WordMain
驱动类package wordcount; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import org.apache.hadoop.util.GenericOptionsParser; public class WordMain { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs(); /** * 这里必须有输入/输出 */ if (otherArgs.length != 2) { System.err.println("Usage: WordCount <in> <out>"); System.exit(2); } Job job = new Job(conf, "wordcount"); job.setJarByClass(WordMain.class); //主类 job.setMapperClass(WordMapper.class); //Mapper job.setCombinerClass(WordReducer.class); //作业合成类 job.setReducerClass(WordReducer.class); //Reducer job.setOutputKeyClass(Text.class); //设置作业输出数据的关键类 job.setOutputValueClass(IntWritable.class); //设置作业输出值类 FileInputFormat.addInputPath(job, new Path(otherArgs[0])); //文件输入 FileOutputFormat.setOutputPath(job, new Path(otherArgs[1])); //文件输出 System.exit(job.waitForCompletion(true) ? 0 : 1); //等待完成退出 } } 复制代码
选择 Run -> Edit Configurations
, 在程序参数栏目中输入 input/ output
,如下图所示
在 input
目录中添加统计单词个数的测试的文件 wordcount1.txt
Hello,i love coding are you ok? Hello, i love hadoop are you ok? 复制代码
再次运行程序,会看到如下的 output
目录结构
- input - output | - ._SUCCESS.crc | - .part-r-00000.crc | - ._SUCCESS | - part-r-00000 复制代码
打开 part-r-00000
文件,即可看到单词出现次数的统计结果
Hello, 1 Hello,i 1 are 2 coding 1 hadoop 1 i 1 love 2 ok? 2 you 2 复制代码
需要注意的是,由于Hadoop的设定,下次运行程序前,需要先删除output文件目录。
File -> Project Structure
选项中,为工程添加 Artifacts
,选择 WordMain
类Build -> Build Artifacts...
,生成 .jar
文件hadoop jar WordCount.jar input/ out/ 复制代码
HDFS(Hadoop Distributed File System
)是一个用在普通硬件设备上的分布式文件系统。 HDFS 具有高容错性(fault-tolerant
)和高吞吐量(high throughput
),适合有超大数据集的应用程序,可以实现通过流的形式访问文件系统中的数据。
运行在HDFS之上的应用程序必须流式地访问它们的数据集,它不是典型的运行在常规的文件系统之上的常规程序。HDFS的设计适合批量处理,而不是用户交互式的,重点是数据吞吐量,而不是数据访问的反应时间。
HDFS以块序列的形式存储每一个文件,文件中除了最后一个块的其他块都是相同的大小。
HDFS 为Hadoop 这个分布式计算框架一共高性能,高可靠,高可扩展的存储服务。HDFS是一个典型的主从架构,一个HDFS集群是由一个主节点(Namenode
)和一定数目的从节点(Datanodes
)组成。
namespace
)以及客户端对文件的访问。同时确定块和数据节点的映射。
metadata
信息,包括文件 owership
和 permissions
,文件包含有哪些块,Block
保存在哪个 DataNode
等metadata
信息在启动后会加载到内存中Rack
):一个 Block 的三个副本通常会保存到两个或者两个以上的机架中,进行防灾容错Block
)是 HDFS 文件系统基本的存储单位,Hadoop 1.X 默认大小是 64MB,Hadoop 2.X 默认大小是 128MB。HDFS上的文件系统被划分为块大小的多个分块(Chunk
)作为独立的存储单元。和其他文件系统不同的是,HDFS上小于一个块大小的文件不会占据整个块的空间。使用块抽象而非整个文件作为存储单元,大大简化了存储子系统的设计。SecondaryNameNode
)负责镜像备份,日志和镜像的定期合并。使用
hadoop fsk / -files -blocks
可以显示块的信息。
Block 数据块大小设置的考虑因素包括
Block 是 HDFS 文件系统的最小组成单元,它通过一个 Long
整数被唯一标识。每个 Block 会有多个副本,默认有3个副本。为了数据的安全和高效,Hadoop 默认对3个副本的存放策略如下图所示
这样的策略可以保证对该 Block 所属文件的访问能够优先在本 Rack 下找到。如果整个 Rack 发生了异常,也可以在另外的 Rack 找到该 Block 的副本。这样足够高效,并且同时做到了数据的容错。
RPC(Remote Procedure Call
)即远程过程调用机制会面临2个问题
RPC 架构如下图所示。Hadoop 自己实现了简单的 RPC 组件,依赖于 Hadoop Writable
类型的支持。
Hadoop Writable
接口要求每个实现类多要确保将本类的对象正确序列化(writeObject
)和反序列化(readObject
)。因此,Hadoop RPC 使用 Java 动态代理和反射实现对象调用方式,客户端到服务器数据的序列化和反序列化由 Hadoop框架或用户自己来实现,也就是数据组装定制的。
Hadoop RPC = 动态代理 + 定制的二进制流
HBase 的特点如下
有句话说得好,“大数据胜于算法”,意思是说对于某些应用(例如根据以往的偏好来推荐电影和音乐),不论算法有多牛,基于小数据的推荐效果往往都不如基于大量可用数据的一般算法的推荐效果。 —— 《Hadoop 权威指南》