hadoop系统概览(四)

2018-08-06 17:15:09 浏览数 (1)

Control and Data Flow

HDFS的设计使得客户端不会通过NameNode读取和写入文件数据。 相反,客户端向NameNode请求它应该使用类型ClientProtocol通过RPC连接联系的DataNode。 然后客户端直接与DataNode进行通信,以使用DataTransferProtocol传输数据,DataTransferProtocol是一种流处理协议,为了提升性能。 此外,Namenode和Datanode之间的所有通信,例如。DataNode注册,heartbeat,Blockreport,都由Datanode启动,并由Namenode响应。

Read

首先,客户端使用文件名,读取范围起始偏移和范围长度查询NameNode。 NameNode返回指定范围内指定文件的块的位置。特别地,每个块的DataNode位置通过与客户端的接近度来排序。客户端然后向一个DataNode发送请求,这个更可能是最接近的一个。

Write

创建文件的客户端请求不会立即到达NameNode。相反,客户端将文件数据缓存到临时本地文件中。一旦本地文件累积了超过一个块大小的数据,客户端就联系NameNode,NameNode更新文件系统命名空间并返回分配的数据块位置。然后客户端将块从本地临时文件刷新到指定的DataNode。当文件关闭时,剩余的最后一个块数据被传输到DataNode。

The Small Files Problem

大数据但确是小文件(明显小于块大小)意味着很多文件,这为NameNode创建了一个大问题。回想一下,NameNode保存主存储器中文件和块的所有元数据。假设每个元数据对象占用大约150字节,则NameNode可以托管大约1000万个文件,每个使用一个块,具有3吉字节的存储器。虽然更大的内存可以推高上限,大堆是JVM垃圾回收器的一大挑战。此外,HDFS不能有效读取小文件,因为客户端NameNode通信的开销,太多的磁盘寻找,以及从DataNode到DataNode的大量跳跃,以检索每个小文件。

为了减少文件数量,从而减少对NameNode内存的压力,引入了Hadoop Archives(HAR文件)。 HAR文件,由hadoop archive命令创建,是包含元数据和数据文件的特殊格式归档。存档将其自身展示为文件系统层。所有原始文件都可以通过har:// URI访问。它也很容易使用HAR文件作为MapReduce中的输入文件系统。请注意,由于对元数据的额外访问,读取HAR中的文件实际上较慢。

由二进制键值对组成的SequenceFile也可以用于处理小文件问题,通过使用文件名作为键和文件内容作为值。这在MapReduce作业的实践中非常好。此外,SequenceFile支持压缩,这减少了磁盘使用,并加快了MapReduce中的数据加载。存在开源工具将tar文件转换为SequenceFiles。

键值存储,例如, HBase和Accumulo也可以用于减少文件数,尽管它们被设计用于更复杂的用例。与SequenceFile相比,它们支持通过密钥进行随机访问。

HDFS Federation

集群中单个NameNode的存在极大地简化了系统的架构。然而,它也带来了问题。文件计数问题,这由于NameNode的内存有限。更严重的问题是,它被证明是客户的瓶颈。即使客户端向NameNode发出少量元数据操作,也可能有成千上万的客户端同时与NameNode进行通信。使用多个MapReduce作业,我们可能突然在一个大型集群中有数千个任务,每个都尝试打开一些文件。由于NameNode每秒只能执行几千个操作,因此处理所有这些请求需要很长时间。

从Hadoop 2.0开始,在具有热备份的主动/被动配置中,我们可以在同一集群中拥有两个冗余的NameNode。虽然这允许快速故障切换到新的NameNode以实现容错,但它不能解决性能问题。为了部分解决可扩展性问题,引入了HDFS联合的概念,以允许HDFS集群中的多个命名空间。在未来,它还可以支持跨集群的合作。

在HDFS联合中,有多个独立的NameNode(因此有多个命名空间)。 NameNode不需要彼此协调。通过注册和处理集群中所有NameNode的命令,DataNode被所有NameNode用作公共存储。 NameNode的失败不会阻止DataNode服务群集中的其他NameNode。

