当前位置:   article > 正文

《Hadoop权威指南》第三章 Hadoop分布式文件系统_httpfs 性能瓶颈

httpfs 性能瓶颈

Hadoop权威指南》第三章 Hadoop分布式文件系统


目录

  1. 前言
  2. HDFS的设计
  3. HDFS的概念
  4. 命令行接口
  5. Hadoop文件系统
  6. Java接口
  7. 数据流
  8. 通过distcp并行复制

注:《Hadoop权威指南》重点学习摘要笔记


1. 前言

  1. 数据集的大小超过一台独立的物理计算机的存储能力时,就有必要对它进行分区(partition)并存储到若干台单独的计算机上。管理网络中跨多台计算机存储的文件系统称为分布式文件系统(distributed filesystem)。该系统架构于网络之上,势必会引人网络编程的复杂性,因此分布式文件系统比普通磁盘文件系统更为复杂。例如,使文件系统能够容忍节点故障且不丢失任何数据,就是一个极大的挑战。

  2. Hadoop自带一个称为HDFS的分布式文件系统,即Hadoop Distributed Filesystem。在非正式文档或旧文档以及配置文件中,有时也简称为DFS,它们是一回事儿。HDFS是Hadoop的旗舰级文件系统,也是本章的重点,但实际上Hadoop是一个综合性的文件系统抽象,因此接下来我们将了解将Hadoop与其他存储系统集成的途径,例如本地文件系统和AmazonS3系统。


2. HDFS的设计

  1. HDFS以流式数据访问模式来存储超大文件,运行于商用硬件集群上。让我们仔细看看下面的描述:

    • 超大文件:超大文件在这里指具有几百MB,几百GB甚至几百TB大小的文件。目前已经有存储PB级数据的Hadoop集群了。
    • 流式数据访问HDFS的构建思路是这样的:一次写人、多次读取是最高效的访问模式。数据集通常由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各种分析。每次分析都将涉及该数据集的大部分数据甚至全部,因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。
    • 商用硬件Hadoop并不需要运行在昂贵且高可靠的硬件上。它是设计运行在商用硬件(在各种零售店都能买到的普通硬件)的集群上的,因此至少对于庞大的集群来说,节点故障的几率还是非常高的。HDFS遇到上述故障时,被设计成能够继续运行且不让用户察觉到明显的中断。
  2. 同样,那些不适合在HDFS上运行的应用也值得研究。目前HDFS对某些应用领域并不适合,不过以后可能会有所改进。

    • 低时间延迟的数据访问:要求低时间延迟数据访问的应用,例如几十毫秒范围,不适合在HDFS上运行。记住,HDFS是为高数据吞吐量应用优化的,这可能会以提高时间延迟为代价。目前,对于低延迟的访问需求,HBase是更好的选择。
    • 大量的小文件:由于namenode将文件系统的元数据存储在内存中,因此该文件系统所能存储的文件总数受限于namenode的内存容量。根据经验,每个文件、目录和数据块的存储信息大约占150字节。因此,举例来说,如果有一百万个文件,且每个文件占一个数据块,那至少需要300MB的内存。尽管存储上百万个文件是可行的,但是存储数十亿个文件就超出了当前硬件的能力。
    • 多用户写入,任意修改文件:HDFS中的文件写人只支持单个写人者,而且写操作总是以“只添加”方式在文件末尾写数据。它不支持多个写人者的操作,也不支持在文件的任意位置进行修改。可能以后会支持这些操作。

3. HDFS的概念

