HDFS体系结构
★ 分布式文件系统
一种可以管理分布在不同机器上的文件的操作系统。因为,单一的一台机器上的存储已经不能满足需要。不同主机上的文件可以通过网络进行分享。也叫网络操作系统,即NFS。通过网络访问的文件,对用户和程序来说,如同本地一样。其中HDFS就是其中一种分布式操作系统。适合一次写入,多次读写的情况。
★ HDFS 常用shell操作
在Hadoop中通过shell命令访问HDFS文件系统中文件的命令方法是hadoop fs 开头或hadoop dfs。
完整的命令应该格式是: hadoop fs schema://authority/path
完整的命令示例如:hadoop fs hdfs://hadooptest:9000/
hdfs://hadooptest:9000是conf/core-site.xml配置文件中fs.default.name对应的值。
下面介绍详细命令:(下面的命令写的是简写)
命令一:hadoop fs -help
[root@hadooptest ~]# hadoop fs -help
Warning: $HADOOP_HOME is deprecated.
hadoop fs is the command to execute fscommands. The full syntax is:
hadoop fs [-fs <local | file systemURI>] [-conf <configuration file>]
[-D <property=value>] [-ls <path>] [-lsr <path>] [-du<path>]
[-dus <path>] [-mv <src> <dst>] [-cp <src><dst>] [-rm [-skipTrash] <src>]
[-rmr [-skipTrash] <src>] [-put <localsrc> ... <dst>][-copyFromLocal <localsrc> ... <dst>]
[-moveFromLocal <localsrc> ... <dst>] [-get [-ignoreCrc][-crc] <src> <localdst>
[-getmerge <src> <localdst> [addnl]] [-cat <src>]
[-copyToLocal [-ignoreCrc] [-crc] <src> <localdst>][-moveToLocal <src> <localdst>]
[-mkdir <path>] [-report] [-setrep [-R] [-w] <rep> <path/file>]
[-touchz <path>] [-test -[ezd] <path>] [-stat [format]<path>]
[-tail [-f] <path>] [-text <path>]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-chgrp [-R] GROUP PATH...]
[-count[-q] <path>]
[-help [cmd]]
-fs [local | <file system URI>]: Specify the file system to use.
...........................
ps : 用于显示命令的帮助文档
命令二:hadoop fs -ls(r) <path> //显示当前目录下所有文件
显示列表中,列表的显示,类似Linux下ls -l 列出来的类似
d表示是文件夹 -表示是文件
再接着的9位表示的是权限,权限分组类似linux
再接着的是表示的是所属的用户和组
再接着的是文件的大小
再接着的是日期
命令三:hadoop fs -du(s) <path> //显示目录中所有文件大小
[root@hadooptest ~]# hadoop fs -dus /txt
Warning: $HADOOP_HOME is deprecated.
hdfs://hadooptest:9000/txt 36
[root@hadooptest ~]# hadoop fs -du /txt
Warning: $HADOOP_HOME is deprecated.
Found 2 items
15 hdfs://hadooptest:9000/txt/wealon.txt
21 hdfs://hadooptest:9000/txt/wealonxt.txt
命令四:hadoop fs -count[-q] <path> //显示目录中文件数量
[root@hadooptest ~]# hadoop fs -count /txt
Warning: $HADOOP_HOME is deprecated.
1 2 36hdfs://hadooptest:9000/txt
分别显示了目录数,文件数,文件大小及所计算的路径
命令五:hadoop fs -mv <src> <dst> //移动多个文件到目标目录
移动文件从HDFS的src到dst路径
命令六:hadoop fs -cp <src> <dst> //复制多个文件到目标目录
移动文件从HDFS的src到dst路径
命令七:hadoop fs -rm(r) //删除文件(夹)
要删除文件夹,必须用rmr参数
命令八:hadoop fs -put <localsrc> <dst> //本地文件复制到hdfs
ps:localsrc表示的是本地路径
命令九:hadoop fs -copyFromLocal //同put
命令十:hadoop fs -moveFromLocal //从本地文件移动到hdfs
命令十一:hadoop fs -get [-ignoreCrc] <src> <localdst> //复制文件到本地,可以忽略crc校验
命令十二:hadoop fs -getmerge <src> <localdst> //将源目录中的所有文件排序合并到一个文件中
PS:用于合并文件,把零散的小文件合并成一个大文件
命令十三:hadoop fs -cat <src> //在终端显示文件内容
PS:查看
命令十四:hadoop fs -text <src> //在终端显示文件内容
同-cat
命令十五:hadoop fs -copyToLocal [-ignoreCrc] <src> <localdst> //复制到本地
PS:From 与 To
命令十六:hadoop fs -moveToLocal <src> <localdst>
PS:From 与 To
命令十七:hadoop fs -mkdir <path> //创建文件夹
命令十八:hadoop fs -touchz <path> //创建一个空文件
PS:HDFS的命令,与linux的命令可以关联起来,比较类似。
▲ 文件的副本数
对于在conf/hdfs-site.xml中配置的dfs.replication参数的值,表示的是HDFS中某个文件或block的副本数。默认是3
如之前设置的副本数是2,则上传的所有文件的副本数都将是2个
如果在上传了一部分文件后,更改了dfs.replication的值为3
则,在修改之后上传的文件或block的副本数为3,而修改之前上传的文件的副本数仍旧为2.
如果要使修改之前上传的文件的副本数从2变为3,只需要执行如下的命令。
hadoop fs -setrep -R 3 /
命令:hadoop fsck -locations 用来查看副本情况
在一个dataNode中只保存一个副本,所以,如果一个集群有3台机器,而设置的副本数为4,则,该参数的设置是不会生效的。
★ HDFS体系结构
两个核心:NameNode 和 DataNode
▲ NameNode功能:
接收用户的操作请求
是整个文件系统的管理节点
维护整个文件系统的文件目录树
维护文件或目录的元信息及每个文件对应的数据块列表
那么它是怎么实现如上功能的呢?
由以下fsimage、edits、fstime这三个文件来实现。
以上三个文件的位置是在core-site.xml文件中的属性为hadoop.tmp.dir的值来指定的,如/usr/local/hadoop1/tmp
在上述目录下的dfs/name/current目录下有如下四个文件:
edits fsimage fstime VERSION
其中:fsimage是元数据镜像文件,存储的是某一时间段NameNode内存的元数据信息
edits保存的是操作日志文件
fstime 保存的是最近一次checkpoint的时间
那么VERSION呢?
▲ SecondaryNameNode的功能
用来合并fsimage和edits成新的fsimage,合并后edits清空。
edits的合并有一定的条件:
条件一,达到一定的周期,默认是3600秒,即1小时。
条件二,达到一定的大小,默认是67108864,也即64M,如果大小已经达到了这个大小,则触发合并的操作,而不管周期是否已经到3600秒。(见源文件)
默认SecondaryNameNode是跟NameNode安装在同一个节点上的,这样是不好的,因为SecondaryNameNode和NameNode都是单节点的,所以,如果出现节点故障,SecondaryNameNode和NameNode都会受到影响。所以,最好是把Name和SecondaryNameNode在集群环境下分别安装在不同的节点中。
▲ DataNode的功能
它提供的是真实文件数据的存储服务。
文件块:它是文件的最基本的存储单位。把一个大的文件划分成小的block,默认的Block大小是64M。
在HDFS文件系统中,如果一个文件的大小小于一个Block块的大小,则该文件并不占用整个数据块的存储空间。
在linux文件系统中,如果一个文件的大小小于一个Block块的大小,则该文件占用的是该块Block的大小。
▲ HDFS的权限
文件的权限都设计成与其它常用平台类似,像Linux。当前,安全性仅限于简单权限。谁启动的NameNode,谁就被当成HDFS的超级用户。
★ 用JAVA操作HDFS
用javaAPI操作HDFS,用到的Hadoop核心类有:
FileSystem 表示的是文件系统,对HDFS的操作的方法均来自于该类,这是一个抽象类。
Configuration 表示配置对象
URL 表示 表示路径,在用URL的时候,路径一定要加上schema,不支持简写
FileStatus 文件状态,从该对象中可以获取到文件的名称、文件的权限、等等信息
Path 表示路径,可以用它来描述一个文件或文件夹
FSDataInputStream 在用FileSystem读取文件的时候,是把文件读取到该流中,从而根据流的对接,可以把文件输出到输出流中。
IOUtils 一个IO工具类,用于处理IO操作,简化了IO操作。
以下是测试的例子,不全,基本操作全在了。
▲ 初始化变量
private static String url = null;
private static Configuration conf = null;
private static FileSystem fs = null;
// 初始化url Configuration FileSystem
static {
try {
url = "hdfs://192.168.75.245:9000";
conf = new Configuration();
fs = FileSystem.get(URI.create(url), conf, "root");
}catch(Exception e) {
e.printStackTrace();
}
}
▲ 例子一:检测HDFS中某个文件是否存在
/** 测试文件夹是否存在 **/
@Test
public void testFolderExist() throws Exception {
//路径相当于:hdfs://hadooptest:9000/wealon
boolean exist = this.isFolderExist("/wealon");
System.out.println(exist);
}
/**
* 检查某个文件夹是否存在
*
* @throws Exception
*/
private boolean isFolderExist(String path) throws Exception {
return fs.exists(new Path(path));
}
▲ 例子二:测试创建文件夹
/** 测试创建文件夹 **/
@Test
public void testCreateFolder() throws Exception {
// 创建文件夹
this.createFolder("/path");
}
/**
* 根据传入的字符串创建文件夹
*
* @param folderName
*/
private void createFolder(String folderName) throws Exception {
fs.mkdirs(new Path(folderName));
}
▲ 例子三:创建文件
/** 测试创建文件 **/
@Test
public void testCreateFile() throws Exception {
// 创建空文件
this.createFile("/path/test.txt");
}
/**
* 根据字符串创建文件
*
* @param fileName
*/
private void createFile(String fileName) throws Exception {
fs.create(new Path(fileName));
}
▲ 例子四:测试读取文件二
/** 测试读取文件二 **/
@Test
public void tesReadFile2() throws Exception {
// 读取文件 方法二
this.readFile2("HDFS://hadooptest:9000/core-site.xml");
}
/**
* 读取文件方法二
*
* @param fileName
*/
private void readFile2(StringpathName) throwsException {
//设置让程序识别HDFS协议
URL.setURLStreamHandlerFactory(newFsUrlStreamHandlerFactory());
URLurl = newURL(pathName);
InputStreamis = url.openStream();
IOUtils.copyBytes(is,System.out,1024, true);
}
▲ 例子五:测试读取文件一
/** 测试读取文件 **/
@Test
public void tesReadFile() throws Exception {
// 读取文件方法一
this.readFile("/core-site.xml");
}
/**
* 读取文件方法一
*
* @param fileName
*/
private void readFile(String pathName) throws Exception {
if (this.isFolderExist(pathName)){
FSDataInputStreamis = fs.open(new Path(pathName));
IOUtils.copyBytes(is,System.out,1024, false);
IOUtils.closeStream(is);
}else{
System.err.println("文件不存在……");
}
}
▲ 例子六:上传文件
/** 测试上传 **/
@Test
public void testPutFile() throws Exception {
// 上传
this.putFile("d://mail.txt","");
}
/**
* put 上传文件
*
* @param fileName
*/
private void putFile(StringsrcPath,String destPath) throws Exception {
StringfileName = getFileName(srcPath);
if (!this.isFolderExist(destPath+ "/"+ fileName)) {
fs.copyFromLocalFile(new Path(srcPath), new Path(destPath==""?"/":destPath));
}else{
System.err.println("文件已经存在……");
}
}
/**
* 根据上传的本地路径,得到文件名
* @param srcPath
* @return
*/
private StringgetFileName(String srcPath) {
String[]arr = srcPath.split("//");
return arr[arr.length-1];
}
▲ 例子七:删除文件或文件夹
/** 测试删除文件或文件夹 **/
@Test
public void testDeleteFile() throws Exception {
// 删除
this.deleteFile("/path");
}
/**
* 删除文件
* @param fileName
*/
private void deleteFile(String filePath) throws Exception {
if (this.isFolderExist(filePath)){
boolean isDir = fs.getFileStatus(newPath(filePath)).isDir();
if(isDir){
System.out.println("正在删除文件夹");
fs.delete(new Path(filePath), true);
}else{
System.out.println("正在删除文件");
fs.delete(new Path(filePath), false);
}
}else{
System.err.println("文件不存在……");
}
}
例子八:测试获取所有的文件夹或文件
/** 测试获取所有的文件或文件夹 **/
@Test
public void testgetAllFiles() throws Exception {
this.getAllHDFSFiles(new Path("/"));
}
/**
* 得到所有的文件
*/
private void getAllHDFSFiles(Path path) throws Exception{
FileStatus[]fileStatus = fs.listStatus(path);
for(FileStatus fs :fileStatus){
//String name = fs.getPath().getName();
Stringtype = fs.isDir()?"目录:":"文件:";
if(fs.isDir()){
System.out.println(type +fs.getPath());
this.getAllHDFSFiles(fs.getPath());
}else{
System.out.println(type +fs.getPath());
}
}
}
注:还可以测试修改文件的副本数,获取文件的大小等。
★ RPC原理
RPC,即Remote ProcedureCall,即远程过程调用。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议存在,如TCP UDP
RPC采用客户机/服务机模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。
首先,客户机调用进程发送一个有进程参数的调用信息到服务器进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用的信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息。然后等待焉个调用信息。
Hadoop的体系结构就是构建在RPC机制上的,如在启动Hadoop的时候,start-all.sh会启动所有的服务器端进程。也就是启动RPC的服务器端。当我们用命令云访问HDFS或跪一个JOB的时候,其实就是客户端在向服务器端发送请求。如果是访问HDFS,则请求到达NameNode,NameNode再把请求分配到对应的DataNode。如果是一个MR的计算请求,则请求到达JobTracker,JobTracker会处理该请求,把任务分配到对应的TaskTracker。
下面是一个RPC的简单原理实现。
▲ 1.定义一个接口
package com.broader.rpc;
import org.apache.hadoop.ipc.VersionedProtocol;
public interfaceMyBean extendsVersionedProtocol {
public abstract Stringhello(String name) ;
}
注:该接口继承自VersionedProtocol,
该类的几个子类:
ClientProtocol 它是客户端FileSystem与NameNode通信和接口
DataNodeProtocol 它是DataNode与NameNode通信的的接口
NamenodeProtocol 它是SecondaryNameNode与NameNode通信的接口
该类结构如下:
▲ 2.定义上述接口的实现类
package com.broader.rpc;
import java.io.IOException;
public classMyBeanImpl implementsMyBean{
public static long VERSION = 2343555L;
@Override//被调用的方法
public String hello(Stringname) {
System.out.println("MyBeanImpl.hello method is called");
return "hello "+ name;
}
@Override
public longgetProtocolVersion(String protocol, long clientVersion)
throws IOException {
return VERSION;
}
}
▲ 3.定义服务器端
package com.broader.rpc;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.Server;
public classMyServer {
public static final int PORT = 12345;
public static final String SERVER_ADDRESS= "localhost";
public static void main(String[] args) throws IOException {
//创建一个Server
final Server server = RPC.getServer(new MyBeanImpl(), SERVER_ADDRESS,PORT,
new Configuration());
//启动Server 一直监听12345端口
server.start();
}
}
▲ 4.定义客户端
package com.broader.rpc;
import java.net.InetSocketAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.ipc.RPC;
public classMyClient {
public static void main(String[] args) throws Exception{
//用接口来接收一个代理对象
final MyBean proxy =(MyBean)RPC.getProxy(
MyBean.class,
MyBeanImpl.VERSION,
newInetSocketAddress(MyServer.SERVER_ADDRESS,MyServer.PORT),
new Configuration());
//通过代理对象调用接口中定义的方法
StringreturnVal = proxy.hello("liuzhixiang");
System.out.println(returnVal);
//停止代理对象
RPC.stopProxy(proxy);
}
}
注:
先运行Myserver,启动服务器端程序
再运行MyClient客户端,则它会得到从服务器返回的信息。
RPC的调用类似于Socket的调用、也类似于WebService的调用