大数据学习-Hadoop生态章
(一) HDFS
1.1.Hadoop简介
Hadoop是分布式的系统架构,是Apache基金会顶级金牌项目。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。
Hadoop的思想之源:
来自于Google 03年发布3大论文, GFS、mapreduce、 Bigtable ;Dougcutting用Java实现)
Hadoop创始人介绍:
Hadoop作者Doug cutting,就职Yahoo期间开发了Hadoop项目,目前在Cloudera 公司从事架构工作。
2003-2004年,Google公开了部分GFS和Mapreduce思想的细节,以此为基础Doug Cutting等人用了2年业余时间实现了DFS和Mapreduce机制,一个微缩版:Nutch
Hadoop 于 2005 年秋天作为 Lucene的子项目 Nutch的一部分正式引入Apache基金会。
2006 年 3 月份,Map-Reduce分布式离线计算 和 Nutch Distributed File System (NDFS) nutch分布式文件系统分别被纳入称为 Hadoop 的项目中。
(hadoop如今是Apache基金会顶级金牌项目)
Hadoop这名字来源于Doug Cutting儿子的玩具大象。
1.2.分布式文件存储系统—HDFS
1.2.1、HDFS是什么?
分布式存储系统HDFS (Hadoop Distributed File System)。
主要解决大数据的存储问题。
分布式:
将一庞大的数据或一个复杂的业务,分发到不同的计算机节点和服务器上面经行运行和处理。
1.2.2、HDFS的优缺点(重要)
优点:
(1) 分布式的特性
- 适合大数据处理:GB 、TB 、甚至PB 级及以上的数据
- 百万规模以上的文件数量:10K+ 节点。
- 适合批处理:移动计算而非数据(MR),数据位置暴露给计算框架
(2) 自身特性:
- 可构建在廉价机器上
- 高可靠性:通过多副本提提高
- 高容错性:数据自动保存多个副本;副本丢失后,自动恢复,提供了恢复机制
缺点:
- 低延迟、高数据 吞吐访问问题
比如不支持毫秒级
吞吐量大但有限制于其延迟
- 小文件存取占用NameNode大量内存(寻道时间超过读取时间(99%))
- 不支持文件修改:一个文件只能有一个写者(深入)
- 仅支持append不支持修改(其实本身是支持的,主要为了空间换时间,节约成本)
1.3.HDFS架构图(重点)
1.4.HDFS的功能模块及原理详解(非常重要)
1.4.1.HDFS 数据存储模型(block)
- 文件被线性切分成固定大小的数据块block
通过偏移量offset(单位:byte)标记;
默认数据块大小为64MB (hadoop1.x版本,hadoop2.x以上版本默认128MB),可自定义配置;
若文件大小不到64MB/128MB ,则单独存成一个block
- 一个文件存储方式
按大小被切分成若干个block ,存储到不同节点上;
默认情况下每个block都有2个副本 共3个副本;副本数不大于节点数
- Block大小和副本数通过Client端上传文件时设置,文件上传成功后
副本数可以变更
,Block Size大小不可变更
1.4.2.NameNode(简称NN)
- NameNode主要功能
- 接受客户端的读/写服务。
- 接受DN汇报的block位置信息。 - NameNode保存metadate(数据)元信息
- 基于内存存储 :不会和磁盘发生 交换; - metadate元数据信息包括以下:
-文件owership(归属)和permissions(权限)
- 文件大小 时间
- Block列表[偏移量]:即一个完整文件有哪些block(b0+b1+b2+…=file)
- 位置信息=Block每个副本保存在哪个DataNode中(由DataNode启动时上报 给NN 因为会随时变化,不保存在磁盘)–动态的!
- NameNode的metadate信息在启动后会加载到内存.。
- metadata存储到磁盘文件名为”fsimage”的镜像文件
- Block的位置信息不会保存到fsimage
- edits记录对metadata的操作日志
1.4.3.SecondaryNameNode(SNN)
- 帮助NN合并
edits log文件
和内存元数据信息
,落到磁盘形成fsimage镜像文件
,减少NN启动时间,它不是NN的备份(但可以做备份),保证数据安全。 - SNN执行合并时间和机制
- 根据配置文件设置的时间间隔fs.checkpoint.period 默认3600秒
- 根据配置文件设置edits log大小 fs.checkpoint.size 规定edits文件的最大值默认是64MB
1.4.4.SecondaryNameNode SNN合并流程
1.从NN拷贝edits 文件 和 fsimage镜像文件 ,edits变为一个新的edits.new文件
2.将两文件信息合并成文件fsimage.ckpt,并传回NN。
3.传回文件覆盖原来fsimage文件,edits.new变为edits文件
1.4.5.DataNode(DN)
- 存储数据(Block)
- 启动DN线程的时候会向NameNode汇报block位置信息
- 通过向NN发送心跳保持与其联系(3秒一次),如果NN
10分钟没有收到DN的心跳,则认为其已经lost,并copy其上的block到其它DN
1.4.6.Block的副本放置策略
- 第一个副本:集群内部提交放置在上传文件的DN;如果是集群外提交,则随机挑选一台磁盘不太满,CPU不太忙的节点。(掌控资源详情)
- 第二个副本:放置在于第一个副本不同的机架的节点上。
- 第三个副本:与第二个副本相同机架的不同节点。
- 更多副本:随机节点
1.4.7.HDFS读写流程
读文件过程
- 首先调用
FileSystem
对象的open
方法,其实是一个DistributedFileSystem的实例。 - DistributedFileSystem通过rpc协议获得文件的第一批block的locations地址,(同一个block按照重复数会返回多个locations,因为同一文件的block分布式存储在不同节点上),这些locations按照hadoop拓扑结构排序,距离客户端近的排在前面(就近原则选择)。
- 前两步会返回一个FSDataInputStream对象,该对象会被封装
DFSInputStream
对象,DFSInputStream可以方便的管理datanode和namenode数据流。客户端调用read
方法,DFSInputStream会找出离客户端最近的datanode并连接。 - 数据从datanode源源不断的流向客户端。
这些操作对客户端来说是透明的,客户端的角度看来只是读一个持续不断的流。
注:
如果第一批block都读完了, DFSInputStream就会去namenode拿下一批block的locations,然后继续读,如果所有的块都读完,这时就会关闭掉所有的流。
如果在读数据的时候, DFSInputStream和datanode的通讯发生异常,就会尝试正在读的block的排序第二近的datanode,并且会记录哪个datanode发生错误,剩余的blocks读的时候就会直接跳过该datanode。 DFSInputStream也会检查block数据校验和,如果发现一个坏的block,就会先报告到namenode节点,然后DFSInputStream在其他的datanode上读该block的镜像。
该设计就是客户端直接连接datanode来检索数据并且namenode来负责为每一个block提供最优的datanode, namenode仅仅处理block location的请求,这些信息都加载在namenode的内存中,hdfs通过datanode集群可以承受大量客户端的并发访问。
拓展了解:
RPC
(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。**RPC协议
**假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层
和应用层
。RPC使得开发包括网络分布式
多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式
。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
写文件流程
- .
- 客户端通过调用
DistributedFileSystem
的create
方法创建新文件。 - DistributedFileSystem通过RPC调用namenode去创建一个没有blocks关联的新文件,创建前, namenode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过, namenode就会记录下新文件,否则就会抛出IO异常。
- 前两步结束后,会返回
FSDataOutputStream
的对象,与读文件的时候相似,FSDataOutputStream被封装成DFSOutputStream
。DFSOutputStream
可以协调namenode和datanode。客户端开始写数据到DFSOutputStream
,DFSOutputStream
会把数据切成一个个小的packet
,然后排成队列data quene。 - DataStreamer会去处理接受data quene,它先询问namenode这个新的block最适合存储的在哪几个datanode里(比如重复数是3,那么就找到3个最适合的datanode),把他们排成一个管道pipeline输出。DataStreamer把packet按队列输出到管道的第一个datanode中,第一个datanode又把packet输出到第二个datanode中,以此类推。
- DFSOutputStream还有一个对列叫ack quene,也是由packet组成等待datanode的收到响应,当pipeline中的datanode都表示已经收到数据的时候,这时ack quene才会把对应的packet包移除掉。 如果在写的过程中某个datanode发生错误,会采取以下几步:
- pipeline被关闭掉;
- 为了防止防止丢包。ack quene里的packet会同步到data quene里;
- 创建新的pipeline管道怼到其他正常DN上
- 剩下的部分被写到剩下的两个正常的datanode中;
- namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。
- 客户端完成写数据后调用close方法关闭写入流。
- 深入DFSOutputStream内部原理
打开一个
DFSOutputStream流
,Client会写数据到流内部的一个缓冲区中,然后数据被分解成多个Packet
,每个Packet大小为64k字节
,每个Packet又由一组chunk和这组chunk对应的checksum数据组成,默认chunk大小为512字节,每个checksum是对512字节数据计算的校验和数据。===》当Client写入的字节流数据
达到一个Packet的长度
,这个Packet会被构建出来,然后会被放到队列dataQueue中
,接着DataStreamer线程
会不断地从dataQueue队列中取出Packet,发送到复制Pipeline中的第一个DataNode上,并将该Packet
从dataQueue队列中移到ackQueue
队列中。ResponseProcessor线程接收从Datanode发送过来的ack,如果是一个成功的ack,表示复制Pipeline中的所有Datanode都已经接收到这个Packet
,ResponseProcessor线程将packet
从队列ackQueue中删除
。====》 在发送过程中,如果
发生错误
,错误的数据节点会被移除掉,ackqueue
数据块同步
到dataqueue中
,然后重新创建一个新的Pipeline,排除掉出错的那些DataNode节点,接着DataStreamer线程继续从dataQueue队列中发送Packet。
下面是DFSOutputStream的结构及其原理,如图所示:
注意:
客户端执行
write操作
后,写完的block才是可见的,正在写的block对客户端是不可见的,只有调用sync方法
,客户端才确保该文件的写操作已经全部完成,当客户端调用close方法
时,会默认调用sync方法
。是否需要手动调用取决你根据程序需要在数据健壮性和吞吐率之间的权衡。
1.5.HDFS文件权限和安全模式
1.5.1.HDFS文件权限
- 与Linux文件权限类似
r: read; w:write; x:execute,权限x对于文件忽略,对于文件夹表示是否允许访问其内容
- 如果Linux系统用户zhangsan使用hadoop命令创建一个文件,那么这个 文件在HDFS中owner就是zhangsan。
- HDFS的权限目的:阻止好人做错事,而不是阻止坏人做坏事。HDFS 相信,你告诉我你是谁,我就认为你是谁。
1.5.2.安全模式
- namenode启动的时候,首先将映像文件(fsimage)载入内存,并执行编辑日志(edits)中的各 项操作。
- 一旦在内存中成功建立文件系统元数据的映射,则创建一个新的fsimage文件(这个操作不需要SecondaryNameNode)和一个空的编辑日志。
- 此刻namenode运行在安全模式。即namenode的文件系统对于客服端来说是只读的。(显示 目录,显示文件内容等。写、删除、重命名都会失败)。
- 在此阶段Namenode收集各个datanode的报告,当数据块达到最小副本数以上时,会被认为是“安全”的, 在一定比例(可设置)的数据块被确定为“安全”后,再过若干时间,安全模式结束
- 当检测到副本数不足的数据块时,该块会被复制直到达到最小副本数,系统中数据块的位 置并不是由namenode维护的,而是以块列表形式存储在datanode中。
1.5.完全分布式搭建及eclipse插件
1.6.HDFS命令:
(1)查看帮助
hdfs dfs -help
(2)查看当前目录信息
hdfs dfs -ls /
(3)上传文件
hdfs dfs -put /本地路径 /hdfs路径
(4)剪切文件
hdfs dfs -moveFromLocal a.txt /aa.txt
(5)下载文件到本地
hdfs dfs -get /hdfs路径 /本地路径
(6)合并下载
hdfs dfs -getmerge /hdfs路径文件夹 /合并后的文件
(7)创建文件夹
hdfs dfs -mkdir /hello
(8)创建多级文件夹
hdfs dfs -mkdir -p /hello/world
(9)移动hdfs文件
hdfs dfs -mv /hdfs路径 /hdfs路径
(10)复制hdfs文件
hdfs dfs -cp /hdfs路径 /hdfs路径
(11)删除hdfs文件
hdfs dfs -rm /aa.txt
(12)删除hdfs文件夹
hdfs dfs -rm -r /hello
(13)查看hdfs中的文件
hdfs dfs -cat /文件
hdfs dfs -tail -f /文件
(14)查看文件夹中有多少个文件
hdfs dfs -count /文件夹
(15)查看hdfs的总空间
hdfs dfs -df /
hdfs dfs -df -h /
(16)修改副本数
hdfs dfs -setrep 1 /a.txt
1.7.My网盘
新建Java项目,导入所需要的jar包
hadoop中的share\hadoop\hdfs
hadoop中的share\hadoop\hdfs\lib
hadoop中的share\hadoop\common
hadoop中的share\hadoop\common\lib
下的jar包。
block底层—offset偏移量来读取字节数组
配置:
这个目录下的2个配置文件,拷贝到这里
代码实现:
package yong.feng.qiaoke;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class HadoopDemo {
private static Configuration cf;
private static FileSystem fs;
public static void main(String[] args) throws IOException {
cf = new Configuration();
fs = FileSystem.get(cf);
// 创建、删除 文件
// mk_del_dir();
// 上传文件
// uploadFile();
// 下载
// downFile();
// 查询
showList();
}
private static void showList() throws FileNotFoundException, IOException {
Path path = new Path("/");
FileStatus[] filestatus = fs.listStatus(path);
for (int i = 0; i < filestatus.length; i++) {
FileStatus f = filestatus[i];
System.out.println(f.getAccessTime() + "/t" + f.getModificationTime() + "/t" + f.getOwner() + "/t"
+ f.getPath() + "/t" + f.getBlockSize() / 1024 / 1024 + "MB/t");
}
}
private static void downFile() throws IOException {
Path path = new Path("/ceshi/img0.jpg");
File file = new File("C:/Users/hand/Desktop/img0.jpg");
FSDataInputStream in = fs.open(path);
FileOutputStream out = new FileOutputStream(file);
IOUtils.copyBytes(in, out, cf);
}
private static void uploadFile() throws FileNotFoundException, IOException {
Path path = new Path("/ceshi/img0.jpg");
File file = new File("C:/Windows/Web/Wallpaper/Windows/img0.jpg");
IOUtils.copyBytes(new FileInputStream(file), fs.create(path), cf);
System.out.println("上传成功");
}
private static void mk_del_dir() throws IOException {
Path path = new Path("/ceshi");
if (fs.exists(path)) {
fs.delete(path, true);
System.out.println("文件存在");
}
fs.mkdirs(path);
System.out.println("创建成功!1");
}
}