1. 数据块
  1. 每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位。构建于单个磁盘之上的文件系统通过磁盘块来管理该文件系统中的块,该文件系统块的大小可以是磁盘块的整数倍。文件系统块一般为几千字节,而磁盘块一般为512字节。这些信息(文件系统块大小)对于需要读/写文件的文件系统用户来说是透明的。尽管如此,系统仍然提供了一些工具(如df和fsck)来维护文件系统,由它们对文件系统中的块进行操作。

  2. HDFS同样也有块(block)的概念,但是大得多,默认为128MB。与单一磁盘上的文件系统相似,HDFS上的文件也被划分为块大小的多个分块(chunk),作为独立的存储单元。但与面向单一磁盘的文件系统不同的是,HDFS中小于一个块大小的文件不会占据整个块的空间(例如,当一个1MB的文件存储在一个128MB的块中时,文件只使用1MB的磁盘空间,而不是128MB)。如果没有特殊指出,本书中提到的“块”特指HDFS中的块。

  3. HDFS中的块为什么那么大?

    1. HDFS的块比磁盘的块大,其目的是为了最小化寻址开销。如果块足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间·因而,传输一个由多个块组成的大文件的时间取决于磁盘传输速率。
    2. 我们来做一个速算,如果寻址时间约为10ms,传输速率为100MB/s,为了使寻址时间仅占传输时间的1%,我们要将块大小设置约为100MB。默认的块大小实际为128MB,但是很多情况下HDFS安装时使用更大的块。以后随着新一代磁盘驱动器传输速率的提升,块的大小会被设置得更大。但是这个参数也不会设置得过大。MapReduce中的map任务通常一次只处理一个块中的数据,因此如果任务数太少(少于集群中的节点数量),作业的运行速度就会比较慢。
  4. 分布式文件系统中的块进行抽象会带来很多好处。第一个最明显的好处是,一个文件的大小可以大于网络中任意一个磁盘的容量。文件的所有块并不需要存储在同一个磁盘上,因此它们可以利用集群上的任意一个磁盘进行存储。事实上,尽管不常见,但对于整个HDFS集群而言,也可以仅存储一个文件,该文件的块占满集群中所有的磁盘。

  5. 第二个好处是,使用抽象块而非整个文件作为存储单元,大大简化了存储子系统的设计。简化是所有系统的目标,但是这对于故障种类繁多的分布式系统来说尤为重要。将存储子系统的处理对象设置为块,可简化存储管理(由于块的大小是固定的,因此计算单个磁盘能存储多少个块就相对容易)。同时也消除了对元数据的顾虑(块只是要存储的大块数据,而文件的元数据,如权限信息,并不需要与块一同存储,这样一来,其他系统就可以单独管理这些元数据)。

  6. 不仅如此,块还非常适合用于数据备份进而提供数据容错能力和提高可用性。将每个块复制到少数几个物理上相互独立的机器上(默认为3个),可以确保在块、磁盘或机器发生故障后数据不会丢失。如果发现一个块不可用,系统会从其他地方读取另一个复本,而这个过程对用户是透明的。一个因损坏或机器故障而丢失的块可以从其他候选地点复制到另一台可以正常运行的机器上,以保证复本的数量回到正常水平。同样,有些应用程序可能选择为一些常用的文件块设置更高的复本数量进而分散集群中的读取负载。

  7. 与磁盘文件系统相似,HDFS中fsck指令可以显示块信息。例如,执行以下命令将列出文件系统中各个文件由哪些块构成。
    在这里插入图片描述

2. namenode和datanode
  1. HDFS集群有两类节点以管理节点-工作节点模式运行,即一个namenode(管理节点)和多个datanode(工作节点)。namenode管理文件系统的命名空间。它维护着文件系统树及整棵树内所有的文件和目录。这些信息以两个文件形式永久保存在本地磁盘上命名空间镜像文件和编辑日志文件。namenode也记录着每个文件中各个块所在的数据节点信息,但它并不永久保存块的位置信息,因为这些信息会在系统启动时根据数据节点信息重建。
  2. 客户端(client)代表用户通过与namenode和datanode交互来访问整个文件系统。客户端提供一个类似于POSIX(可移植操作系统界面)的文件系统接口,因此用户在编程时无需知道namenode和datanode也可实现其功能。
  3. datanode是文件系统的工作节点。它们根据需要存储并检索数据块(受客户端或namenode调度),并且定期向namenode发送它们所存储的块的列表。
  4. 没有namenode,文件系统将无法使用。事实上,如果运行namenode服务的机器毁坏,文件系统上所有的文件将会丢失,因为我们不知道如何根据datanode的块重建文件。因此,对namenode实现容错非常重要,Hadoop为此提供两种机制。
    1. 第一种机制是备份那些组成文件系统元数据持久状态的文件。Hadoop可以通过配置使namenode在多个文件系统上保存元数据的持久状态。这些写操作是实时同步的,且是原子操作。一般的配置是,将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS)。
    2. 另一种可行的方法是运行一个辅助namenode,但它不能被用作namenode。这个辅助namenode的重要作用是定期合并编辑日志与命名空间镜像,以防止编辑日志过大。这个辅助namenode一般在另一台单独的物理计算机上运行,因为它需要占用大量CPU时间,并且需要与namenode一样多的内存来执行合并操作。它会保存合并后的命名空间镜像的副本,并在namenode发生故障时启用。但是,辅助namenode保存的状态总是滞后于主节点,所以在主节点全部失效时,难免会丢失部分数据。在这种情况下,一般把存储在NFS上的namenode元数据复制到辅助namenode并作为新的主namenode运行。
3. 缓存块
  1. 通常datanode从磁盘中读取块,但对于访问频繁的文件,其对应的块可能被显式地缓存在datanode的内存中,以堆外块缓存(off-heap block cache)的形式存在。默认情况下,一个块仅缓存在一个datanode的内存中,当然可以针每个文件配置datanode的数量。作业调度器(用于MapReduce、Spark和其他框架的)通过在缓存块的datanode上运行任务,可以利用块缓存的优势提高读操作的性能。例如,连接(join)操作中使用的一个小的查询表就是块缓存的一个很好的候选。
  2. 用户或应用通过在缓存池(cache pool)中增加一个cache directive 来告诉namenode需要缓存哪些文件及存多久。缓存池是一个用于管理缓存权限和资源使用的管理性分组。
