几个重要的流程理解:
1.HDFS的读写流程
2.NameNode启动流程
3.YARN的架构
4.MapReduce的计算流程
输入—map()–shuffle–reduce()—输出
map()方法的输出:context.write(key, value) key:1
reduce()方法的输入:key {1,1,1}
shuffle两个阶段:
map()方法结束
第一阶段:map shuffle
第二阶段:reduce shuffle
reduce()方法执行
map阶段:
map的输出结果会直接写到本地磁盘(map方法运行的NodeManager上)
shuffle的流程
map side阶段:
1. map输出以后,并没有直接写入磁盘,而是使用了缓存,进行了一些预排序的过程,
这样可以使mapreduce更有效率
2. 每一个map方法都具有一个环形缓冲区(circular memory buffer),这里是先接受
map输出的地方。环形缓冲区大小默认100M,可以在mapreduce.task.io.sort.mb属性
中设置。当缓冲区达到80%的时候,就会溢写到磁盘。
3. partition阶段在写到磁盘之前,会首先被分区,而分区的数量和最终要发送到的
reducer的数量是一致的。
4. 在每一个分区中都进行了一次sort by key
5. 如果有一个combiner的函数,那么此时会将排序的输出作为输入,进行执行。
combiner实际是一个在map端执行的reducer
6. merge:在map task结束之前,所有的spills file会被合并成一个分过区的排过序
的文件在这里,一次合并的小文件的数量配置:mapreduce.task.io.sort.factor,
默认是10
7. compress压缩,设置mapreduce.map.output.compress=true
网络传输
reduce side
1. reduce会从不同的服务器上将对应分区的结果文件拷贝到本地
2. map端与ApplicationMaster保持一个心跳,当map端执行完成时,会通知
ApplicationMaster,reduce端会随时询问ApplicationMaster,一旦得知有map
执行完成,就会去下载输出文件
3. 被拷贝来的map输出文件如果是被压缩过的,那么在执行合并之前要进行解压缩。
4. sort阶段:When all the map outputs have been copied, the reduce task
moves into the sort phase
5. merge:merges the map outputs, maintaining their sort ordering
默认一次合并文件的数量是10,设置: mapreduce.task.io.sort.factor
6. 在合并之后会发送给reduce处理(排过序)
reduce处理:(key ,{1,1,1..})
处理的结果会直接发送到HDFS(流式写入)
注意:
map的数量和split的数量是一致的
split的数量和block的数量关系是可以配置的,默认情况下是1:1
默认情况下:一个block就是一个map
如果200M一个文件:会有2个map任务执行
如果有1一个文件是10M,另一个文件是100M:会有2个map任务执行
partition的数量和reducer的数量是一致的,默认reducer的数量是1
reducer的数量决定了partition的数量
实现Partition
根据provinceId除以3余数不同,来分成3个区
package com.ibeifeng.hadoop;
import java.io.IOException;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class PVMapReduce {
public static void main(String[] args) throws Exception {
//1.get Job
Job job = Job.getInstance(new Configuration());
//2.set Jar
job.setJarByClass(PVMapReduce.class);
//3.set Mapper
job.setMapperClass(PVMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
//3.5 set Partition
job.setPartitionerClass(MyPartitioner.class);
//set number of Reducer
job.setNumReduceTasks(new Integer(args[2]));
//4.set Reducer
job.setReducerClass(PVReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(LongWritable.class);
//5.PathIn PathOut
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//6.submit
job.waitForCompletion(true);
}
//map method
public static class PVMapper extends Mapper{
Text text = new Text();
LongWritable longWritable = new LongWritable(1);
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line = value.toString();
String[] split = line.split("\t");
// length >= 30 is useful
if (split.length < 30) {
//counter defination
context.getCounter("my error counter", "length is less than 30").increment(1);
return;
}
// url is null is useless
if ( StringUtils.isBlank(split[1])) {
context.getCounter("my error counter", "url is null").increment(1);
return;
}
// provinceId null is useless
if (StringUtils.isBlank(split[23])) {
context.getCounter("my error counter", "provinceId is null").increment(1);
return;
}
// provinceId should be a number
try {
new Integer(split[23]);
} catch (NumberFormatException e) {
context.getCounter("my error counter", "provinceId is not a number").increment(1);
e.printStackTrace();
return ;
}
//text.set(provinceId)
text.set(split[23]);
context.write(text, longWritable);
}
}
/*
* 自定义分区
* partition的输入是map的输出
* numPatitions:传来的partition的数量
*/
public static class MyPartitioner extends Partitioner{
/*
* return的值就是partition
* 在这里就分成了3个区
*/
@Override
public int getPartition(Text key, LongWritable value, int numPartitions) {
Integer num = Integer.valueOf(key.toString());
if (num % 3 == 0) {
return 0;
}else if (num % 3 == 1) {
return 1;
}
return 2;
}
}
public static class PVReducer extends Reducer{
LongWritable value = new LongWritable();
@Override
protected void reduce(Text key, Iterable iterable,
Context context)
throws IOException, InterruptedException {
long count = 0;
for (LongWritable longWritable : iterable) {
long l = longWritable.get();
count += l;
}
value.set(count);
//key:provinceId value:number of provinceId
context.write(key, value);
}
}
}
执行:
$ ./hadoop jar /opt/data/pvcountpar.jar /nicole/input/2015082818 /nicole/outputpvcount_par1 1
此时:设置reducer的数量是1,那么partition的数量也就是1
$ ./hadoop jar /opt/data/pvcountpar.jar /nicole/input/2015082818 /nicole/outputpvcount_par3 3
此时:设置reducer的数量是3,那么partition的数量也就是3
$ ./hadoop jar /opt/data/pvcountpar.jar /nicole/input/2015082818 /nicole/outputpvcount_par4 4
此时:设置reducer的数量是4,那么partition的数量也就是4
然而part-r-00003文件中是空的
combiner函数
public static class MyCombiner extender Reducer<>{
}
main(){
//设置可插拔的Combiner
job.setCombinerClass(MyCombiner.class);
}
实际上Combiner就是在map shuffle阶段执行一次reducer
mapreduce参数调优
mapreduce.map.cpu.vcores
每个Map Task需要的虚拟CPU个数
mapreduce.reduce.cpu.vcores
每个Reduce Task需要的虚拟CPU个数
mapreduce.job.reduces:
默认值:1
说明:默认启动的reduce数。通过该参数可以手动修改reduce的个数。
mapreduce.task.io.sort.factor:
默认值:10
说明:Reduce Task中合并小文件时,一次合并的文件数据,
每次合并的时候选择最小的前10进行合并。
mapreduce.task.io.sort.mb:
默认值:100
说明: Map Task缓冲区所占内存大小。
mapreduce.reduce.shuffle.parallelcopies:
默认值:5
说明:reduce shuffle阶段并行传输数据的数量。集群大可以增大为10或更大。
mapreduce.map.output.compress:
默认值:false
说明: map输出是否进行压缩,如果压缩就会多耗cpu,但是减少传输时间,
如果不压缩,就需要较多的传输带宽。配合 mapreduce.map.output.compress.codec使用,默认是 org.apache.hadoop.io.compress.DefaultCodec,
可以根据需要设定数据压缩方式。
mapreduce.reduce.shuffle.merge.percent:
默认值: 0.66
说明:reduce归并接收map的输出数据可占用的内存配置百分比。
RPC远程过程调用
hadoop各进程之间的通信就是建立在RPC通信的基础上构建的。
http://blog.csdn.net/thomas0yang/article/details/41211259
========================================================================
分布式系统搭建
集群规划:构建3台服务器
PC1 PC2 PC3
HDFS NameNode SecondaryNameNode
DataNode DataNode DataNode
Yarn ResourceManager
NodeManager NodeManager NodeManager
第一步:准备3台服务器
创建3个虚拟机,建议大家直接安装,
或者克隆,或者直接拷贝文件
(这样的话需要修改MAC地址,以及部分配置文件)
vi /etc/sysconfig/network-scripts/ifcfg-eth0
vi /etc/udev/rules.d/70-persistent-net.rules
第二步:准备系统环境
1. 设置主机名(三台PC)
$ vi /etc/sysconfig/network
HOSTNAME=hadoop.ibeifeng.com.cn01
设置hosts文件(三台PC)
192.168.230.101 101
192.168.230.102 102
192.168.230.103 103
在 windows系统上也要配置hosts文件
192.168.230.101 101
192.168.230.102 102
192.168.230.103 103
关闭防火墙
/# service iptables stop
/# chkconfig iptables off
关闭selinux
/# vi /etc/sysconfig/selinux
SELINUX=disabled
/# reboot 重启电脑
配置静态IP和DNS [root用户] [三台服务器]
/# vi /etc/sysconfig/network-scripts/ifcfg-eth0
配置IP地址、DNS地址(网管地址)
创建普通用户(三台PC)
/# useradd nicole
/# passwd nicole 设置密码
注意:三台PC要使用同一个用户
可选操作(修改启动级别)
/# vi /etc/inittab
id:5:initdefault: #代表重启进入图形化界面
id:3:initdefault: #代表进入cli界面
卸载系统自带的JDK
rpm -qa | grep java #查询系统是否自带jdk rpm -e java(软件名) –nodeps #卸载
第三步:配置NTP时间服务器
1)配置第一台PC的时间服务器
2)让其他的PC来同步PC1的时间
在第一台PC上进行设置:
查看目前系统使用的时区
$ date -R
Sun, 16 Oct 2016 23:35:08 -0700
如果得到的结果不是+0800,那么就需要修改时区
删除目前系统使用的时区文件
# rm -f /etc/localtime
创建软链接文件/etc/localtime指向/usr/share/zoneinfo/Asia/Shanghai
# ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# date -R 查看,此时时区已经是+0800
调整当前时间,与当前时间同步
# ntpdate cn.pool.ntp.org
使用ntpdate命令之前可以检查此软件的安装情况
# rpm -qa | grep ntp
ntpdate-4.2.4p8-3.el6.centos.x86_64
fontpackages-filesystem-1.41-1.1.el6.noarch
ntp-4.2.4p8-3.el6.centos.x86_64
如果没有安装这个软件,可以使用yum安装
# yum -y install ntp
修改ntp的配置文件
# vi /etc/ntp.conf
修改1:去掉下面这行的#,将网段改为自己的网段:192.168.234.0
restrict 192.168.234.0 mask 255.255.255.0 nomodify notrap
修改2:注释掉以下几行内容
#server 0.centos.pool.ntp.org
#server 1.centos.pool.ntp.org
#server 2.centos.pool.ntp.org
修改3:去掉如下两行前的#,如没有需要 自己添加
server 127.127.1.0 # local clock
fudge 127.127.1.0 stratum 10
启动NTP服务
# service ntpd start
在启动电脑的时候自动启动
# chkconfig ntpd on
到另外两台PC(PC2 PC3)关闭ntpd进程
# service ntpd stop
# chkconfig ntpd off
同步第一台PC的时间(PC2 PC3)
格式:ntpdate 开启ntp服务的主机名
# ntpdate hadoop.ibeifeng.com.cn01
设置计划任务,每十分钟同步一次时间(PC2 PC3)
# crontab -e
/10 * * * /usr/sbin/ntpdate hadoop101
第四步:SSH免密码登陆
在第一台主机上,生成公钥
ssh-keygen -t rsa
然后将公钥发送给其他主机
ssh-copy-id 其他主机名
此时,在其他的主机的私钥中就保存了加密的主机的公钥
完成之后既可以ssh直接登陆其他主机了
ssh 其他主机名
PC1-----PC1 PC2 PC3 设置ssh
# ssh-keygen
# ssh-copy-id hadoop.ibeifeng.com.cn01
# ssh-copy-id hadoop.ibeifeng.com.cn02
# ssh-copy-id hadoop.ibeifeng.com.cn03
PC2-----PC1 PC2 PC3 设置ssh
# ssh-keygen
# ssh-copy-id hadoop.ibeifeng.com.cn01
# ssh-copy-id hadoop.ibeifeng.com.cn02
# ssh-copy-id hadoop.ibeifeng.com.cn03
PC3-----PC1 PC2 PC3 设置ssh
# ssh-keygen
# ssh-copy-id hadoop.ibeifeng.com.cn01
# ssh-copy-id hadoop.ibeifeng.com.cn02
# ssh-copy-id hadoop.ibeifeng.com.cn03
第五步:安装jdk
在第一台主机上安装jdk
使用scp命令将jdk发送到另外两台主机
scp−rjdk1.7.067/hadoop.ibeifeng.com.cn02:/opt/modules/ scp -r jdk1.7.0_67/ hadoop.ibeifeng.com.cn03:/opt/modules/
配置环境变量
vi /etc/profile
## JAVA HOME
JAVA_HOME=/opt/modules/jdk1.7.0_67
export PATH=$PATH:$JAVA_HOME/bin
# source /etc/profile
第六步:安装hadoop
1.在PC1上安装hadoop
tar−zxvfhadoop−2.5.0.tar.gz−C/opt/modules/ rm -rf doc/
2.修改环境配置文件
hadoop-env.sh yarn-env.sh mapred-env.sh
export JAVA_HOME=/opt/modules/jdk1.7.0_67
3.修改配置文件
core-site.xml
fs.defaultFS
hdfs://hadoop.ibeifeng.com.cn01:8020
hadoop.tmp.dir
/opt/modules/hadoop-2.5.0/tmp
hdfs-site.xml
dfs.replication
3
dfs.namenode.secondary.http-address
hadoop.ibeifeng.com.cn03:50090
dfs.namenode.http-address
hadoop.ibeifeng.com.cn01:50070
dfs.permissions.enabled
false
mapred-site.xml
mapreduce.framework.name
yarn
mapreduce.jobhistory.address
hadoop.ibeifeng.com.cn01:10020
mapreduce.jobhistory.webapp.address
hadoop.ibeifeng.com.cn01:19888
yarn-site.xml
yarn.resourcemanager.hostname
hadoop.ibeifeng.com.cn02
yarn.nodemanager.aux-services
mapreduce_shuffle
yarn.log-aggregation-enable
true
yarn.log-aggregation.retain-seconds
86400
slaves
hadoop.ibeifeng.com.cn01
hadoop.ibeifeng.com.cn02
hadoop.ibeifeng.com.cn03
4.将hadoop文件夹发给另外两台主机
$ scp -r hadoop-2.5.0/ 192.168.234.102:/opt/modules/
$ scp -r hadoop-2.5.0/ 192.168.234.103:/opt/modules/
5.格式化
在第一台主机上格式化namenode
$ ./hdfs namenode -format
6.启动hdfs
在第一台主机上启动hdfs
$ ./start-dfs.sh
在第二台主机上启动yarn
$ ./start-yarn.sh
jps
PC1:
3150 NameNode
3237 DataNode
3693 Jps
3591 NodeManager
3487 JobHistoryServer
PC2:
5070 Jps
4828 DataNode
4949 ResourceManager
5040 NodeManager
PC3:
4856 SecondaryNameNode
5086 Jps
4983 NodeManager
4804 DataNode
web页面查看
hdfs:http://hadoop.ibeifeng.com.cn01:50070/
yarn:http://hadoop.ibeifeng.com.cn02:8088/cluster