《hadoop权威指南》学习笔记-hadoop分布式文件系统

这章节的内容重理论,所以有些枯燥,不过对整个分布式文件系统的构建还是讲的蛮详细的。

这章其实主要可以分为三大部分:基本概念介绍,接口,数据流。

一、基本概念中,介绍了数据块、namenode、datanode。这些概念在好多地方都有讲,而且讲得也很好,在这里我只稍微提一下。namenode可以认为是记录了任何一个文件所对应的datanode(其实是文件对应块所在的datanode的信息),datanode用于存储检索本节点的数据块,数据块是真真正正的存储文件内容。这里需要强调的是,HDFS中小于一个块的文件不会占据一个块,HDFS数据块之所以设置那么大,是为了是为了减少寻址开销,但是块也不应该设置的太大,因为MapReduce中的map通常每次处理一个块大小的数据,所以如果每次处理的任务过少,集群上的结点不能充分利用,处理速度自然会慢下来。

    二、接口中,书中先是讲了命令行接口,这一部分没有什么好说的,就是一些命令而已,和Linux下的命令很相似。类比的学习就很容易掌握。重要的是Java接口,

(1)用Hadoop URL读数据(本书源代码下载地址

import java.io.InputStream;
import java.net.URL;

import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;

// vv URLCat
public class URLCat {

  static {
    URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
  }
  
  public static void main(String[] args) throws Exception {
    InputStream in = null;
    try {
      in = new URL(args[0]).openStream();
      IOUtils.copyBytes(in, System.out, 4096, false);
    } finally {
      IOUtils.closeStream(in);
    }
  }
}
编译前要注意设置${hadoop-home}/conf/hadoop-env.sh,把HADOOP_HOME(文件中本来就有,只是被注释掉了,自己改过来就好)的值设为你创建.class的目录,编译时注意添加所需类的路径,也就是${hadoop-home}/hadoop-core-x.x.x.jar,编译语句是这样的
javac -cp ${hadoop-home}/hadoop-core-x.x.x.jar 源文件(.java文件)

编译完后利用${hadoop-home}/bin/hadoop 类名(无.class) 输入文件就可以看到结果。这里不清楚就直接按书上做就可以。

这个函数仅仅是利用URL进行文件传输的,和java从任何一个网站上读信息没有区别,由于setURLStreamHandlerFactory()方法只能被java虚拟机调用一次,所以如果第三方创建了URLStreamHandlerFactory实例,那么这个类将无法从hadoop上读取信息。所以接口中不作为重点讲。

(2)用FileSystem API读取数据

import java.io.InputStream;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

// vv FileSystemCat
public class FileSystemCat {

  public static void main(String[] args) throws Exception {
    String uri = args[0];
    Configuration conf = new Configuration();
    FileSystem fs = FileSystem.get(URI.create(uri), conf);
    InputStream in = null;
    try {
      in = fs.open(new Path(uri));
      IOUtils.copyBytes(in, System.out, 4096, false);
    } finally {
      IOUtils.closeStream(in);
    }
  }
}
重要API:<1>FileSystem该类提供了FileSystem.get来检索文件系统实例,conf中封装了客户端或者服务端的配置信息,URI用于确定使用的文件系统。FileSystem.open用来打开文件获取输入流。其实此处FileSystem.open返回的不是java.io而是FsDataInputStream,只是这个类继承了java.io.DataInputStream。书中在这部分还详细的讲了FsDataInputStream,着重讲到着各类中用于定位的接口,也就是说我们可以通过这个类从文件的任意位置读取数据。
(3)写入数据

需要指明的是这里的写入数据是将数据写到HDFS上。

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;

// vv FileCopyWithProgress
public class FileCopyWithProgress {
  public static void main(String[] args) throws Exception {
    String localSrc = args[0];
    String dst = args[1];
    
    InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
    
    Configuration conf = new Configuration();
    FileSystem fs = FileSystem.get(URI.create(dst), conf);
    OutputStream out = fs.create(new Path(dst), new Progressable() {
      public void progress() {
        System.out.print(".");
      }
    });
    
    IOUtils.copyBytes(in, out, 4096, true);
  }
}
重要API:<1>FsDataOutputStream,这个类是fs.create(fs.append也可以实现同样的效果)这一句返回的类型,相当于返回了一个用于写入的输出流。Progressable这个接口是用于返回进度的(输出进度)。但是与FsDataInputStream不同的是,写入操作是没有定位的,因为在hadoop中数据不能更改,也没有必要更改。

(4)查询文件系统

import java.net.URI;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;

// vv ListStatus
public class ListStatus {

  public static void main(String[] args) throws Exception {
    String uri = args[0];
    Configuration conf = new Configuration();
    FileSystem fs = FileSystem.get(URI.create(uri), conf);
    
    Path[] paths = new Path[args.length];
    for (int i = 0; i < paths.length; i++) {
      paths[i] = new Path(args[i]);
    }
    
    FileStatus[] status = fs.listStatus(paths);
    Path[] listedPaths = FileUtil.stat2Paths(status);
    for (Path p : listedPaths) {
      System.out.println(p);
    }
  }
}

重要API:FileStatus封装了文件系统中文件和目录的元数据,包括文件长度,块大小,备份、修改时间,所有者及权限信息。FileSystem可以利用getFileStatus获取文件或目录的FileStatus实例,但这里有一个更好用的方法就是listStatus,该方法可以连同目录的内容一并列出来。最后就是stat2Paths这个方法,这个方法可以把FileStatus对象转化为Path数组。

(5)文件模式

该部分就是用单个操作实现处理一批文件,就是利用通配符实现批量处理,重要的API就是FileSystem.globStatus()这个函数,他会返回与变量地址相配的所有文件的FileStatus数组。globStatus有两个形式

public FileStatus[] globStatus(Path pathPattern) throws IOException
public FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws IOException


第二个涉及到了PathFilter,这是用来限定条件的,比如你排除特定的文件。这个接口需要自己重新定义,用来完成特定的筛选功能。类似于

public class RegexExcludePathFilter implements PathFilter {
  
  private final String regex;

  public RegexExcludePathFilter(String regex) {
    this.regex = regex;
  }

  public boolean accept(Path path) {
    return !path.toString().matches(regex);
  }
}

这里的accept里说明了不与给定的路径匹配,也就是说排除该文件。

最后,删除文件就用FileSystem.delete()就可以。

三、数据流

这一部分理论性很强,我不能保证我完全理解正确,如果有错误还请网友点出。

(1)文件读取



首先,客户端通过FileSystem对象的open函数来打开要读取的文件,然后DistributedFileSystem通过RPC调用namenode以确定文件前几个块的位置,然后namenode返回存有该块复本的datanode地址。DistributedFileSystem返回一个FsDataInputStream对象,FsDataInputStream封装有DFSInputStream(管理namenode和datanode I/O),存储着文件前几个块DFSInputStream然后连接距离最近的datanode,然后客户端反复调用read(),数据从datanode流入客户端,到达块末端的时候,关闭与该datanode的连接,寻求下一个最佳的datanode。当客户端读完一批数据块的时候,就需要再次向namenode请求检索一批datanode的位置了。当读取完毕的时候,客户端调用close()关闭FsDataInputStream。这样基本流程就介绍完了,下面是对流程中的一些细节作出的说明。

namenode返回的数据块复本的datanode的地址是按照datanode到客户端的距离来排序的,其实这里的距离是很模糊的概念,由于带宽限制了数据的传输速率,所以利用带宽来表示距离,而带宽的衡量也很难,所以我们从抽象的层面来构建树,说是这样构造的:叶子节点相当于进程,进程的父亲是节点,节点的父亲为机架,机架的父亲是数据中心。两个节点之间的距离是他们到最近的共同祖先的距离总和。

如果DFSInputStream在与datanode通信时遇到错误,他便会从离这个块最近的datanode读取数据,并且记录下datanode以保证以后不会反复地去读取了,同时他还会确认从这个datanode发来的信息是否有错,如果有错,就在从其他datanode读取这个数据块的复本前通知给namenode。

(2)文件写入



客户端通过对DistributedFileSystem对象调用create()函数来创建文件,然后DistributedFileSystem对Name弄得创建一个RPC调用,在文件系统的命名空间中创建一个新文件,此时该文件还没有对应的数据块。而且namenode执行各种检查来确保这个文件不存在。如果检查通过,namenode就会为创建新文件怎加一条记录,否则就会抛出异常。DistributedFileSystem返回一个FSDataOutputStream对象(客户端利用它来写数据)。FSDataOutputStream封装了一个DFSOutputStream对象,用于处理datanode和namenode 之间的通信。客户端写入数据时,DFSOutputStream将数据分为一个个的包,存入数据队列,DataStreamer利用数据队列,根据datanode列表来要求namenode分配合适的新块来存储数据备份。这些块构成一个管线,DataStreamer将数据块流式传给第一个datanode,第一个datanode在存储完数据包后再发送给第二个datanode,这样一直传给最后一个datanode。DFSOutputStream还维护这一个确认队列,用来等待datanode的确认回执,只有当管线中所有datanode确认收到数据后才会把数据包从确认队列中删除。

如果一个Datanode的数据被写入时失败,首先,该管道被关闭,并且确认队列中的所有数据包添加到数据队列的前面,因此,故障节点的下游数据节点不会丢掉数据包。在好的DataNode上的当前块被赋予了新的标示,并将该标示传递给namenode。从管线中把故障节点删除,然后把余下的数据写入剩下的datanode,如果namenode发现复本量不足时会在新的节点上创建复本。

客户端写完数据时会调用close(),此行为刷新Datanode管道的所有剩余的数据包,并在联系namenode和发送文件操作完成之前等待确认。namenode已经知道文件有哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。

加一条个人理解,namenode选定datanode之后就会把数据写到相应的datanode上,每一个datanode都会记录下该文件的所有信息,不同的复本只是用于冗余备份。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这本书很全,是Hadoop中的圣经级教材,不过看起来挺累。 内容简介 Discover how Apache Hadoop can unleash the power of your data. This comprehensive resource shows you how to build and maintain reliable, scalable, distributed systems with the Hadoop framework -- an open source implementation of MapReduce, the algorithm on which Google built its empire. Programmers will find details for analyzing datasets of any size, and administrators will learn how to set up and run Hadoop clusters. This revised edition covers recent changes to Hadoop, including new features such as Hive, Sqoop, and Avro. It also provides illuminating case studies that illustrate how Hadoop is used to solve specific problems. Looking to get the most out of your data? This is your book. Use the Hadoop Distributed File System (HDFS) for storing large datasets, then run distributed computations over those datasets with MapReduce Become familiar with Hadoop’s data and I/O building blocks for compression, data integrity, serialization, and persistence Discover common pitfalls and advanced features for writing real-world MapReduce programs Design, build, and administer a dedicated Hadoop cluster, or run Hadoop in the cloud Use Pig, a high-level query language for large-scale data processing Analyze datasets with Hive, Hadoop’s data warehousing system Take advantage of HBase, Hadoop’s database for structured and semi-structured data Learn ZooKeeper, a toolkit of coordination primitives for building distributed systems "Now you have the opportunity to learn about Hadoop from a master -- not only of the technology, but also of common sense and plain talk."

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值