4. 联邦HDFS
  1. namenode在内存中保存文件系统中每个文件和每个数据块的引用关系,这意味着对于一个拥有大量文件的超大集群来说,内存将成为限制系统横向扩展的瓶颈。在2.x发行版本系列中引人的联邦HDFS允许系统通过添加namenode实现扩展,其中每个namenode管理文件系统命名空间中的一部分。例如,一个namenode可能管理/user 目录下的所有文件,而另一个namenode可能管理/share 目录下的所有文件。
  2. 在联邦环境下,每个namenode维护一个命名空间卷(namespace volume),由命名空间的元数据和一个数据块池(block pool)组成,数据块池包含该命名空间下文件的所有数据块。命名空间卷之间是相互独立的,两两之间并不相互通信,甚至其中一个namenode的失效也不会影响由其他namenode维护的命名空间的可用性。数据块池不再进行切分,因此集群中的datanode需要注册到每个namenode,并且存储着来自多个数据块池中的数据块。
  3. 要想访问联邦HDFS集群,客户端需要使用客户端挂载数据表将文件路径映射到namenode。该功能可以通过ViewFi1eSystem和 viewfs://URI进行配置和管理。
5. HDFS的高可用性
  1. 通过联合使用在多个文件系统中备份namenode的元数据和通过备用namenode创建监测点能防止数据丢失,但是依旧无法实现文件系统的高可用性。namenode依旧存在单点失效(SPOF,single point of failure)的问题。如果namenode失效了,那么所有的客户端,包括MapReduce作业,均无法读、写或列举(list)文件,因为namenode是唯一存储元数据与文件到数据块映射的地方。在这一情况下,Hadoop系统无法提供服务直到有新的namenode上线。

  2. 在这样的情况下,要想从一个失效的namenode恢复,系统管理员得启动一个拥有文件系统元数据副本的新的namenode,并配置datanode和客户端以便使用这个新的namenode。新的namenode直到满足以下情形才能响应服务:

    1. 将命名空间的映像导人内存中;
    2. 重演编辑日志;
    3. 接收到足够多的来自datanode的数据块报告并退出安全模式。
  3. 对于一个大型并拥有大量文件和数据块的集群,namenode的冷启动需要30分钟,甚至更长时间。

  4. 系统恢复时间太长,也会影响到日常维护。事实上,预期外的namenode失效出现概率很低,所以在现实中,计划内的系统失效时间实际更为重要。

  5. Hadoop2针对上述问题增加了对HDFS高可用性(HA)的支持。在这一实现中,配置了一对活动-备用(active-standby)namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显中断。实现这一目标需要在架构上做如下修改。

    1. namenode之间需要通过高可用共享存储实现编辑日志的共享。当备用namenode接管工作之后,它将通读共享编辑日志直至末尾,以实现与活动namenode的状态同步,并继续读取由活动namenode写人的新条目。
    2. datanode需要同时向两个namenode发送数据块处理报告,因为数据块的映射信息存储在namenode的内存中,而非磁盘。
    3. 客户端需要使用特定的机制来处理namenode的失效问题,这一机制对用户是透明的。
    4. 辅助namenode的角色被备用namenode所包含,备用namenode为活动的namenode命名空间设置周期性检查点。
  6. 可以从两种高可用性共享存储做出选择:NFS过滤器或群体日志管理器(QJM,quorum journal manager)。QJM是一个专用的HDFS实现,为提供一个高可用的编辑日志而设计,被推荐用于大多数HDFS部署中。QJM以一组日志节点(journalnode)的形式运行,每一次编辑必须写入多数日志节点。典型的,有三个journal节点,所以系统能够忍受其中任何一个的丢失。这种安排与Zookeeper的工作方式类似,当然必须认识到,QJM的实现并没使用Zookeeper。(然而,值得注意的是,HDFS HA在选取活动的namenode时确实使用了Zookeeper技术)

  7. 在活动namenode失效之后,备用namenode能够快速(几十秒的时间)实现任务接管,因为最新的状态存储在内存中:包括最新的编辑日志条目和最新的数据块映射信息。实际观察到的失效时间略长一点(需要1分钟左右),这是因为系统需要保守确定活动namenode是否真的失效了。

  8. 在活动namenode失效且备用namenode也失效的情况下,当然这类情况发生的概率非常低,管理员依旧可以声明一个备用namenode并实现冷启动。这类情况并不会比非高可用(non·HA)的情况更差,并且从操作的角度讲这是一个进步,因为上述处理已是一个标准的处理过程并植人Hadoop中。

