HDFS概述及设计目标
什么是HDFS
· hadoop实现了一个分布式文件系统四个单词的简写HDFS
· 源于google的GFS
· 论文发表于2003年,是GFS克隆版
HDFS的设计目标
· 非常巨大的分布式文件系统
· 运行在普通廉价的硬件上
· 易扩展,为用户提供性能不错的文件存储服务
HDFS架构
· 1 Master(NameNode/NN) 带 N个Slaves(DataNode/DN)
HDFS/YARN/HBase
· 1个文件会被拆分成多个Block
· blocksize:128M
· 130M ==> 2个Block: 128M 和 2M
· NN(namenode):
1)负责客户端请求的响应
2)负责元数据(文件的名称、副本系数、Block存放的DN)的管理
· DN(datanode):
1)存储用户的文件对应的数据块(Block)
2)要定期向NN发送心跳信息,汇报本身机器所有的block信息,健康状况
· A typical deployment has a dedicated machine that runs only the NameNode software.
Each of the other machines in the cluster runs one instance of the DataNode software.
The architecture does not preclude running multiple DataNodes on the same machine
but in a real deployment that is rarely the case.
· NameNode + N个DataNode
· 建议:NN和DN是部署在不同的节点上
HDFS副本机制(容错)
· replication factor:副本系数、副本因子
· All blocks in a file except the last block are the same size
HDFS副本存放策略
· 第一个存放在当前操作的机架上
· 第二个和第三个存放在第二个机架上的不同机器上
· 越多的话就随机分配机器了
· 生产上至少要有两个机架
JDK安装和ssh安装和ssh免密码登陆配置
Hadoop伪分布式安装步骤
JDK安装
· 使用版本:hadoop-2.6.0-cdh5.7.0
· 解压jdktar -zxvf jdk-7u79-linux-x64.tar.gz -C ~/app/
· 进入到解压的目录文件里边,使用pwd显示目录信息,拷贝这个信息配置环境变量
· 配置环境变量vim ~/.bash_profile
· export JAVA_HOME=.../jdk.1.7.0_79
· export PATH=$JAVA_HOME/bin:$PATH
· 配置生效source ~/.bash_profile
· echo $JAVA_HOEM查看配置是否成功
· java -version
安装SSH
· yum install -y ssh
· ssh-keygen -t rsa
· 拷贝公钥cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
· 测试ssh localhost,ssh 机器名字
HDFS伪分布式环境搭建
参考网页http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.7.0/hadoop-project-dist/hadoop-common/SingleCluster.html
http://archive.cloudera.com/cdh5/cdh/5/hadoop-2.6.0-cdh5.7.0.tar.gz
解压tar -zxvf hadoop-2.6.0-cdh5.7.0.tar.gz -C ~/app/
进入到解压的hadoop文件下
cd /etc
vim /hadoop-env.sh设置一下java的JAVA_HOME参数
vim /core-site.xml
# 修改一下机器名字和端口
<property>
<name>fs.default.name</name>
<value>hdfs://节点机器名字:port</value> // 8020
</property>
# 临时文件
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/app/tmp</value>
</property>
vim /hdfs-site.xml设置副本数为1,因为默认是3
<property>
· <name>dfs.replication</name>
· <value>1</value>
· </property>
vim slaves主机名,有多少个datanode就需要写在这里边,这里暂时不用配置这个文件
启动hdfs,先到hadoop的目录下 或者cd到bin目录下(/home/hadoop/app/hadoop-2.6.0-cdh5.7.0/bin)
格式化文件系统(仅仅第一次执行,不要重复执行),:bin/hdfs namenode -format (表明在bin目录下执行hdfs命令)
启动hdfs: sbin/start-dfs.sh
验证是否启动成功:
方式1:
jps
DataNode
SecondaryNameNode
NameNode
方式二:浏览器访问方式: http://ip:50070
停止hdfs
sbin/stop-dfs.sh
HDFS shell操作
将hadoop的bin目录配置成环境变量中,和jdk的配置是一样的操作
vim ~/.bash_profile
export HADOOP_HOME=/.../hadoop...cdh5.7.0
export PATH=$HADOOP_HOME/bin:$PATH
source ~/.bash_profile
hdfs dfs,hadoop fs这就是操作hadoop的命令,可以查看到命令的介绍和帮助信息
在/data/目录下创建一个hello.txt文件,vim hello.txt
查看hadoop的根目录hadoop fs -ls /
将data下面的hello.txt传递到hadoop上边去hadoop fs -put hello.txt /
查看hadoop上边的文件的内容hadoop fs -text /hello.txt
在hadoop上边创建一个目录hadoop fs -mkdir /test
递归创建文件夹hadoop fs -mkdir -p /test/a/b
递归展示目录hadoop fs -ls -R /或者-lsr选项
使用别的方式拷贝本地文件到hadoop上边hadoop fs -copyFromLocal hello.txt /test/a/b/h.txt
查看拷贝的文件的内容hadoop fs -cat /test/a/b/h.txt
将hadoop上边的内容拿到本地来hadoop fs -get /text/a/b/h.txt
删除文件hadoop fs rm /hello.txt
删除文件夹目录hadoop fs rm -R /test
从浏览器上边去浏览文件信息http://ip:50070上边的Utilities
上传一个大的文件hadoop的安装包查看大小信息
java操作HDFS开发环境搭建
· idea+maven创建java工程
· 添加HDFS依赖
· JAVA API 操作文件系统
具体操作
· 统一管理版本和添加hadoop的依赖包
<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>com.imooc.hadoop</groupId>
<artifactId>hadoop-train</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>hadoop-train</name>
<url>http://maven.apache.org</url>
<!--自己定义统一管理Hadoop的版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
</properties>
<!--添加一个cdh的仓库,url必须指定正确的路径-->
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<!--添加hadoop依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!--添加单元测试的依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
JAVA代码
package com.imooc.hadoop.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
/**
- Hadoop HDFS Java API 操作
*/
public class HDFSApp {
public static final String HDFS_PATH = "hdfs://ip:8020";
FileSystem fileSystem = null;
Configuration configuration = null;
/**
* 创建HDFS目录
*/
@Test
public void mkdir() throws Exception {
fileSystem.mkdirs(new Path("/hdfsapi/test"));
}
/**
* 创建文件
*/
@Test
public void create() throws Exception {
FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/a.txt"));
output.write("hello hadoop".getBytes());
output.flush();
output.close();
}
/**
* 查看HDFS文件的内容
*/
@Test
public void cat() throws Exception {
FSDataInputStream in = fileSystem.open(new Path("/hdfsapi/test/a.txt"));
IOUtils.copyBytes(in, System.out, 1024);
in.close();
}
/**
* 重命名
*/
@Test
public void rename() throws Exception {
Path oldPath = new Path("/hdfsapi/test/a.txt");
Path newPath = new Path("/hdfsapi/test/b.txt");
fileSystem.rename(oldPath, newPath);
}
/**
* 上传文件到HDFS
*
* @throws Exception
*/
@Test
public void copyFromLocalFile() throws Exception {
Path localPath = new Path("/Users/rocky/data/hello.txt");
Path hdfsPath = new Path("/hdfsapi/test");
fileSystem.copyFromLocalFile(localPath, hdfsPath);
}
/**
* 上传文件到HDFS
*/
@Test
public void copyFromLocalFileWithProgress() throws Exception {
InputStream in = new BufferedInputStream(
new FileInputStream(
new File("/Users/rocky/source/spark-1.6.1/spark-1.6.1-bin-2.6.0-cdh5.5.0.tgz")));
FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/spark-1.6.1.tgz"), new Progressable() { public void progress() {
System.out.print("."); //带进度提醒信息
}});
IOUtils.copyBytes(in, output, 4096);
}
/**
* 下载HDFS文件
*/
@Test
public void copyToLocalFile() throws Exception {
Path localPath = new Path("/Users/rocky/tmp/h.txt");
Path hdfsPath = new Path("/hdfsapi/test/hello.txt");
fileSystem.copyToLocalFile(hdfsPath, localPath);
}
/**
* 查看某个目录下的所有文件
*/
@Test
public void listFiles() throws Exception {
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for(FileStatus fileStatus : fileStatuses) {
String isDir = fileStatus.isDirectory() ? "文件夹" : "文件";
short replication = fileStatus.getReplication();
long len = fileStatus.getLen();
String path = fileStatus.getPath().toString();
System.out.println(isDir + "\t" + replication + "\t" + len + "\t" + path);
}
}
/**
* 删除
*/
@Test
public void delete() throws Exception{
fileSystem.delete(new Path("/"), true);
}
@Before
public void setUp() throws Exception {
System.out.println("HDFSApp.setUp");
configuration = new Configuration();
fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, "hadoop");
}
@After
public void tearDown() throws Exception {
configuration = null;
fileSystem = null;
System.out.println("HDFSApp.tearDown");
}
HDFS写数据流程
文件写流程图解
告诉客户端读写请求
namenode协调全局把控
datanode负责数据的存储
写过程
发起请求(多少兆什么的)
客户端收到请求(客户端需要告诉block的大小,副本多少等)
好的客户端需要知道一些事情(blocksize,副本因子)
将文件拆分成blocks
客户端和namenode通信告诉blocksize以及副本等
namenode思考怎么做呢,计算
将结果告诉客户端(存放到哪儿)
客户端发数据到datanode上
datanode1接受数据的同时将数据发送到另外一个datanode,进行副本处理,第二个也是同样的处理,最后一个datanode写完了,就说明第一个block写完了
每个datanode将信息告诉namenode写完了
namenode知道了之后客户端也知道了
重复剩下的block块
客户端处理完了之后关闭流
namenode将元信息存储到内存或者磁盘
HDFS读数据流程
读过程
用户告诉客户端读取数据,读哪个文件
客户端将文件名给namenode获取元数据信息
namenode告诉block信息和datanode的集合
客户端知道了信息block和存放在哪个datanode
客户端和datanode交互下载文件
故障的容错,hdfs都做了很好的处理
HDFS文件系统的优缺点
优点
数据冗余(多副本存储在节点上),硬件容错(某台挂掉也能用)
处理流式的数据访问,这里的流式是一次存储多次读写的操作
适合存储大文件
可构建在廉价的机器上
缺点(不能满足下列问题)
低延时的数据访问
小文件存储,元信息太多