因为多个NameNode独立运行,可能存在由不同NameNode生成的64位块ID的冲突。为了避免此问题,命名空间使用一个或多个块池,由群集中的唯一ID标识。块池属于单个命名空间,不跨越命名空间边界。扩展块id是(块池ID,块ID)的元组,用于HDFS联合中的块标识。

Java API

HDFS以Java实现,并提供本机JavaAPI。 要以其他编程语言访问HDFS,Thrift绑定提供给Perl,Python,Ruby和PHP 。 接下来,我们将讨论如何使用HDFSJava API以及几个小例子。首先,我们需要添加以下依赖项到项目的Maven POM文件。

<dependency>

<groupId>org.apache.hadoop</groupId>

<artifactId>hadoop-common</artifactId>

<version>2.6.0</version>

</dependency>

<dependency>

<groupId>org.apache.hadoop</groupId>

<artifactId>hadoop-hdfs</artifactId>

<version>2.6.0</version>

</dependency>

HDFS Java API的主要入口点是作为通用文件系统表示的包org.apache.hadoop.fs中的抽象类FileSystem。 FileSystem有各种实现:

DistributedFileSystem

: The implementation of distributed file system. This object is the wayend-user code interacts with an HDFS.

LocalFileSystem

: The local implementation for small Hadoop instances and for testing.

FTPFileSystem

: A FileSystem backed by an FTP client.

S3FileSystem

: A block-based FileSystem backed by Amazon S3.

The FileSystem class also serves as a factory for concreteimplementations:

Configuration conf = new Configuration();

FileSystem fs = FileSystem.get (conf);

where the Configuration class passes the Hadoop configuration informationsuch as scheme, authority, NameNode host and port, etc. Unless explicitlyturned off, Hadoop by default specifies two resources, loaded in-order from theclasspath:

core-default.xml

: Read-only defaults for Hadoop.

core-site.xml

: Site-specific configuration for a given Hadoop installation.

应用程序可以添加其他资源,这些资源按照它们添加的顺序在这些资源之后加载。使用FileSystem,可以进行通用命名空间操作。例如创建,删除和重命名文件。我们还可以查询文件的状态,例如长度,块大小,块位置,权限等。要读取或写入文件,我们需要使用类FSDataInputStream和FSDataOutputStream。在下面的示例中,我们开发了两个简单的函数来将本地文件复制到HDFS或从HDFS复制。为了简单起见,我们不检查文件是否存在或任何I / O错误。请注意,FileSystem确实提供了用于在本地文件系统和分布式文件系统之间复制文件的多个实用程序。

代码语言:javascript复制
/** Copy a local file to HDFS */
public void copyFromLocal(String src, String dst) throwsIOException {
  Configurationconf = new Configuration();
  FileSystem fs =FileSystem.get(conf);
  // The Pathobject represents a file or directory in HDFS.
 FSDataOutputStream out = fs.create(new Path(dst));
  InputStream in =new BufferedInputStream(new FileInputStream(new File(src)));
  byte[] b = newbyte[1024];
  int numBytes = 0;
  while ((numBytes= in.read(b)) > 0) {
    out.write(b, 0,numBytes);
  }
  in.close();
  out.close();
  fs.close();
}
/** Copy an HDFS file to local file system */
public void copyToLocal(String src, String dst) throwsIOException {
  Configurationconf = new Configuration();
  FileSystem fs =FileSystem.get(conf);
  FSDataInputStreamin = fs.open(new Path(src));
  OutputStream out= new BufferedOutputStream(new FileOutputStream(new File(dst)));
  byte[] b = newbyte[1024];
  int numBytes = 0;
  while ((numBytes= in.read(b)) > 0) {
    out.write(b, 0,numBytes);
  }
  in.close();
  out.close();
  fs.close();
}

在示例中,我们使用FileSystem.create方法在指定的路径上创建一个FSDataOutputStream。如果文件存在,它将被默认覆盖。 Path对象用于定位HDFS中的文件或目录。路径真的是一个URI。对于HDFS,它采用hdfs:// host:port / location的格式。要读取HDFS文件,我们使用方法FileSystem.open返回一个FSDataInputStream对象。其余的例子就像常规的Java I / O流操作一样。

--未完待续

0 人点赞