故障切换与规避
  1. 系统中有一个称为故障转移控制器(failover controller)的新实体,管理着将活动namenode转移为备用namenode的转换过程。有多种故障转移控制器,但默认的一种是使用了Zookeeper来确保有且仅有一个活动namenode。每一个namenode运行着一个轻量级的故障转移控制器,其工作就是监视宿主namenode是否失效(通过一个简单的心跳机制实现)并在namenode失效时进行故障切换。
  2. 管理员也可以手动发起故障转移,例如在进行日常维护时。这称为“平稳的故障转移”(graceful failover),因为故障转移控制器可以组织两个namenode有序地切换角色。
  3. 但在非平稳故障转移的情况下,无法确切知道失效namenode是否已经停止运行。例如,在网速非常慢或者网络被分割的情况下,同样也可能激发故障转移,但是先前的活动namenode依然运行着并且依旧是活动namenode。高可用实现做了更进一步的优化,以确保先前活动的namenode不会执行危害系统并导致系统崩溃的操作,该方法称为“规避”(fencing)。
  4. 同一时间QJM仅允许一个namenode向编辑日志中写人数据。然而,对于先前的活动namenode而言,仍有可能响应并处理客户过时的读请求,因此,设置一个SSH规避命令用于杀死namenode的进程是一个好主意。当使用NFS过滤器实现共享编辑日志时,由于不可能同一时间只允许一个namenode写人数据(这也是为什么推荐QJM的原因),因此需要更有力的规避方法。规避机制包括:撤销namenode访问共享存储目录的权限(通常使用供应商指定的NFS命令)、通过远程管理命令屏蔽相应的网络端口。诉诸的最后手段是,先前活动namenode可以通过一个相当形象的称为“一枪爆头”STONITH,shoot the other node in the head)的技术进行规避,该方法主要通过一个特定的供电单元对相应主机进行断电操作。
  5. 客户端的故障转移通过客户端类库实现透明处理。最简单的实现是通过客户端的配置文件实现故障转移的控制。HDFS URI使用一个逻辑主机名,该主机名映射到一对namenode地址(在配置文件中设置),客户端类库会访问每一个namenode地址直至处理完成。

4. 命令行接口

  1. 现在我们通过命令行交互来进一步认识HDFS。HDFS还有很多其他接口,但命令行是最简单的,同时也是许多开发者最熟悉的。
  2. 参照附录A中伪分布模式下设置Hadoop的说明,我们先在一台机器上运行HDFS。稍后介绍如何在集群上运行HDFS,以提供可扩展性与容错性。
  3. 在我们设置伪分布配置时,有两个属性项需要进一步解释。第一项是fs.defaultFS,设置为hdfs://localhost/,用于设置Hadoop的默认文件系统。文件系统是由URI指定的,这里我们已使用hdfs URI来配置HDFS为Hadoop的默认文件系统。HDFS的守护程序通过该属性项来确定HDFS namenode的主机及端口。我们将在localhost默认的HDFS端口8020上运行namenode。样一来,HDFS客户端可以通过该属性得知namenode在哪里运行进而连接到它。
  4. 第二个属性dfs.replication,我们设为1,这样一来,HDFS就不会按默认设置将文件系统块复本设为3。在单独一个datanode上运行时,HDFS无法将块复制到3个datanode上,所以会持续给出块复本不足的警告。设置这个属性之后,上述问题就不会再出现了。
1. 文件系统的基本操作
  1. 至此,文件系统已经可以使用了,我们可以执行所有常用的文件系统操作,例如,读取文件,新建目录,移动文件,删除数据,列出目录,等等。可以输人hadoop fs -help命令获取每个命令的详细帮助文件。

  2. 首先从本地文件系统将一个文件复制到HDFS:

    %hadoop fs -copyFromLocal input/docs/quangle.txt \ hdfs://localhost/user/tom/quangle.txt
    
    • 1
  3. 该命令调用Hadoop文件系统的shell命令fs,后者提供了一系列子命令,在这个例子中,我们执行的是-copyFromLocal。本地文件quangle.txt被复制到运行在localhost上的HDFS实例中,路径为/user/tom/quangle.txt。事实上,我们可以简化命令格式以省略主机的URI并使用默认设置,即省略hdfs://localhost,因为该项已在core-site.xml中指定。

    %hadoop fs -copyFromLoca1 input/docs/quangle.txt /user/tom/quangle.txt
    
    • 1
  4. 我们也可以使用相对路径,并将文件复制到HDFS的home目录中,本例中为

    %hadoop fs -copyFromLoca1 input/docs/quangle.txt quangle.txt
    
    • 1
  5. 我们把文件复制回本地文件系统,并检查是否一致:

    %hadoop fs -copyToLocal quangle.txt quangle.copy.txt
    %md5 input/docs/quangle.txt quangle.copy.txt
    MD5(input/docs/quangle.txt)=e7891a2627cf263a979fb9f18256ffb2
    MD5(quangle.copy.txt)=e7891a2627cf263a979fb9f18256ffb2
    
    • 1
    • 2
    • 3
    • 4
  6. MD5键值相同,表明这个文件在HDFS之旅中得以幸存并保存完整。

  7. 最后,看一下HDFS文件列表。我们新建一个目录,看它在列表中怎么显示:

    %hadoop fs -mkdir books
    %hadoop fs -ls .
    
    • 1
    • 2

