Hadoop笔记
一、概述
大数据
大数据(big data)是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产
大数据的5V特点(IBM提出):
Hadoop是什么?
Apache Hadoop是一个开源、可靠、可扩展的分布式计算框架。
Hadoop框架允许用户在一个超大的规模的服务器集群中,对大数据集进行分布式的处理计算。Hadoop集群规模可以是单个(伪分布式集群)或者上千台的商用服务器(完全分布式集群)构成。Hadoop集群中每一个服务器都提供了本地计算和存储能力。Hadoop框架并不是通过硬件实现的高可用,而是通过应用层检测处理错误,那这样的话Hadoop集群就可以建立在廉价的商用服务器上。
- 狭义的Hadoop(六大模块)
- Hadoop Common: Hadoop框架通用支持库
- Hadoop Distributed File System (HDFS™): 分布式文件系统 提供了高吞吐能力的数据访问
- Hadoop YARN: 一个框架用来做任务的调度和分布式集群的资源管理
- Hadoop MapReduce: 基于YARN的系统,对大数据集进行分布式的并行计算处理
- Hadoop Ozone: Hadoop对象存储系统
- Hadoop Submarine: 机器学习的引擎
- 广义的Hadoop(泛指生态体系)
- Apache HBase : Big Table,用来存储海量的结构化数据
- Apache Zookeeper(动物园管理者): 分布式协调服务系统,主要解决Hadoop生态体系各个分布式系统存在的一些通用问题
- Apache Hive(小蜜蜂): 数据仓库的基础设施,用来简化Hadoop的操作
- Apache Flume(数据采集): 负责采集各种类型的数据,并且进行简单的预处理操作
- Apache Spark(scala语言): 更为高效的分布式计算引擎
- Apache Flink: 高效的分布式计算引擎(第三代数据分析引擎)
二、HDFS
HDFS是Hadoop的分布式文件系统( Hadoop Distributed File System ),类似于其它的分布式文件系统。HDFS支持高度容错,可以部署在廉价的硬件设备上,特别适宜于大型的数据集的分布式存储。
Google开源论文GFS的开源实现
环境搭建
构建HDFS的伪分布式集群(使用单台机器,模拟HDFS集群所有的服务)
- 安装CentOS
CentOS7.2版本
- 配置网络
# ip a 查看当前的服务器网络设置
vi /etc/sysconfig/network-scripts/ifcfg-ens33
# 将配置文件中的ONBOOT=yes
systemctl restart network
- 关闭防火墙
[root@localhost ~]# systemctl stop firewalld
[root@localhost ~]# systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
- 修改服务器的主机名
# 简化连接服务器操作
[root@localhost ~]# vi /etc/hostname
# 删除localhost,新增hadoop(自定义的主机名)
- 配置主机名和ip地址的映射关系
[root@localhost ~]# vi /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# 最后一行添加当前服务器ip地址和主机名的映射
192.168.12.129 hadoop
# 测试
[root@localhost ~]# ping hadoop
PING hadoop (192.168.12.129) 56(84) bytes of data.
64 bytes from hadoop (192.168.12.129): icmp_seq=1 ttl=64 time=0.107 ms
64 bytes from hadoop (192.168.12.129): icmp_seq=2 ttl=64 time=0.053 ms
- 配置SSH(Secure Shell)免密远程登录
[root@hadoop ~]# ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
Generating public/private rsa key pair.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:/VJcuTQzpC4EDqiiEKWwwtYAqS9Von3ssc12fM+ldvQ root@hadoop
The key's randomart image is:
+---[RSA 2048]----+
|++. .. . . |
|=o+ o o . o . |
|=* * . . . B |
|B + + o o o = |
|o+ o = .S o + . |
|o . o + o .+ o |
| . . . ..o.+ . |
| .= . E|
| . . |
+----[SHA256]-----+
[root@hadoop ~]#
[root@hadoop ~]#
[root@hadoop ~]# cd .ssh/
[root@hadoop .ssh]# ll
总用量 12
-rw-------. 1 root root 1679 8月 12 15:45 id_rsa
-rw-r--r--. 1 root root 393 8月 12 15:45 id_rsa.pub
-rw-r--r--. 1 root root 183 8月 12 15:43 known_hosts
[root@hadoop .ssh]# cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
[root@hadoop .ssh]# ll
总用量 16
-rw-r--r--. 1 root root 393 8月 12 15:47 authorized_keys
-rw-------. 1 root root 1679 8月 12 15:45 id_rsa
-rw-r--r--. 1 root root 393 8月 12 15:45 id_rsa.pub
-rw-r--r--. 1 root root 183 8月 12 15:43 known_hosts
[root@hadoop .ssh]# chmod 0600 ~/.ssh/authorized_keys
[root@hadoop .ssh]#
[root@hadoop .ssh]# ssh hadoop
Last login: Mon Aug 12 15:43:18 2019 from 192.168.12.1
- 安装JDK
[root@hadoop ~]# rpm -ivh jdk-8u191-linux-x64.rpm
警告:jdk-8u191-linux-x64.rpm: 头V3 RSA/SHA256 Signature, 密钥 ID ec551f03: NOKEY
准备中... ################################# [100%]
正在升级/安装...
1:jdk1.8-2000:1.8.0_191-fcs ################################# [100%]
Unpacking JAR files...
tools.jar...
plugin.jar...
javaws.jar...
deploy.jar...
rt.jar...
jsse.jar...
charsets.jar...
localedata.jar...
[root@hadoop ~]# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
- 安装Hadoop
[root@hadoop ~]# tar -zxf hadoop-2.6.0_x64.tar.gz -C /usr
- 修改HDFS集群的配置文件
[root@hadoop hadoop-2.6.0]# vi etc/hadoop/core-site.xml
<property>
<name>fs.defaultFS</name>
<value>hdfs://hadoop:9000</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/hadoop-2.6.0/hadoop-${user.name}</value>
</property>
[root@hadoop hadoop-2.6.0]# vi etc/hadoop/hdfs-site.xml
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
[root@hadoop hadoop-2.6.0]# vi etc/hadoop/slaves
hadoop
- 添加环境变量配置
[root@hadoop ~]# vi .bashrc
HADOOP_HOME=/usr/hadoop-2.6.0
JAVA_HOME=/usr/java/latest
CLASSPATH=.
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
export JAVA_HOME
export CLASSPATH
export PATH
export HADOOP_HOME
[root@hadoop ~]# source .bashrc
服务启动
- 初始化操作
[root@hadoop ~]# hdfs namenode -format
NOTE:
初始化操作只需要在第一次启动HDFS集群之前执行,后续不需要执行,跳过直接启动服务即可
- 启动HDFS集群
[root@hadoop ~]# start-dfs.sh
Starting namenodes on [hadoop]
hadoop: starting namenode, logging to /usr/hadoop-2.6.0/logs/hadoop-root-namenode-hadoop.out
hadoop: starting datanode, logging to /usr/hadoop-2.6.0/logs/hadoop-root-datanode-hadoop.out
Starting secondary namenodes [0.0.0.0]
The authenticity of host '0.0.0.0 (0.0.0.0)' can't be established.
ECDSA key fingerprint is SHA256:yDvdRHO65GeTfU6PJQjEKMap+lEZb8a/JeuesbTsMYs.
ECDSA key fingerprint is MD5:d4:bf:fe:86:d3:ed:2d:fc:5f:a2:2b:e5:86:0c:ae:ee.
Are you sure you want to continue connecting (yes/no)? yes
0.0.0.0: Warning: Permanently added '0.0.0.0' (ECDSA) to the list of known hosts.
0.0.0.0: starting secondarynamenode, logging to /usr/hadoop-2.6.0/logs/hadoop-root-secondarynamenode-hadoop.out
- 验证服务是否启动成功
# 1. java的指令 jps,查看java进程列表
[root@hadoop ~]# jps
10995 SecondaryNameNode # HDFS小蜜
10796 NameNode # HDFS Master
10877 DataNode # HDFS Slaves
# 2. 访问hdfs的web ui
http://服务器地址:50070
# 3. 分布式系统学会看日志
[root@hadoop hadoop-2.6.0]# cd logs/
[root@hadoop logs]# ll
总用量 92
-rw-r--r--. 1 root root 24249 8月 12 16:12 hadoop-root-datanode-hadoop.log
-rw-r--r--. 1 root root 714 8月 12 16:12 hadoop-root-datanode-hadoop.out
-rw-r--r--. 1 root root 30953 8月 12 16:17 hadoop-root-namenode-hadoop.log
-rw-r--r--. 1 root root 714 8月 12 16:12 hadoop-root-namenode-hadoop.out
-rw-r--r--. 1 root root 22304 8月 12 16:13 hadoop-root-secondarynamenode-hadoop.log
-rw-r--r--. 1 root root 714 8月 12 16:12 hadoop-root-secondarynamenode-hadoop.out
-rw-r--r--. 1 root root 0 8月 12 16:12 SecurityAuth-root.audit
- 关闭服务
[root@hadoop logs]# stop-dfs.sh
指令操作
HDFS分布式文件系统,操作类似于Linux文件系统
比如Linux:cp、mv、rm、cat、mkdir 常用指令非常类似
语法:hdfs dfs -参数
Usage: hadoop fs [generic options]
[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...] # 查看文本文件内容
[-checksum <src> ...]
[-chgrp [-R] GROUP PATH...] # 修改属组
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...] # 修改权限
[-chown [-R] [OWNER][:[GROUP]] PATH...] # 修改属主
[-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>] # 从本地拷贝到HDFS
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>] # 从HDFS拷贝到本地
[-count [-q] [-h] <path> ...] # 计数
[-cp [-f] [-p | -p[topax]] <src> ... <dst>] # 拷贝
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] <path> ...]
[-expunge]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>] # 下载
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] <src> <localdst>]
[-help [cmd ...]] # 帮助
[-ls [-d] [-h] [-R] [<path> ...]] # 查看目录列表
[-mkdir [-p] <path> ...] # 创建文件夹
[-moveFromLocal <localsrc> ... <dst>] # 从本地移动到HDFS
[-moveToLocal <src> <localdst>] # 将HDFS中的文件移动到本地
[-mv <src> ... <dst>] # HDFS中的文件或文件夹的移动
[-put [-f] [-p] [-l] <localsrc> ... <dst>] # 上传
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] <src> ...] # 删除
[-rmdir [--ignore-fail-on-non-empty] <dir> ...] # 删除文件夹
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] <file>] # 查看文本文件的末尾内容
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-usage [cmd ...]]
JAVA API操作
- 环境搭建(windows平台为例)
- 解压缩Hadoop的安装包
# 如解压缩安装到E:\\根目录
- 拷贝兼容文件到安装目录bin中
- 在windows的hosts文件中添加主机名和IP地址的映射关系
- 重启开发工具
- 配置HADOOP_HOME环境变量
- 实战
- 创建Maven工程,并导入HDFS Client Driver
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
- 测试代码
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
/**
* hdfs java api测试
* FileSystem
*/
public class HDFSDemo {
/**
* hdfs 客户端操作对象
*/
private FileSystem fileSystem = null;
private Configuration configuration = null;
@Before
public void doBefore() throws URISyntaxException, IOException {
URI uri = new URI("hdfs://hadoop:9000");
configuration = new Configuration();
fileSystem = FileSystem.get(uri, configuration);
}
/**
* 文件上传
* put
* copyFromLocal
* moveFromLocal
*
* @org.apache.hadoop.security.AccessControlException: Permission denied: user=Administrator, access=WRITE, inode="/dingl":root:supergroup:drwxr-xr-x
* 解决方案:
1. 修改权限 (UGO) o+w
2. 修改操作hdfs用户身份:-DHADOOP_USER_NAME=root
3. 关闭hdfs权限检查功能: hdfs-site.xml
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
*/
@Test
public void testUpload() throws IOException {
Path src = new Path("G:\\apache-tomcat-7.0.85.zip");
Path dst = new Path("/dingl");
fileSystem.copyFromLocalFile(src, dst);
}
@Test
public void testUpload2() throws IOException {
FileInputStream src = new FileInputStream("F:\\生态图.png");
Path dst = new Path("/dingl/test");
FSDataOutputStream dstOutputStream = fileSystem.create(dst);
IOUtils.copyBytes(src, dstOutputStream, configuration);
}
/**
* 下载文件
* get
* copyToLocal
* moveToLocal
*/
@Test
public void testDownload() throws IOException {
Path src = new Path("/dingl/test");
Path dst = new Path("G:\\1.png");
fileSystem.copyToLocalFile(src, dst);
}
@Test
public void testDownload2() throws IOException {
FSDataInputStream inputStream = fileSystem.open(new Path("/dingl/test"));
FileOutputStream outputStream = new FileOutputStream("G:\\2.png");
IOUtils.copyBytes(inputStream, outputStream, configuration);
}
/**
* 删除文件
*/
@Test
public void testDelete() throws IOException {
// fileSystem.delete(new Path("/dingl/test"),false);
// true代表递归删除
fileSystem.delete(new Path("/dingl"), true);
}
@Test
public void testOther() throws IOException {
// rwxrw-r-- /dingl
// fileSystem.mkdirs(new Path("/dingl"), new FsPermission(FsAction.ALL, FsAction.READ_WRITE, FsAction.READ));
boolean exists = fileSystem.exists(new Path("/dingl"));
System.out.println(exists?"存在":"不存在");
}
@After
public void doAfter() throws IOException {
fileSystem.close();
}
}
HDFS架构
HDFS采用master/slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。Namenode是一个中心服务器器,负责管理文件系统的名字空间(namespace)以及客户端对文件的访问。集群中的Datanode一般是一个节点一个,负责管理它所在节点上的存储。 HDFS暴露了了文件系统的名字空间,用户能够以文件的形式在上面存储数据。从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组Datanode上。 Namenode执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。 Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制。
- Namenode : 存储系统元数据、 namespace、管理datanode、接受datanode状态汇报
- Datanode: 存储块数据,响应客户端的块的读写,接收namenode的块管理理指令
- Block: HDFS存储数据的基本单位,默认值是128MB,实际块大小0~128MB
- Rack: 机架,对datanode所在主机的物理标识,标识主机的位置,优化存储和计算
架构图
Block的复制原理
元数据(MetaData)的持久化机制
Namenode使用内存存储MetaData,存在安全风险,HDFS提供了元数据的持久化
好处: 保证元数据绝对不会丢失,并且
fsimage
加速Namenode元数据的恢复速度
HDFS常见问题
- 为什么HDFS不适合小文件存储?
情况 | Namenode占用 | Datanode占用 |
10000个文件总共128MB | 10000个元数据 | 128MN |
1个128MB文件 | 1个元数据 | 128MB |
- 小文件过多,会过多占用namenode的内存,并浪费block
- HDFS适用于高吞吐量,而不适合低时间延迟的访问。文件过小,寻道时间大于数据读写时间,这不符合HDFS的设计原则
- Namenode和SecondaryNameNode区别?
Namenode主要维护两个组件,一个是 fsimage ,一个是 editlog
- fsimage保存了最新的元数据检查点,包含了整个HDFS文件系统的所有目录和文件的信息。对于文件来说包括了数据块描述信息、修改时间、访问时间等;对于目录来说包括修改时间、访问权限控制信息(目录所属用户,所在组)等。
- editlog主要是在NameNode已经启动情况下对HDFS进⾏的各种更新操作进行记录,HDFS客户端执行所有的写操作都会被记录到editlog中。
为了避免editlog不断增加,secondary namenode会周期性合并fsimage和edits成新的fsimage
三、YARN
架构理解
https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/YARN.html
Apache Hadoop YARN (Yet Another Resource Negotiator,另一种资源协调者)是一种新的 Hadoop 资源管理器,它是一个通用资源管理系统,可为上层应用提供统一的资源管理和调度,它的引入为集群在利用率、资源统一管理和数据共享等方面带来了巨大好处。
- ResourceManager:是在系统中的所有应用程序之间仲裁资源的最终权限。
- NodeManager:是每台机器框架代理,负责容器,监视其资源使用情况(CPU,内存,磁盘,网络)并将其报告给ResourceManager的Scheduler
- App Master :应用的Master负责任务计算过程中的任务监控、故障转移,每个Job只有一个。
- Container:表示一个计算进程
环境搭建
- 修改
mapred-site.xml
[root@hadoop ~]# cd /usr/hadoop-2.6.0/
[root@hadoop hadoop-2.6.0]# mv etc/hadoop/mapred-site.xml.template etc/hadoop/mapred-site.xml
[root@hadoop hadoop-2.6.0]# vi etc/hadoop/mapred-site.xml
# 添加以下内容
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
- 修改
yarn-site.xml
[root@hadoop hadoop-2.6.0]# vi etc/hadoop/yarn-site.xml
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.hostname</name>
<value>hadoop</value>
</property>
- 启动YARN的服务
伪分布式的YARN集群
[root@hadoop hadoop-2.6.0]# start-yarn.sh
starting yarn daemons
starting resourcemanager, logging to /usr/hadoop-2.6.0/logs/yarn-root-resourcemanager-hadoop.out
hadoop: starting nodemanager, logging to /usr/hadoop-2.6.0/logs/yarn-root-nodemanager-hadoop.out
[root@hadoop hadoop-2.6.0]# jps
6892 ResourceManager # master
6974 NodeManager # slave
四、MapReduce
思想理解
Hadoop MapReduce是一个软件框架,基于该框架能够容易地编写应用程序,这些应用程序能够运行在由上千个商用机器组成的大集群上,并以一种可靠的,具有容错能力的方式并行地处理上TB级别的海量数据集。这个定义里面有着这些关键词:
一是软件框架,二是并行处理,三是可靠且容错,四是大规模集群,五是海量数据集。
MapReduce擅长处理大数据,它为什么具有这种能力呢?这可由MapReduce的设计思想发觉。MapReduce的思想就是“分而治之”或者“化繁为简”。
Mapper
负责“分”,即把复杂的任务分解为若干个“简单的任务”来处理。 “简单的任务”包含三层含义:
- 是数据或计算的规模相对原任务要大大缩小;
- 是就近计算原则,即任务会分配到存放着所需数据的节点上进行计算;
- 是这些小任务可以并行计算,彼此间几乎没有依赖关系。
Reducer
主要负责对map阶段的结果进行汇总
基本开发
新建Maven工程,导入依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>2.6.0</version>
</dependency>
开发MapReduce应用程序
单词计数的应用程序
MapReduce应用程序的两个阶段:
Mapper:将大任务拆分为若干个小任务,将非结构化的数据映射为KV结构数据
Reducer:负责计算统计
准备样例文件
How are you
Where are you from
Welcome to BJ
Are you ok
将模拟数据上传到HDFS中
[root@hadoop ~]# hdfs dfs -put data.txt /dingl
定义Mapper任务
package com.dingl;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* *Writable表示的Hadoop提供的序列化对象
* LongWritable
* IntWritable
* String ---> Text
* ...
* <p>
* Mapper阶段
* keyIn: LongWritable 每行数据的首字符的offset(位置)
* valueIn: Text 一行记录
* keyOut: Text 单词
* valueOut: IntWritable 初始值 1
*/
public class MyMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
/**
* 映射方法
* How are you ---> (how,1) (are,1) (you,1)
*
* @param key keyIn
* @param value valueIn
* @param context 上下文(MapReduce应用程序运行的上下文信息)
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] words = line.toLowerCase().split(" ");
for (String word : words) {
// 输出处理完成kv数据
context.write(new Text(word), new IntWritable(1));
}
}
}
定义Reducer任务
package com.dingl;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.util.Iterator;
/**
* reducer阶段 统计和计算
* keyIn:类型等价于Mapper的keyOut
* valueIn:类型等价于Mapper的valueOut
* keyOut:单词 Text
* valueOut:总次数 IntWritable
*/
public class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
/**
* 统计计算方法
* how are you
* are you ok
* are [1,1]
* you [1,1]
* how [1]
*
* @param key 单词
* @param values key相同的初始值的集合
* @param context 上下文对象
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int count = 0;
Iterator<IntWritable> iterator = values.iterator();
while (iterator.hasNext()){
int num = iterator.next().get(); // 1
count += num;
}
// 计算完成后 输出结算结果
context.write(key,new IntWritable(count));
}
}
初始化类
package com.dingl;
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.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import java.io.IOException;
/**
* 单词计数的初始化类
*/
public class WordCountApplication {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//1. 创建MapReduce任务对象
Configuration conf = new Configuration();
String jobName = "wordcount";
Job job = Job.getInstance(conf,jobName);
job.setJarByClass(WordCountApplication.class);
//2. 设置计算数据的输入格式和计算结果的输出格式(文本)
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
//3. 指定计算数据的来源位置以及计算结果的输出位置
TextInputFormat.addInputPath(job,new Path("/dingl/data.txt"));
// 注意:计算结果的输出目录必须不存在
TextOutputFormat.setOutputPath(job,new Path("/dingl/result"));
//4. 指定MapReduce应用的Mapper阶段和Reducer阶段的实现类
job.setMapperClass(MyMapper.class);
job.setReducerClass(MyReducer.class);
//5. 设置Mapper阶段和Reducer阶段的KeyOut和ValueOut的类型
job.setMapOutputKeyClass(Text.class); // mapper的keyOut的类型
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//6. 任务提交
job.waitForCompletion(true); // true 输出运行日志
}
}
打包MapReduce应用程序为jar
测试运行
将应用jar包 上传到Linux操作系统中
使用命令提交MapReduce应用程序
- 语法:
hadoop jar xxx.jar 入口类的全限定名
查看计算结果
第二个案例(流量统计)
MapReduce应用程序的其它运行方式
注意:
在生产环境中,MapReduce Application一定是运行在YARN分布式集群中的
但是在测试开发MapReduce程序,我们可以使用以下方式,来测试代码
本地计算 + 本地数据
本地计算指的是借助于Windows平台的hadoop环境模拟运行MapReduce程序
本地数据指的是计算的数据来源于Windows平台,并且输出到本地
- 修改初始化类中如下代码
// 注意:file:/// 表示使用本地文件系统中的数据
TextInputFormat.addInputPath(job,new Path("file:///e:\\ssby.txt"));
// 注意:计算结果的输出目录必须不存在
TextOutputFormat.setOutputPath(job,new Path("file:///e:\\result"));
运行程序
右键初始化类 --> Run as
# 如出现以下异常 Exception in thread "main" java.lang.UnsatisfiedLinkError: org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z # 解决方案: 1. 在项目的根目录中新建包 org.apache.hadoop.io.nativeio 2. 在包中新建类 NativeIO 3. 找到Hadoop的NativeIO类将所有的代码复制到自建的NativeIO中 4. 修改NativeIO中的源码(关联源码是557,未关联287行),将return true; 5. 重新运行,得到运行结果 ```![在这里插入图片描述]()
本地计算 + 远程数据
- 修改初始化类
//3. 指定计算数据的来源位置以及计算结果的输出位置
TextInputFormat.addInputPath(job,new Path("hdfs://hadoop:9000/dingl/data.txt"));
// 注意:计算结果的输出目录必须不存在
TextOutputFormat.setOutputPath(job,new Path("hdfs://hadoop:9000/dingl/result3"));
- 运行程序
右键初始化类 --> Run as
- 访问控制异常,添加虚拟机参数
-DHADOOP_USER_NAME=root
远程计算 + 远程数据
远程计算指MapReduce应用程序依然运行在YARN集群中
远程数据指数据依赖来源于HDFS或者输出到HDFS
修改初始化类
// 添加远程计算的支持 //=============================================================== conf.set("fs.defaultFS", "hdfs://hadoop:9000/"); conf.set("mapreduce.job.jar", "file:///F:\\IdeaProjects\\20190812\\hadoop-mapreduce\\target\\hadoop-mapreduce-1.0-SNAPSHOT.jar"); conf.set("mapreduce.framework.name", "yarn"); conf.set("yarn.resourcemanager.hostname", "hadoop"); conf.set("yarn.nodemanager.aux-services", "mapreduce_shuffle"); conf.set("mapreduce.app-submission.cross-platform", "true"); conf.set("dfs.replication", "1"); //===============================================================
将Maven项目重新打包
maven plugin ---> package---> xxx.jar
运行程序
右键初始化类 --> Run as
项目练习
- 有某系统的访问日志的样例数据,访问日志的格式如下:
# 客户端的ip地址 请求时间 请求方式 访问资源 响应的字节大小 状态码
192.168.0.3 2019-08-14 15:30:15 GET /index.jsp 300 200
11.135.14.110 2019-08-14 15:32:10 POST /user/login.do 500 404
..,,
PV
(Page View): 系统的访问量mapreduce map: k: 日期 v: 1 reduce: k: 日期 values: [1,1,1,1]
UV
(Unique View): 独立用户的访问量mapreduce map: k: 日期 v:ip reduce: k: 日期 values:[ip,ip,ip] values ---> Set
MapReduce程序的运行流程
MapReduce任务提交的源码剖析
InputFormat和OutputFormat
InputFormat
InputFormat数据的输入格式对象
[
TextInputFormat为例探讨背后事情
getSplits
createRecordReader
结论:
InputFormat
决定了如何对计算的数据集进行逻辑切割(140.8MB)
InputFormat
决定了如何解析读取数据切片(split)中的数据内容,并且map任务的keyIn和valueIn的类型由RecordReader中的key、value决定。- 一个
inputSplit
会由一个Map任务进行映射处理inputformat
负责输入数据的合法性校验
常见的InputFormat
FileInputFormat
TextInputFormat
: 基于文本的数据输入格式对象特点:按行读取文本中的数据,KeyIn:LongWritable ValueIn:Text
NLineInputFormat
特点:将文本中的N行(默认为1行)数据作一个数据切片,KeyIn:LongWritable ValueIn:Text
设置N行:
conf.set("mapreduce.input.lineinputformat.linespermap","3");
KeyValueLineRecordReader
特点:按照KV解析文本中的数据. KeyIn:Text ValueIn:Text
数据切片的计算规则等同于
TextInputFormat
数据切片的读取方式按照KV的结构进行解析
mapreduce.input.keyvaluelinerecordreader.key.value.separator
默认为\t
例如:
conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator",",");
FixedLengthInputFormat
CombineTextInputFormat
特点:将多个小文件的内容整合到一个数据切片中, KeyIn:LongWritable ValueIn: Text
DBInputFormat
DBInputFormat
特点: 从数据库中获取数据,将获得的数据作为Map任务的输入
KeyIn: LongWritable ValueIn:extends DBWritable
开发自定义的Writable对象,读写数据库表中的记录
package com.dingl.inputformat.db; import org.apache.hadoop.mapreduce.lib.db.DBWritable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Date; /** * 通过OrderWritable对象读写数据库的记录 */ public class OrderWritable implements DBWritable { private Integer orderId; private Double totalMoney; private Date createTime; private Integer userId; public OrderWritable() { } public OrderWritable(Integer orderId, Double totalMoney, Date createTime, Integer userId) { this.orderId = orderId; this.totalMoney = totalMoney; this.createTime = createTime; this.userId = userId; } public void write(PreparedStatement pstm) throws SQLException { pstm.setInt(2, this.orderId); pstm.setDouble(3, this.totalMoney); java.sql.Date date = new java.sql.Date(this.createTime.getTime()); pstm.setDate(4, date); pstm.setInt(5, this.userId); } public void readFields(ResultSet rs) throws SQLException { this.orderId = rs.getInt("order_id"); this.totalMoney = rs.getDouble("total_money"); this.createTime = rs.getDate("create_time"); this.userId = rs.getInt("user_id"); } }
开发处理的Map任务
package com.dingl.inputformat.db; import org.apache.hadoop.io.DoubleWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; import java.util.Date; public class OrderMapper extends Mapper<LongWritable, OrderWritable, Text, DoubleWritable> { /** value: 数据库一行记录 */ @Override protected void map(LongWritable key, OrderWritable value, Context context) throws IOException, InterruptedException { Date createTime = value.getCreateTime(); Integer userId = value.getUserId(); Double totalMoney = value.getTotalMoney(); String month = createTime.getYear() + "-" + createTime.getMonth() + "-" + userId; context.write(new Text(month), new DoubleWritable(totalMoney)); } }
开发统计的Reduce任务
package com.dingl.inputformat.db; import org.apache.hadoop.io.DoubleWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; import java.util.Iterator; public class OrderReducer extends Reducer<Text, DoubleWritable, Text, DoubleWritable> { @Override protected void reduce(Text key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException { double sum = 0.0D; Iterator<DoubleWritable> iterator = values.iterator(); while (iterator.hasNext()) { DoubleWritable money = iterator.next(); sum += money.get(); } context.write(key, new DoubleWritable(sum)); } }
设置初始化类
package com.dingl.inputformat.db; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.DoubleWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapred.lib.db.DBInputFormat; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.db.DBConfiguration; import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat; import org.iq80.leveldb.DB; import java.io.IOException; public class OrderComputApplication { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration configuration = new Configuration(); // 设置数据源信息 configuration.set(DBConfiguration.DRIVER_CLASS_PROPERTY,"com.mysql.jdbc.Driver"); configuration.set(DBConfiguration.URL_PROPERTY,"jdbc:mysql://localhost:3306/vue"); configuration.set(DBConfiguration.USERNAME_PROPERTY,"root"); configuration.set(DBConfiguration.PASSWORD_PROPERTY,"root"); Job job = Job.getInstance(configuration, "order"); job.setJarByClass(OrderComputApplication.class); job.setInputFormatClass(DBInputFormat.class); job.setOutputFormatClass(TextOutputFormat.class); // select order_id,total_money... from t_order where ... order by ... DBInputFormat.setInput(job,OrderWritable.class,"t_order",null,null, "order_id","total_money","create_time","user_id"); TextOutputFormat.setOutputPath(job,new Path("file:///E:/result5")); job.setMapperClass(OrderMapper.class); job.setReducerClass(OrderReducer.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(DoubleWritable.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(DoubleWritable.class); job.waitForCompletion(true); } }
引入数据源驱动的Jar包
本地计算:在Maven项目中导入mysql的依赖即可
远程计算:将MySQL的驱动jar包上传到
hadoop安装目录的/share/hadoop/yarn/lib
中
OutputFormat
OutputFormat数据的输出格式对象,决定了如何将Reducer的计算结果输出到指定的存储系统中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1GZipV9-1570095866933)(assets/1565920835698.png)]
常见的OutputFormat
FileOutputFormat:基于文件的数据输出格式
TextOutputFormat
特点: 计算的结果以文本的形式保存在文件中,文本中一行结果为Reduce方法的keyOut valueOut
DBOutputFormat: 基于数据库的数据输出格式
DBOutputFormat
特点:将Reducer的计算结果输出保存到数据库,reduce方法每输出一次则在数据库产生一条记录
TableOutputFormat: 基于HBase的数据输出格式
OutputFormat作用
结论:
- 决定了计算的结果以何种格式保存到指定的存储系统中
- 校验计算结果的输出位置是否合法
Shuffle原理剖析
Shuffle,是指对Map输出结果进行分区、排序、合并等处理并交给Reduce的过程。分为Map端的操作和Reduce端的操作。
Shuffle过程
Map端的Shuffle
Map的输出结果首先被缓存到内存,当缓存区容量到达80%(缓冲区默认100MB),就启动溢写操作。当启动溢写操作时,首先需要把缓存中的数据进行分区,然后对每个分区的数据进行排序和合并(combine),之后再写入磁盘文件。每次溢写操作会生成一个新的磁盘文件,随着Map任务的执行,磁盘中就会生成多个溢写文件。在Map任务全部结束前,这些溢写文件会被归并成一个大的磁盘文件,然后通知相应的Reduce任务来领取属于自己处理的数据。
在Reduce端的Shuffle过程
Reduce任务从Map端的不同Map机器领回属于自己处理的那部分数据,然后对数据进行合并排序后交给Reduce处理
作用
保证每一个Reduce任务处理的数据大致是一致的
Map任务输出的key相同,一定是相同分区,并且肯定是相同的Reduce处理的,保证计算结果的准确性
Reduce任务的数量决定了分区的数量,Reduce任务越多计算处理的并行度也就越高
Reduce任务的数量(默认为1)可以通过:
job.setNumReduceTasks(数量)
特点
- Map端溢写时,key相同的一定是在相同的分区
- Map端溢写时,排序减少了Reduce的全局排序的复杂度
- Map端溢写是,合并(combiner【可选】)减少溢写文件的体积,提高了Reduce任务在Fetch数据时的效率,它是一种MapReduce优化策略
- Reduce端计算或者输出时,它的数据都是有序的
Shuffle源码追踪
- MapTask
ReduceTask
(略)
建议阅读
数据清洗
数据清洗指将原始数据处理成有价值的数据的过程,就称为数据清洗。
企业大数据开发的基本流程:
采集数据(flume、logstash)先保存到MQ(Kafka)中
将MQ中的暂存数据存放到HDFS中保存
数据清洗(低价值密度的数据处理)存放到HDFS
算法干预(MapReduce),计算结果保存到HDFS或者HBase
计算结果的可视化展示(Echarts、HCharts)
需求
现有某系统某天的Nginx的访问日志,格式如下:
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/faq.gif HTTP/1.1" 200 1127
110.52.250.126 - - [30/May/2013:17:38:20 +0800] "GET /data/cache/style_1_widthauto.css?y7a HTTP/1.1" 200 1292
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/hot_1.gif HTTP/1.1" 200 680
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/common/hot_2.gif HTTP/1.1" 200 682
27.19.74.143 - - [30/May/2013:17:38:20 +0800] "GET /static/image/filetype/common.gif HTTP/1.1" 200 90
大数据处理的算法,需要参数客户端的ip地址、请求时间、资源、响应状态码
正则表达式提取数据
Regex Expression主要作用字符串
匹配
、抽取
和替换
语法
规则 | 解释 |
. | 匹配任意字符 |
\d | 匹配任意数字 |
\D | 匹配任意非数字 |
\w | 配置a-z和A-Z |
\W | 匹配非a-z和A-Z |
\s | 匹配空白符 |
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾 |
规则的匹配次数
语法 | 解释 |
* | 规则匹配0到N次 |
? | 规则匹配1次 |
{n} | 规则匹配N次 |
{n,m} | 规则匹配n到m次 |
+ | 规则匹配1到N次(至少一次) |
应用
# 匹配手机号码 11位数值构成
\d{11}
# 邮箱地址校验 @
.+@.+
使用正则表达式提取Nginx访问日志中的四项指标
测试站点:http://regex101.com
分析后得到需要的正则表达式
^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*\[(.*)\]\s"\w*\s(.*)\sHTTP\/1.1"\s(\d{3}).*$
使用MapReduce分布式并行计算框架进行数据清洗
注意: 因为数据清洗不涉及统计计算,所以MapReduce程序通常只有map任务,而没有Reduce任务
job.setNumReduceTasks(0)
实现代码
数据清洗的Mapper
package com.dingl.dataclean;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DataCleanMapper extends Mapper<LongWritable, Text, Text, NullWritable> {
/**
* @param key
* @param value nginx访问日志中的一行记录(原始数据)
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
final String regex = "^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}).*\\[(.*)\\]\\s\"\\w*\\s(.*)\\sHTTP\\/1.1\"\\s(\\d{3}).*$";
String line = value.toString();
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
// 四项关键指标 ip 请求时间 请求资源 响应状态码
String clientIp = matcher.group(1);
// yyyy-MM-dd HH:mm:ss
String accessTime = matcher.group(2);
String accessResource = matcher.group(3);
String status = matcher.group(4);
// 30/May/2013:17:38:21 +0800
// 30/05/2013:17:38:21
SimpleDateFormat sdf = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss", Locale.ENGLISH);
try {
Date date = sdf.parse(accessTime);
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String finalDate = sdf2.format(date);
context.write(new Text(clientIp + " " + finalDate + " " + accessResource + " " + status), null);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
初始化类
package com.dingl.dataclean;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import java.io.IOException;
public class DataCleanApplication {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Job job = Job.getInstance(new Configuration(), "data clean");
job.setJarByClass(DataCleanApplication.class);
job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);
TextInputFormat.setInputPaths(job,new Path("file:///E:/access.log"));
TextOutputFormat.setOutputPath(job,new Path("file:///E:/final"));
job.setMapperClass(DataCleanMapper.class);
// 注意:数据清洗通常只有map任务而没有reduce
job.setNumReduceTasks(0);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(NullWritable.class);
job.waitForCompletion(true);
}
}
数据倾斜
数据分区默认策略
数据倾斜指大量的key相同的数据交由一个reduce任务统计计算,造成”闲的闲死,忙的忙死“这样的现象。不符合分布式并行计算的设计初衷的。
现象
某一个reduce运行特别耗时
Reduce任务内存突然溢出
解决方案
增大Reduce任务机器JVM的内存(硬件的水平扩展)
增加Reduce任务的数量,每个Reduce任务只负责极少部分的数据处理,并且Reduce任务的数量增加提高了数据计算的并行度
Reduce任务的正确数量: 0.95或者1.75 * (NodeManage数量 * 每个节点最大容器数量)
- 自定义分区规则Partitioner
package com.dingl.partition;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapreduce.Partitioner;
/**
* 自定义分区规则
*/
public class CustomPartitioner extends Partitioner<Text, LongWritable> {
/**
* @param key
* @param value
* @param i numReduceTasks
* @return 分区序号
*/
public int getPartition(Text key, LongWritable value, int i) {
if (key.toString().equals("CN-GD")) return 0;
else if (key.toString().equals("CN-GX")) return 1;
else if (key.toString().equals("CN-HK")) return 2;
else if (key.toString().equals("JP-TY")) return 3;
else return 4;
}
}
- 合适使用
Combiner
,将key相同的value进行整合合并
在combiner合并时,v必须得能支持迭代计算,并且不能够影响Reduce任务的输入
combiner通常就是Reducer任务
// 优化策略:combiner合并操作
job.setCombinerClass(MyReducer.class);
五、Hadoop完全分布式集群
架构
环境搭建
准备3台虚拟机
- Node1:
192.168.12.130
- Node2:
192.168.12.131
- Node3:
192.168.12.132
服务划分
服务名 | node1 | node2 | node3 |
Namenode | Y(主) | Y(备) | |
Datanode | Y | Y | Y |
Journal node | Y | Y | Y |
Zookeeper | Y | Y | Y |
ResourceManager | Y(主) | Y(备) | |
NodeManger | Y | Y | Y |
配置步骤
准备安装包
# centos7
# jdk8
# hadoop-2.6.0
# zookeeper
修改主机名和IP地址的映射文件
[root@nodex ~]# vi /etc/hosts
192.168.12.130 node1
192.168.12.131 node2
192.168.12.132 node3
关闭防火墙
[root@nodex ~]# systemctl stop firewalld
[root@nodex ~]# systemctl disable firewalld
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
SSH免密登陆
[root@nodex ~]# ssh-keygen -t rsa
[root@nodex ~]# ssh-copy-id node1
[root@nodex ~]# ssh-copy-id node2
[root@nodex ~]# ssh-copy-id node3
同步时钟
[root@nodeX ~]# date -s '2018-12-1 20:06:00'
2018年 12月 01日 星期六 20:06:00 CST
[root@nodeX ~]# clock -w
[root@nodeX ~]# date
2018年 12月 01日 星期六 20:06:09 CST
注意:服务器集群时钟如果一致,可以跳过此步骤
修改服务器的主机名
[root@x ~]# vi /etc/hostname
# 192.168.12.130
node1
# 192.168.12.131
node2
# 192.168.12.132
node3
重启机器,生效
安装JDK
# 将集群搭建的安装包上传到某节点
# 利用scp命令拷贝到其它节点
[root@node1 ~]# scp jdk-8u191-linux-x64.rpm root@node2:~
jdk-8u191-linux-x64.rpm
[root@node1 ~]# scp jdk-8u191-linux-x64.rpm root@node3:~
jdk-8u191-linux-x64.rpm
安装ZooKeeper
[root@node1 ~]# scp zookeeper-3.4.6.tar.gz root@node2:~
zookeeper-3.4.6.tar.gz 100% 17MB 84.3MB/s 00:00
[root@node1 ~]# scp zookeeper-3.4.6.tar.gz root@node3:~
zookeeper-3.4.6.tar.gz 100% 17MB 73.4MB/s 00:00
[root@nodex ~]# tar -zxf zookeeper-3.4.6.tar.gz -C /usr
[root@nodex ~]# vi /usr/zookeeper-3.4.6/conf/zoo.cfg
tickTime=2000
dataDir=/root/zkdata
clientPort=2181
initLimit=5
syncLimit=2
server.1=node1:2887:3887
server.2=node2:2887:3887
server.3=node3:2887:3887
[root@nodex ~]# mkdir -p /root/zkdata
# node1执行此指令
[root@node1 ~]# cd zkdata/
[root@node1 zkdata]# vi myid
1
# node2执行此指令
[root@node2 ~]# cd zkdata/
[root@node2 zkdata]# vi myid
2
# node3执行此指令
[root@node3 ~]# cd zkdata/
[root@node3 zkdata]# vi myid
3
# 启动ZooKeeper集群
[root@nodex ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh start /usr/zookeeper-3.4.6/conf/zoo.cfg
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/conf/zoo.cfg
Starting zookeeper ... STARTED
# 确认zookeper服务是否正常:方法一
[root@nodex ~]# jps
1777 QuorumPeerMain
1811 Jps
# 确认zookeper服务是否正常:方法二
[root@nodex ~]# /usr/zookeeper-3.4.6/bin/zkServer.sh status /usr/zookeeper-3.4.6/conf/zoo.cfg
JMX enabled by default
Using config: /usr/zookeeper-3.4.6/conf/zoo.cfg
Mode: leader
安装Hadoop
#1. 将hadoop的安装包远程拷贝到其它的节点
[root@node1 ~]# scp hadoop-2.6.0_x64.tar.gz root@node2:~
hadoop-2.6.0_x64.tar.gz 100% 172MB 113.8MB/s 00:01
[root@node1 ~]# scp hadoop-2.6.0_x64.tar.gz root@node3:~
hadoop-2.6.0_x64.tar.gz 100% 172MB 120.8MB/s 00:01
#2. 安装
[root@nodex ~]# tar -zxf hadoop-2.6.0_x64.tar.gz -C /usr
#3. 配置java和hadoop的环境变量
[root@nodex ~]# vi ~/.bashrc
HADOOP_HOME=/usr/hadoop-2.6.0
JAVA_HOME=/usr/java/latest
CLASSPATH=.
PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin
export JAVA_HOME
export CLASSPATH
export PATH
export HADOOP_HOME
[root@nodex ~]# source ~/.bashrc
修改hadoop的配置文件
core-site.xml
[root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/core-site.xml
<property>
<name>fs.defaultFS</name>
<value>hdfs://mycluster</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>/usr/hadoop-2.6.0/hadoop-${user.name}</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>30</value>
</property>
<property>
<name>net.topology.script.file.name</name>
<value>/usr/hadoop-2.6.0/etc/hadoop/rack.sh</value>
</property>
- 创建机架脚本文件,该脚本可以根据IP判断机器所处的物理位置
[root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/rack.sh
while [ $# -gt 0 ] ; do
nodeArg=$1
exec</usr/hadoop-2.6.0/etc/hadoop/topology.data
result=""
while read line ; do
ar=( $line )
if [ "${ar[0]}" = "$nodeArg" ] ; then
result="${ar[1]}"
fi
done
shift
if [ -z "$result" ] ; then
echo -n "/default-rack"
else
echo -n "$result "
fi
done
[root@nodex ~]# chmod u+x /usr/hadoop-2.6.0/etc/hadoop/rack.sh
[root@nodeX ~]# vi /usr/hadoop-2.6.0/etc/hadoop/topology.data
192.168.12.130 /rack1
192.168.12.131 /rack2
192.168.12.132 /rack2
[root@nodeX ~]# /usr/hadoop-2.6.0/etc/hadoop/rack.sh 192.168.23.137
/rack1
hdfs-site.xml
[root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/hdfs-site.xml
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<property>
<name>ha.zookeeper.quorum</name>
<value>node1:2181,node2:2181,node3:2181</value>
</property>
<property>
<name>dfs.nameservices</name>
<value>mycluster</value>
</property>
<property>
<name>dfs.ha.namenodes.mycluster</name>
<value>nn1,nn2</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn1</name>
<value>node1:9000</value>
</property>
<property>
<name>dfs.namenode.rpc-address.mycluster.nn2</name>
<value>node2:9000</value>
</property>
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://node1:8485;node2:8485;node3:8485/mycluster</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.mycluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
slaves
[root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/slaves
node1
node2
node3
启动HDFS HA集群的服务
[root@nodex ~]# hadoop-daemon.sh start journalnode
[root@node1 ~]# hdfs namenode -format
[root@node1 ~]# hadoop-daemon.sh start namenode
[root@node2 ~]# hdfs namenode -bootstrapStandby
[root@node2 ~]# hadoop-daemon.sh start namenode
# zkfc: zookeeper failover controller
[root@node1|2 ~]# hdfs zkfc -formatZK (可以在node1或者node2任意一台注册namenode信息)
[root@node1 ~]# hadoop-daemon.sh start zkfc (哨兵)
[root@node2 ~]# hadoop-daemon.sh start zkfc (哨兵)
[root@nodeX ~]# hadoop-daemon.sh start datanode
注意:CentOS7需要安装一个中间依赖服务
[root@nodex ~]# yum install -y psmisc
YARN的HA集群
mapred-site.xml
[root@nodex ~]# cp /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml.template /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml [root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml <property> <name>mapreduce.framework.name</name> <value>yarn</value> </property>
yarn-site.xml
<property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.cluster-id</name> <value>cluster1</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>node2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>node3</value> </property> <property> <name>yarn.resourcemanager.zk-address</name> <value>node1:2181,node2:2181,node3:2181</value> </property>
启动YARN
[root@node2 ~]# yarn-daemon.sh start resourcemanager [root@node3 ~]# yarn-daemon.sh start resourcemanager [root@nodeX ~]# yarn-daemon.sh start nodemanager
查看ResourceManager HA状态
[root@node1 ~]# yarn rmadmin -getServiceState rm1 active [root@node1 ~]# yarn rmadmin -getServiceState rm2 standby
p-daemon.sh start namenode
zkfc: zookeeper failover controller[root@node1|2 ~]# hdfs zkfc -formatZK (可以在node1或者node2任意一台注册namenode信息)
[root@node1 ~]# hadoop-daemon.sh start zkfc (哨兵)
[root@node2 ~]# hadoop-daemon.sh start zkfc (哨兵)
[root@nodeX ~]# hadoop-daemon.sh start datanode
> 注意:CentOS7需要安装一个中间依赖服务
>
> `[root@nodex ~]# yum install -y psmisc`
#### YARN的HA集群
- `mapred-site.xml`
```xml
[root@nodex ~]# cp /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml.template /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml
[root@nodex ~]# vi /usr/hadoop-2.6.0/etc/hadoop/mapred-site.xml
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
yarn-site.xml
<property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.ha.enabled</name> <value>true</value> </property> <property> <name>yarn.resourcemanager.cluster-id</name> <value>cluster1</value> </property> <property> <name>yarn.resourcemanager.ha.rm-ids</name> <value>rm1,rm2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm1</name> <value>node2</value> </property> <property> <name>yarn.resourcemanager.hostname.rm2</name> <value>node3</value> </property> <property> <name>yarn.resourcemanager.zk-address</name> <value>node1:2181,node2:2181,node3:2181</value> </property>
启动YARN
[root@node2 ~]# yarn-daemon.sh start resourcemanager [root@node3 ~]# yarn-daemon.sh start resourcemanager [root@nodeX ~]# yarn-daemon.sh start nodemanager
查看ResourceManager HA状态
[root@node1 ~]# yarn rmadmin -getServiceState rm1 active [root@node1 ~]# yarn rmadmin -getServiceState rm2 standby