在这里插入图片描述
8. 返回的结果信息与Unix命令Is -l 的输出结果非常相似,仅有细微差别。第1列显示的是文件模式。第2列是这个文件的备份数(这在传统Unix文件系统是没有的)。由于我们在整个文件系统范围内设置的默认复本数为1,所以这里显示的也都是1。这一列的开头目录为空,因为本例中没有使用复本的概念,目录作为元数据保存在namenode中,而非datanode中。第3列和第4列显示文件的所属用户和组别。第5列是文件的大小,以字节为单位,目录为0。第6列和第7列是文件的最后修改日期与时间。最后,第8列是文件或目录的名称。

2. HDFS中的文件访问权限
  1. 针对文件和目录,HDFS的权限模式与POSIX的权限模式非常相似。
  2. 一共提供三类权限模式:只读权限®、写入权限(w)和可执行权限(x)。读取文件或列出目录内容时需要只读权限。写入一个文件或是在一个目录上新建及删除文件或目录,需要写入权限。对于文件而言,可执行权限可以忽略,因为你不能在HDFS中执行文件(与POSIX不同),但在访问一个目录的子项时需要该权限。
  3. 每个文件和目录都有所属用户(owner)、所属组别(group)及模式(mode)。这个模式是由所属用户的权限、组内成员的权限及其他用户的权限组成的。
  4. 在默认情况下,Hadoop运行时安全措施处于停用模式,意味着客户端身份是没有经过认证的。由于客户端是远程的,一个客户端可以在远程系统上通过创建和任一个合法用户同名的账号来进行访问。当然,如果安全设施处于启用模式,这些都是不可能的。无论怎样,为防止用户或自动工具及程序意外修改或删除文件系统的重要部分,启用权限控制还是很重要的(这也是默认的配置,参见dfs.permissions.enabled属性)
  5. 如果启用权限检查,就会检查所属用户权限,以确认客户端的用户名与所属用户是否匹配,另外也将检查所属组别权限,以确认该客户端是否是该用户组的成员;若不符,则检查其他权限。
  6. 这里有一个超级用户(super-user)的概念,超级用户是namenode进程的标识·对于超级用户,系统不会执行任何权限检查。

5. Hadoop文件系统

  1. Hadoop有一个抽象的文件系统概念,HDFS只是其中的一个实现。Java抽象类org.apache.hadoop.fs.FileSystem定义了Hadoop中一个文件系统的客户端接口,并且该抽象类有几个具体实现,其中和Hadoop紧密相关的见表3.1。
    在这里插入图片描述

  2. Hadoop对文件系统提供了许多接口,它一般使用URI方案来选取合适的文件系统实例进行交互。举例来说,我们在前一小节中遇到的文件系统命令行解释器可以操作所有的Hadoop文件系统命令。要想列出本地文件系统根目录下的文件,可以输人以下命令:

    %hadoop fs -ls file:///
    
    • 1
  3. 尽管运行的MapReduce程序可以访问任何文件系统(有时也很方便),但在处理大数据集时,建议你还是选择一个有数据本地优化的分布式文件系统,如HDFS。

1. 接口

Hadoop是用Java写的,通过Java API可以调用大部分Hadoop文件系统的交互操作。例如,文件系统的命令解释器就是一个Java应用,它使用Java的FileSystem类来提供文件系统操作。其他一些文件系统接口也将在本小节中做简单介绍。这些接口通常与HDFS一同使用,因为Hadoop中的其他文件系统一般都有访问基本文件系统的工具(对于FTP,有FTP客户端;对于S3,有S3工具,等等),但它们大多数都能用于任何Hadoop文件系统。

1. HTTP
  1. Hadoop以Java API的形式提供文件系统访问接口,非Java开发的应用访问HDFS会很不方便。由Web HDFS协议提供的HTTP REST API则使得其他语言开发的应用能够更方便地与HDFS交互。注意,HTTP接口比原生的Java客户端要慢,所以不到万不得已,尽量不要用它来传输特大数据。
  2. 通过HTTP来访问HDFS有两种方法:直接访问,HDFS守护进程直接服务于来自客户端的HTTP请求;通过代理(一个或多个)访问,客户端通常使用DistributedFileSystem API访问HDFS。这两种方法如图所示。两者都使用了WebHDFS协议。
    在这里插入图片描述
  3. 在第一种情况中,namenode和datanode内嵌的web服务器作为WebHDFS的端节点运行。(由于dfs.webhdfs.enabled被设置为true,WebHDFS默认是启用状态。)文件元数据操作由namenode管理,文件读(写)操作首先被发往namenode,由namenode发送一个HTTP重定向至某个客户端,指示以流方式传输文件数据的目的或源datanode。
  4. 第二种方法依靠一个或者多个独立代理服务器通过HTTP访问HDFS。(由于代理服务是无状态的,因此可以运行在标准的负载均衡器之后。)所有到集群的网络通信都需要经过代理,因此客户端从来不直接访问namenode或datanode。使用代理服务器后可以使用更严格的防火墙策略和带宽限制策略。通常情况下都通过代理服务器,实现在不同数据中心中部署的Hadoop集群之间的数据传输,或从外部网络访问云端运行的Hadoop集群。
  5. HttpFS 代理提供和WebHDFS相同的HTTP(和HTTPS)接口,这样客户端能够通过webhdfs(swebhdfs) URI访问这两类接口。HttpFS代理的启动独立于namenode和datanode的守护进程,使用httpfs.sh脚本,默认在一个不同的端口上监听(端口号14000)。
2. C语言
  1. Hadoop提供了一个名为libhdfs的C语言库,该语訁库是Java Filesystem接口类的一个镜像(它被写成访问HDFS的C语言库,但其实它可以访问任何一个Hadoop文件系统)。它使用Java原生接口(JNI,Java Native Interface)调用Java文件系统客户端。同样还有一个libwebfhdfs库,该库使用了前述章节描述的WebHDFS接口。
  2. 这个C语言API与Java的API非常相似,但它的开发滞后于JavaAPI,因此目前一些新的特性可能还不支持。可以在Apache Hapdoop二进制压缩发行包的include目录中找到头文件hdfs.h。
  3. Apache Hapdoop二进制压缩包自带预先编译好的libhdfs二进制编码,支持64位Linux。但对于其他平台,需要按照源代码树顶层的BUILDING.txt指南自行编译。
3. NFS
  1. 使用Hadoop的NFSv3网关将HDFS挂载为本地客户端的文件系统是可行的。然后你可以使用Unix实用程序(如Is和cat)与该文件系统交互,上传文件,通过任意一种编程语言调用POSIX库来访问文件系统。由于HDFS仅能以追加模式写文件,因此可以往文件末尾添加数据,但不能随机修改文件。
  2. 关于如何配置和运行NFS网关,以及如何从客户端连接网关,可以参考Hadoop相关文档资料。
4. FUSE
  1. 用户空间文件系统(FUSE,Filesystem in Userspace,)允许将用户空间实现的文件系统作为Unix文件系统进行集成。通过使用Hadoop的Fuse-DFS功能模块,HDFS(或任何一个Hadoop文件系统)均可以作为一个标准的本地文件系统进行挂载。Fuse-DFS是用C语言实现的,使用libhdfs作为访问HDFS的接口。在写操作时,Hadoop NFS网关对于挂载HDFS来说是更健壮的解决方案,相比Fuse-DFS而言应优先选择。

6. JAVA接口

有时间补上。


7. 数据流

1. 剖析文件读取
  1. 为了了解客户端及与之交互的HDFS、namenode和datanode之间的数据流是什么样的,我们可参考下图,该图显示了在读取文件时事件的发生顺序。
  2. 客户端通过调用Filesyste对象的open()方法来打开希望读取的文件,对于HDFS来说,这个对象是DistributedFileSystem的一个实例(下图中的步骤1)。DistributedFileSystem通过使用远程过程调用(RPC)来调用namenode,以确定文件起始块的位置(步骤2)。对于每一个块,namenode返回存有该块副本的datanode地址。此外,这些datanode根据它们与客户端的距离来排序(根据集群的网络拓扑)。如果该客户端本身就是一个datanode(比如,在一个MapReduce任务中),那么该客户端将会从保存有相应数据块复本的本地datanode读取数据。
    在这里插入图片描述
  3. DistributedFileSystem类返回一个FSDataInputStream对象(一个支持文件定位的输人流)给客户端以便读取数据。FSDatainputstream类转而封装DFSInputStream对象,该对象管理着datanode和namenode的I/0。
  4. 接着,客户端对这个输人流调用read()方法(步骤3)。存储着文件起始几个块的datanode地址的DFSInputstream随即连接距离最近的文件中第一个块所在的datanode。通过对数据流反复调用read()方法,可以将数据从datanode传输到客户端(步骤4)。到达块的末端时,DFSlnputStream关闭与该datanode的连接,然后寻找下一个块的最佳datanode(步骤5)。所有这些对于客户端都是透明的,在客户看来它一直在读取一个连续的流。
  5. 客户端从流中读取数据时,块是按照打开DFSInputstream与datanode新建连接的顺序读取的。它也会根据需要询问namenode来检索下一批数据块的datanode的位置。一旦客户端完成读取,就对调用close()方法(步骤6)。
  6. 在读取数据的时候,如果DFSInputStream在与datanode通信时遇到错误,会尝试从这个块的另外一个最邻近datanode读取数据。它也记住那个故障datanode,以保证以后不会反复读取该节点上后续的块。DFSInputStream也会通过校验和确认从datanode发来的数据是否完整。如果发现有损坏的块,DFSInputStream会试图从其他datanode读取其复本,也会将被损坏的块通知给namenode。
  7. 这个设计的一个重点是,客户端可以直接连接到datanode检索数据,且namenode告知客户端每个块所在的最佳datanode。由于数据流分散在集群中的所有datanode,所以这种设计能使HDFS扩展到大量的并发客户端。同时,namenode只需要响应块位置的请求(这些信息存储在内存中,因而非常高效),无需响应数据请求,否则随着客户端数量的增长,namenode会很快成为瓶颈。
网络扩扑与Hadoop
  1. 在本地网络中,两个节点被称为“彼此近邻”是什么意思?在海量数据处理中,其主要限制因素是节点之间数据的传输速率一一带宽很稀缺。这里的想法是将两个节点间的带宽作为距离的衡量标准。
  2. 不用衡量节点之间的带宽,实际上很难实现(它需要一个稳定的集群,并且在集群中两两节点对数量是节点数量的平方),hadoop为此采用一个简单的方法:把网络看作一棵树,两个节点间的距离是它们到最近共同祖先的距离总和。该树中的层次是没有预先设定的,但是相对于数据中心、机架和正在运行的节点,通常可以设定等级。具体想法是针对以下每个场景,可用带宽依次递减:
    • 同一节点上的进程
    • 同一机架上的不同节点
    • 同一数据中心中不同机架上的节点
    • 不同数据中心中的节点.
  3. 例如,假设有数据中心d1机架r1中的节点n1。该节点可以表示为/d1/r1/n1。利用这种标记,这里给出四种距离描述:
    • distance(/d1/r1/n1,/d1/r1/n1)=0(同一节点上的进程)
    • distance(/d1/r1/n1,/d1/r1/n2)=2(同一机架上的不同节点)
    • distance(/d1/r1/n1,/d1/r2/n3)=4(同一数据中心中不同机架上的节点)
    • distance(/d1/r1/n1,/d1/r1/n1)=6(不同数据中心中的节点)
  4. 示意图参见下图(数学爱好者会注意到,这是一个测量距离的例子)。
  5. 最后,我们必须意识到Hadoop无法自动发现你的网络拓扑结构。它需要一些帮助(我们将在10.1.2节的“网络拓扑”中讨论如何配置网络拓扑)。不过在默认情况下,假设网络是扁平化的只有一层,或换句话说,所有节点都在同一数据中心的同一机架上。规模小的集群可能如此,不需要进一步配置。
    在这里插入图片描述
2. 剖析文件写入
  1. 接下来我们看看文件是如何写人HDFS的。尽管比较详细,但对于理解数据流还是很有用的,因为它清楚地说明了HDFS的一致模型。我们要考虑的情况是如何新建一个文件,把数据写人该文件,最后关闭该文件。如下图所示。
  2. 客户端通过对DistributedFileSystem对象调用create()来新建文件(图中的步骤1)。DistributedFileSystem对namenode创建一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中还没有相应的数据块(步骤2)。namenode执行各种不同的检查以确保这个文件不存在以及客户端有新建该文件的权限。如果这些检查均通过,namenode就会为创建新文件记录一条记录;否则,文件创建失败并向客户端抛出一个I0Exception异常。DistributedFileSystem向客户端返回一个FSData0utputStream对象,由此客户端可以开始写人数据。就像读取事件一样,FSDataOutputStream封装一个DFSoutputstream对象,该对象负责处理datanode和namenode之间的通信。
    在这里插入图片描述
  3. 在客户端写人数据时(步骤3),DFSOutputStream将它分成一个个的数据包,并写人内部队列,称为“数据队列”(data queue)。DataStreamer处理数据队列,它的责任是挑选出适合存储数据复本的一组datanode,并据此来要求namenode分配新的数据块。这一组datanode构成一个管线一一我们假设复本数为3,所以管线中有3个节点。DataStreamer将数据包流式传输到管线中第1个datanode,该datanode存储数据包并将它发送到管线中的第2个datanode。同样,第2个datanode存储该数据包并且发送给管线中的第3个(也是最后一个)datanode(步骤4)。
  4. DFSOutputStream也维护着一个内部数据包队列来等待datanode的收到确认回执,称为“确认队列”(ackqueue)。收到管道中所有datanode确认信息后,该数据包才会从确认队列删除(步骤5)。
  5. 如果任何datanode在数据写人期间发生故障,则执行以下操作(对写入数据的客户端是透明的)。首先关闭管线,确认把队列中的所有数据包都添加回数据队列的最前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。为存储在另一正常datanode的当前数据块指定一个新的标识,并将该标识传送给namenode,以便故障datanode在恢复后可以删除存储的部分数据块。从管线中删除故障datanode,基于两个正常datanode构建一条新管线。余下的数据块写人管线中正常的datanodeonamenode注意到块复本量不足时,会在另一个节点上创建一个新的复本。后续的数据块继续正常接受处理。
  6. 在一个块被写人期间可能会有多个datanode同时发生故障,但非常少见。只要写人了dfs.namenode.replication.min的复本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标复本数@fs,repli(ation的默认值为3)。
  7. 客户端完成数据的写人后,对数据流调用close()方法(步骤6)。该操作将剩余的所有数据包写人datanode管线,并在联系到namenode告知其文件写入完成之前,等待确认(步骤7)。namenode已经知道文件由哪些块组成(因为Datastreamer请求分配数据块),所以它在返回成功前只需要等待数据块进行最小量的复制。
复本怎么放
  1. namenode如何选择在哪个datanode存储复本(replica)?这里需要对可靠性、写入带宽和读取带宽进行权衡。例如,把所有复本都存储在一个节点损失的写入带宽最小(因为复制管线都是在同一节点上运行),但这并不提供真实的冗余(如果节点发生故障,那么该块中的数据会丢失)。同时,同一机架上服务器间的读取带宽是很高的。另一个极端,把复本放在不同的数据中心可以最大限度地提高冗余,但带宽的损耗非常大。即使在同一数据中心(到目前为止,,所有Hadoop集群均运行在同一数据中心内),也有多种可能的数据布局策喀。
  2. Hadoop的默认布局策略是在运行客户端的节点上放第1个复本(如果客户端运行在集群之外,就随机选择一个节点,不过系统会避免挑选那些存储太满或太忙的节点)。第2个复本放在与第一个不同且随机另外选择的机架中节点上(离架)。第3个复本与第2个复本放在同一个机架上,且随机选择另一个节点。其他复本放在集群中随机选择的节点上,不过系统会尽量避免在同一个的机架上放太多复本。
  3. 一旦选定复本的放置位置,就根据网络拓扑创建一个管线。如果复本数为3,则有下图所示的管线。
    在这里插入图片描述
  4. 总的来说,这一方法不仅提供很好的稳定性(数据块存储在两个机架中)并实现很好的负载均衡,包括写入带宽(写入操作只需要遍历一个交换机)、读取性能(可以从两个机架中选择读取)和集群中块的均匀分布(客户端只在本地机架上写入一个块)。
3. 一致性模型
  1. 文件系统的一致模型(coherency model)描述了文件读/写的数据可见性。HDFS为性能牺牲了一些POSIX要求,因此一些操作与你期望的可能不同。

  2. 新建一个文件之后,它能在文件系统的命名空间中立即可见,如下所示:
    在这里插入图片描述

  3. 但是,写人文件的内容并不保证能立即可见,即使数据流已经刷新并存储。所以文件长度显示为0:
    在这里插入图片描述

  4. 当写人的数据超过一个块后,第一个数据块对新的reader就是可见的。之后的块也不例外。总之,当前正在写人的块对其他reader不可见。

  5. HDFS提供了一种强行将所有缓存刷新到datanode中的手段,即对FSDataOutputStream调用hflush()方法。当hflush()方法返回成功后,对所有新的reader而言,HDFS能保证文件中到目前为止写人的数据均到达所有datanode的写人管道并且对所有新的reader均可见:

  6. 注意,hflush()不保证datanode已经将数据写到磁盘上,仅确保数据在datanode的内存中(因此,如果数据中心断电,数据会丢失)。为确保数据写人到磁盘上,可以用hsync()替代.

  7. hsync()操作类似于POSIX中的fsync()系统调用,该调用提交的是一个文件描述符的缓冲数据。例如,利用标准Java API数据写人本地文件,我们能够在刷新数据流且同步之后看到文件内容:
    在这里插入图片描述

  8. 在HDFS中关闭文件其实隐含了执行hflush()方法
    在这里插入图片描述
    待续…

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/276708
推荐阅读
相关标签
  

闽ICP备14008679号