Apache Hadoop 2.9.2文档中文译文 -------未完!!!!!!

目录

一. General(概括)

1. Overview

2. Single Node Setup

3. Cluster Setup

4. Commands Reference

5. FileSystem Shell

6. Hadoop Compatibility

7. Interface Classification

8. FileSystem Specification

二. Common(共同)

1. CLI Mini Cluster

2. Native Libraries

3. Proxy User

4. Rack Awareness

5. Secure Mode

6. Service Level Authorization

7. HTTP Authentication

8. Credential Provider API

9. Hadoop KMS

10. Tracing

三. HDFS

1. Architecture

2. User Guide

3. Commands Reference

4. NameNode HA With QJM

5. NameNode HA With NFS

6. Federation

7. ViewFs

8. Snapshots

9. Edits Viewer

10. Image Viewer

11. Permissions and HDFS

12. Quotas and HDFS

13. HFTP

14. libhdfs (C API)

15. WebHDFS (REST API)

16. HttpFS

17. Short Circuit Local Reads

18. Centralized Cache Management

19. NFS Gateway

20. Rolling Upgrade

21. Extended Attributes

22. Transparent Encryption

23. Multihoming

24. Storage Policies

25. Memory Storage Support

26. Upgrade Domain

27. DataNode Admin

28. Router Federation

四. MapReduce

1. Tutorial

2. Commands Reference

3. Compatibility with 1.x

4. Encrypted Shuffle

5. Pluggable Shuffle/Sort

6. Distributed Cache Deploy

7. Support for YARN Shared Cache

五. MapReduce REST APIs

1. MR Application Master

2. MR History Server

六. YARN

1. Architecture

2. Commands Reference

3. Capacity Scheduler

4. Fair Scheduler

5. ResourceManager Restart

6. ResourceManager HA

7. Node Labels

8. Web Application Proxy

9. Timeline Server

10. Timeline Service V.2

11. Writing YARN Applications

12. YARN Application Security

13. NodeManager

14. DockerContainerExecutor

15. Running Applications in Docker Containers

16. Using CGroups

17. Secure Containers

18. Registry

19. Reservation System

20. Graceful Decommission

21. Opportunistic Containers

22. YARN Federation

23. Shared Cache

七. YARN REST APIs

1. Introduction

2. Resource Manager

3. Node Manager

4. Timeline Server

5. Timeline Service V.2

八. Hadoop compatible File Systems

1. Amazon S3

2. Azure Blob Storage

3. OpenStack Swift

九. Auth

1. Overview

2. Examples

3. Configuration

4. Building

十. Tools

1. Hadoop Streaming

2. Hadoop Archives

3. Hadoop Archive Logs

4. DistCp

5. GridMix

6. Rumen

7. Resource Estimator Service

8. Scheduler Load Simulator

9. Hadoop Benchmarking

十一. Reference

1. Changelog and Release Notes

2. API docs

3. Metrics

十二. Configuration

1. core-default.xml

2. hdfs-default.xml

3. mapred-default.xml

4. yarn-default.xml

5. Deprecated Properties


一. General(概括)

1. Overview(概述)

Apache Hadoop 2.9.2

Apache Hadoop 2.9.2是2.x.y发行版中的一个点发行版,它建立在前一个稳定版2.9.1的基础上。

以下是主要特性和改进的简要概述:

*  Common

        >  阿里云OSS支持

       >  Hadoop资源估测器

*  HDFS

       >  基于HDFS路由器的联合

*  YARN

       >  YARN 时间轴服务v.2

       >  YARN联合

       >  随即容器

      >  YARN Web UI v.2

      >  通过API(仅在容量调度器上支持)改变队列配置

      >  更新已分配/正在运行的容器的资源和执行类型。(仅在容量调度器上支持)

入门(Getting Started)

Hadoop文档包括开始使用Hadoop所需要的信息。从单节点设置(Single Node Setup)开始,它向您展示了如何设置单节点Hadoop安装;然后转到集群设置(Cluster Setup),学习如何设置多节点Hadoop安装。

2. Single Node Setup

Hadoop: Setting up a Single Node Cluster.

Purpose

本文档描述了如何设置和配置单节点Hadoop安装,以便您可以使用Hadoop MapReduce和Hadoop分布式文件系统(HDFS)快速执行简单操作。

Prerequisites(前提条件)

支持的平台

  • 支持GNU/Linux作为开发和生产平台。Hadoop已经在具有2000个节点的GNU/Linux集群上进行了演示。
  • 也支持Window平台,但是下面的步骤只针对Linux。要在Window上设置Hadoop,请参阅wiki page。

所需软件

对于Linux需要的软件包括:

1. 必须安装Java。在  HadoopJavaVersions上描述了推荐的Java版本。

2. 必须安装ssh,并运行sshd以使用 管理远程Hadoop守护进程的Hadoop脚本。

安装软件

如果你的集群没有必备的软件,您将需要安装它。

例如在Ubuntu Linux:

  $ sudo apt-get install ssh
  $ sudo apt-get install rsync

Download

要获得一个Hadoop发行版,请从Apache Download Mirrors之一下载最新的稳定版本。

Prepare to Start the Hadoop Cluster

解压下载的Hadoop发行版。在发行版中,编辑etc/hadoop/hadoop-env.sh文件来定义以下一些参数:

  # set to the root of your Java installation
  export JAVA_HOME=/usr/java/latest

尝试以命令:

       $ bin/hadoop

 

这将显示hadoop脚本的使用文档。

现在您将在三种支持的模式之一中启动Hadoop集群:

  • Local(Standalone) Mode--本地(独立)模式
  • Pseudo-Distributed Mode--伪分布模式
  • Fully-Distributed Mode--完全分布式模式

Standalone Operation

默认情况下,Hadoop被配置为在非分布式模式下运行,作为单个Java进程。这对调试很有用。

下面的示例复制解压后的conf目录用作输入,然后查找并显示给定的正则表达式的每个匹配项。输出被写入给定的输出目录。

  $ mkdir input
  $ cp etc/hadoop/*.xml input
  $ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.9.2.jar grep input output 'dfs[a-z.]+'
  $ cat output/*

Pseudo-Distributed Operation

Hadoop还可以在一个伪分布式模式的单个节点上运行,其中每个Hadoop守护进程在单独的Java进程中运行。

Configuration

使用如下:

etc/hadoop/core-site.xml:

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

etc/hadoop/hdfs-site.xml:

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

Setup passphraseless ssh(设置无密码ssh)

现在检查您在没有密码的情况下,是都能ssh到主机:

$ ssh localhost

如果在没密码的情况下无法ssh到主机,那么就执行下面的命令:

  $ ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
  $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
  $ chmod 0600 ~/.ssh/authorized_keys

Execution

以下说明用于在本地运行MapReduce作业。如果想在YARN上运行作业,请参见YARN on Single Node。

1. 格式化文件系统:

$ bin/hdfs namenode -format

2. 启动NameNode守护进程和DataNode守护进程:

$ sbin/start-dfs.sh

hadoop守护进程日志输出被写入$HADOOP_LOG_DIR目录(默认是$HADOOP_HOME/logs)

3.浏览NameNode的web页面;默认位于:

  • NameNode  -  http://localhost:50070

4.创建执行MapReduce作业所需的HDFS目录:

$ bin/hdfs dfs -mkdir /user
$ bin/hdfs dfs -mkdir /user/<username>

5.将输入文件复制到分布式文件系统中:

$ bin/hdfs dfs -mkdir input
$ bin/hdfs dfs -put etc/hadoop/*.xml input

6.运行提供的一些示例:

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.9.2.jar grep input output 'dfs[a-z.]+'

7.检查输出文件:将输出文件从分布式文件系统复制到本地文件系统,然后查看它们:

$ bin/hdfs dfs -get output output
$ cat output/*

or

在分布式文件系统上查看文件:

$ bin/hdfs dfs -cat output/*    

8.完成后,停止守护进程:

$ sbin/stop-dfs.sh

YARN on a Single Node

通过设置一些参数并运行ResourceManager守护进程和NodeManager守护进程,您可以在伪分布式模式的YARN上运行MapReduce作业。以下说明假定Execution中的前4步已经执行。

1.配置参数如下:

etc/hadoop/mapred-site.xml:

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

etc/hadoop/yarn-site.xml:

<configuration>
    <property>
        <name>yarn.nodemanger.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
</configuration>

2.启动ResourceManager守护进程和NodeManager守护进程

$ sbin/start-yarn.sh

3.浏览ResourceManager web页面;默认位于:

  • ResourceManager  -  http://localhost:8088/

4. 运行MapReduce作业。

5.完成后,停止守护进程。

$ sbin/stop-yarn.sh

Fully-Distributed Operation

有关设置完全分布式、非普通集群的信息,请参阅Cluster Setup.

3. Cluster Setup

Hadoop Cluster Setup

Pursose

本文描述了如何安装和配置Hadoop集群,从几个节点到具有数千个节点的超大集群。要使用Hadoop,您可能首先希望将其安装在一台机器上(请参阅单节点设置)

本文档不包括安全性或高可用性等高级主题。

Prerequisites(前提条件)

  • 安装Java。参阅Hadoop Wiki了解已知的好的版本。
  • 从Apache镜像下载稳定版Hadoop。

Installation(安装)

安装Hadoop集群通常需要在集群中的所有机器上解压软件,或者通过适合您的操作系统的打包系统安装软件。把硬件分成几个功能是很重要的。

通常集群中的一台机器被指定为Namenode,另一台机器被专门指定为ResourceManager。这些都是主机。其它服务(比如web应用代理服务器和MapReduce作业历史服务器)通常运行在专用硬件或共享基础设施上,这取决于负载。

集群中其它机器同时充当DataNode和NodeManager。这些都是slaves。

Configuring Hadoop in Non-Secure Mode(非安全模式下配置Hadoop)

Hadoop的Java配置是由两种重要的配置文件驱动的:

  • 只读默认配置 -- core-default.xml,hdfs-default.xml,yarn-default.xml,mapred-default.xml。
  • 特定站点配置 -- etc/hadoop/core-site.xml,etc/hadoop/hdfs-site.xml,etc/hadoop/yarn-site.xml,etc/hadoop/mapred-site.xml。

另外,你可以通过etc/hadoop/hadoop-env.sh和etc/hadoop/yarn-env.sh设置特定站点的值来控制分布的bin目录中的hadoop脚本。

要配置hadoop集群,你需要配置Hadoop守护进程执行的环境和Hadoop守护进程的配置参数。

HDFS守护进程是:NameNode,SecondaryNode,DataNode。YARN守护进程是:ResourceManager,NodeManager,WebAppProxy

。如果用到了MapReduce,那么MapReduce Job History服务器也将会运行。对于大型安装,它们通常安装在单独的主机上。

配置Hadoop守护进程环境

管理员应该使用etc/hadoop/hadoop-env.sh和可选的etc/hadoop/mapred-env.sh和etc/hadoop/yarn-env.sh脚本对Hadoop守护进程环境进行特定站点的自定义。

至少,必须定义JAVA_HOME,以便在每个远程节点上正确定义它。

管理员可以使用下表中显示的配置选项配置各个守护进程:

Daemon Environment Variable
NameNode HADOOP_NAMENODE_OPTS
DataNode HADOOP_DATANODE_OPTS
Secondary NameNode HADOOP_SECONDARYNAMENODE_OPTS
ResourceManager YARN_RESOURCEMANAGER_OPTS
NodeManager YARN_NODEMANAGER_OPTS
WebAppProxy  YARN_PROXY_SERVER_OPTS
MapReduce Job History Server HADOOP_JOB_HISTORYSERVER_OPTS

例如,要将Namenode配置为使用parallelGC,应该在hadoop-env.sh中添加如下语句:

export HADOOP_NAMENODE_OPTS="-XX:+UseParallelGC"

查看etc/hadoop/hadoop-env.sh文件中的其它示例。

可以自定义的其它有用配置参数包括:

  • HADOOP_PID_DIR -- 存储守护进程进程ID文件的目录
  • HADOOP_LOG_DIR --存储守护进程日志文件的目录。如果日志文件不存在,则会自动创建.
  • HADOOP_HEAPSIZE / YARN_HEAPSIZE --要使用的堆大小,单位是MB.例如,如果此变量设置为1000,那么堆将设置为1000MB.这用于配置守护进程的堆大小.默认情况下,值是1000.如果你想为每一个守护进程单独配置值,你可以使用它.

大多情况下,应该指定HADOOP_PID_DIR和HADOOP_LOG_DIR目录,这样它们只能由运行hadoop守护进程的用户写入.否者可能会发生符号攻击.

在整个系统shell环境配置中配置HADOOP_PREFIX也是传统的做法.比如,目录profile.d中一个简单的脚本:

HADOOP_PREFIX=/path/to/hadoop

export HADOOP_PREFIX

Daemon Environment Variable
ResourceManager YARN_RESOURCEMANAGER_HEAPSIZE
NodeManager YARN_NODEMANAGER_HEAPSIZE
WebAppProxy YARN_PROXYSERVER_HEAPSIZE
Map Reduce Job History Server HADOOP_JOB_HISTORYSERVER_HEAPSIZE

配置Hadoop守护进程

本节处理在给定配置文件中要指定的重要参数:

  • etc/hadoop/core-site.xml
Parameter Value Notes
fs.defaultFS NameNode URI hdfs://host:port/
io.file.buffer.size 131072 序列文件中使用的读/写缓冲区大小
  • etc/hadoop/hdfs-site.xml
  • Configuration for NameNode:
Parameter Value Notes
dfs.namenode.name.dir NameNode持久存储名称空间和事务日志的本地文件系统上的路径 如果这是一个以逗号分隔的目录列表,那么那么将在所有目录中复制名称表,以实现冗余

dfs.hosts  /

dfs.hosts.exclude

允许/排除的数据节点列表 如果必要,使用这些文件控制允许的数据节点列表
dfs.blocksize 268435456 对于大型文件系统,HDFS块大小为256MB
dfs.namenode.handler 100 处理来自大量数据节点的PRCs的更多名称节点服务器线程数
  • Configurations  for DataNode:
Parameter Value Notes
dfs.datanode.data.dir 数据节点的本地文件系统上的以逗号分隔的路径列表,其中数据节点存储块 如果这是一个逗号分隔目录列表,那么数据将存储在所有命名分目录中,通常存储在不同的设备上
  • etc/hadoop/yarn-site.xml
  • Configurations for ResourceManager and NodeManager:
Parameter Value Notes
yarn.acl.enable

true /

false

enable ACLs?默认为false

yarn.admin.acl

Admin  在集群上设置管理员的ACL.ACLs的形式:逗号分隔的用户+空格+逗号分隔的组.默认为特殊值*,表示所有人;仅仅是特殊值空格表示任何人都没权限.
yarn.log-aggregation-enable false 启用或禁用日志聚合的配置
  • Configuration for ResourceManager:
Parameter Value Notes
yarn.resourcemanager.address

供用户提交作业的ResourceManager的host:port

如果设置了host:port,将会覆盖设置在yarn.resourcemanager.hostname中的主机名.
yarn.resourcemanager.scheduler.address 供应用程序管理器与调度程序进行对话以获取资源的ResourceManager的host:port 如果设置了host:port,将会覆盖设置在yarn.resourcemanager.hostname中的主机名.
yarn.resourcemanager.resource-tracker.address ResourceManager host:port for NodeManagers 如果设置了host:port,将会覆盖设置在yarn.resourcemanager.hostname中的主机名.
yarn.resourcemanager.admin.address 用于管理命令的ResourceManager的host:port 如果设置了host:port,将会覆盖设置在yarn.resourcemanager.hostname中的主机名.
yarn.resourcemanager.webapp.address ResourceManager web-ui host:port 如果设置了host:port,将会覆盖设置在yarn.resourcemanager.hostname中的主机名.
yarn.resourcemanager.hostname ResourceManager host host 可以替代所有yarn.resourcemanager*address资源的单一主机名.为ResourceManager组件生成默认端口.
yarn.resourcemanager.scheduler.class ResourceManager Scheduler class CapacityScheduler(推荐),FairScheduler(也推荐),或者FiFoScheduler.使用完全限定的类名,比如:org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler
yarn.scheduler.minimum-allocation-mb 在ResourceManager上分配给每个容器请求的最小内存限制 单位是MBs
yarn.scheduler.maximum-allocation-mb 在ResourceManager上分配给每个容器请求的最大内存限制 单位是MBs

yarn.resourcemanager.nodes.include-path /

yarn.resourcemanager.nodes.exclude-path

允许/排除的NodeManagers列表 如果有必要,使用这些文件控制允许的NodeManagers列表
  • Configurations for NodeManager:
Parameter Value Notes
yarn.nodemanager.resource.memory-mb

资源,即给定的NodeManager的可用物理内存,单位是MB

定义NodeManager上可供运行容器使用的总可用资源
yarn.nodemanager.vmem-pmem-ratio 任务的虚拟内存使用可能超出物理内存的最大比率 每个任务的虚拟内存使用率可能超出其物理内存限制的比率;NodeManager上的任务使用的虚拟内存总量超过其物理内存使用的比率.
yarn.nodemanager.local-dirs 写入中间数据的本地文件系统上以逗号分隔的路径列表。 多路径有助于扩展磁盘I/O.
yarn.nodemanager.log-dirs 写入日志的本地文件系统上以逗号分隔的路径列表。 多路径有助于扩展磁盘I/O
yarn.nodemanager.log.retain-seconds 10800 NodeManager上日志保留的默认时间(以秒为单位),仅当log-aggregation禁用时适用.
yarn.nodemanager.remote-app-log-dir /logs 应用程序完成时应用程序日志所要移动到的HDFS目录.需要设置适当的权限.仅当log-aggregation启用时适用.
yarn.nodemanager.remote-app-log-dir-suffix logs 附加到远程日志目录的后缀.日志将聚集到${yarn.nodemanager.remote-app-log-dir}/${user}/${thisParam},仅当log-aggregation启用时适用.
yarn.nodemanager.aux-services mapreduce_shuffle 需要为Map Reduce应用程序设置的洗牌服务
  • Configuration for History Server(需要移动到别处):
Parameter Value Notes
yarn.log-aggregation.retain-seconds

-1

在删除聚合日志之前保留多长时间.-1表示禁止.注意,此参数值设置太小将会向NameNode发送垃圾邮件.

yarn.log-aggregation.retain-check-internal-seconds

-1 检查聚合日志间隔之间的时间间隔.如果设置为零或负值,则该值将计算为聚合日志保留时间的十分之一.注意,此参数值设置太小将会向NameNode发送垃圾邮件.
  • etc/hadoop/mapred-site.xml
  • Configurations for MapReduce Applications:
Parameter Value Notes
mapreduce.framework.name

yarn

执行框架设置为YARN
mapreduce.map.memory.mb 1536 maps更大的资源限制.描述:调度程序为每个map任务请求的内存量.
mapreduce.map.java.opts -Xmx1024M  
mapreduce.reduce.memory.mb  3072 reduces更大的资源限制.描述:调度程序为每个reduce任务请求的内存量.
mapreduce.reduce.java.opts -Xm2560M  
mapreduce.task.io.sort.mb 512 有效排序数据时更大的内存限制.描述:排序文件时要使用的缓冲区内存总量,以兆字节为单位。默认情况下,为每个合并流提供1MB,这将使查找最小化。
mapreduce.task.io.sort.factor 100 排序文件时同时合并的更多的流.描述:排序文件时要同时合并的流的数目。这将确定打开的文件句柄数。
mapreduce.reduce.shuffle.parallelcopyies 50 用于从大量的maps获取输出后在reduces上运行的更多的并行副本数.描述:在复制(混排)阶段通过由reduce运行的默认并行传输数.
  • Configuration for MapReduce JobHistroy Server:
Parameter Value Notes
mapreduce.jobhistory.address MapReduce JobHistroy Server host:port 默认端口是10020.描述:MapReduce JobHistory Server IPC(进程间通信) host:port
mapreduce.jobhistory.webapp.address MapReduce JobHistory Server Web UI host:port 默认端口是19888.描述:MapReduce JobHistory Server Web UI host:port
mapreduce.jobhistory.intermediate-done-dir /mr-history/tmp MapReduce作业写入历史文件的目录.
mapreduce.jobhistory.done-dir /mr-history/done MR JobHistory 服务器管理历史文件的目录

Monitoring Health of NodeManagers

Hadoop提供了一种机制,管理员可以通过该机制将节点管理器配置为定期运行管理员提供的脚本,以确定节点是否正常。 

管理员可以通过在脚本中执行他们选择的任何检查来确定节点是否处于健康状态.如果脚本检测到节点处于不健康状态,它必须将一行以字符串ERROR开头的内容打印到标准输出中。NodeManager定期生成脚本并检查其输出。如果脚本的输出包含如上所述的字符串错误,则节点的状态报告为不健康,并且该节点被ResourceManager列入黑名单。不再向该节点分配任何其他任务。但是,NodeManager将继续运行该脚本,以便如果节点再次恢复健康,它将自动从ResourceManager上的黑名单节点中删除。节点的运行状况以及脚本的输出(如果不正常)可供管理员在ResourceManager Web界面中使用。自节点正常运行以来的时间也显示在Web界面上.

etc/hadoop/ yarns -site.xml中的下面的参数可以用来控制节点健康监控脚本.

Parameter Value Notes
yarn.nodemanager.health-checker.script.path Node health script 用于检查节点运行状况的脚本.
yarn.nodemanager.health-checker.script.opts Node health script options 用于检查节点运行状况的脚本选项.描述:传递给运行状况检查脚本的参数
yarn.nodemanager.health-checker.inter  Node health script interval 运行health script的时间间隔.描述:运行磁盘运行状况检查程序代码的频率.
yarn.nodemanager.health-checker.script.timeout-ms Node health script  timeout interval health script 执行超时.

如果只有部分本地磁盘损坏,则健康检查程序脚本不应该给出ERROR.NodeManager能够定期检查本地磁盘的运行状况(特别是检查nodemanager-local-dirs和nodemanager-log-dirs),并且在根据配置属性yarn.nodeManager.disk-health-checker.min-health-disks设置的值达到坏目录数阈值后,整个节点都被标记为不健康,而且这个信息也会发送给资源管理器。引导盘被攻击,或者健康检查程序脚本检测到引导盘中的故障.

Slaves File

在文件etc/hadoop/slaves中列出所有从属主机名或IP地址,每行一个.Helper scripts(如下所述)将使用etc/hadoop/slaves文件一次在很多主机上运行命令.它不用于任何基于Java的Hadoop配置.为了使用此功能,必须为用于运行Hadoop的帐户建立ssh信任(通过无密码ssh或其它方式,如Kerberos).

Hadoop Rack Awareness

许多Hadoop组建具有机架感知功能,并利用网络拓扑结构提高性能和安全性.Hadoop守护进程通过调用管理员配置的模块来获取集群中从机的机架信息.有关更多具体信息,请参阅 Rack Awareness文档.

强烈建议在启动HDFS之前配置机架感知.

Logging

Hadoop使用 Apache log4j.通过Apache Commons日志框架进行日志记录.编辑etc/hadoop/log4j.properties文件以自定义Hadoop守护进程的日志配置(日志格式等).

Operating the Hadoop Cluster(操作Hadoop集群)

完成所有必需的配置后,将文件分发到所有计算机上的HADOOP_CONF_DIR目录.这应该是所有机器上的同一个目录.一般来说,建议HDFS和YARN作为单独的用户来运行.在大多数安装中,HDFS进程作为hdfs执行,YARN通常使用yarn帐户.

Hadoop Starttup

要启动Hadoop集群,需要同时启动HDFS和YARN集群.

第一次打开HDFS时,必须要格式化.将一个新的分布式文件系统格式化为hdfs:

[hdfs]$ $HADOOP_PREFIX/bin/hdfs  namenode  -format  <cluster_name>

在指定的节点上使用下面命令启动HDFS NameNode 作为hdfs:

[hdfs]$ $HADOOP_PREFIX/sbin/hadoop-daemon.sh  --config  $HADOOP_CONF_DIR  --script  hdfs  start  namenode

在每个指定的节点上使用下面命令启动一个DataNode 作为hdfs:

[hdfs]$ $HADOOP_PREFIX/sbin/hadoop-daemon.sh  --confg  $HADOOP_CONF_DIR  --script  hdfs start  datanode

如果配置了etc/hadoop/slaves和ssh可信访问(参阅Single Node Setup),那么所有的HDFS进程都可以用一个实用脚本启动.作为hdfs:

[hdfs]$ $HADOOP_PREFIX/sbin/start-dfs.sh

用下面的命令启动YARN,运行在指定的ResourceManager上,作为yarn:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  start  resourcemanager

在每个指定的主机上运行一个脚本以启动一个NodeManager,作为 yarn:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  start  nodemanager

启动一个独立的WebAppProxy服务器.作为 yarn 运行在WebAppProxy上.如果多个服务器用来平衡负载,则应该在每个服务器运行它们:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  start  proxyserver

如果配置了etc/hadoop/slaves和ssh可信访问(参阅Single Node Setup),那么所有的YARN进程都可以用一个实用脚本启动.作为yarn:

[yarn]$ $HADOOP_YARN/HOME/sbin/start-yarn.sh

用下面的命令启动MapReduce JobHistory服务器,运行在指定的服务器上,作为 mapred:

[mapred]$ $HDOOP_PREFIX/sbin/mr-jobhistory-daemon.sh  --config  $HADOOP_CONF_DIR  start  historyserver

Hadoop Shutdown

使用以下命令停止NameNode,运行在指定的NameNode,作为 hdfs:

[hdfs]$ $HADOOP_PREFIX/sbin/hadopo-daemon.sh  --config  $HADOOP_CONF_DIR  --script  hdfs stop  namenode

运行一个脚本以停止一个DataNode,作为 hdfs:

[hdfs]$  $HADOOP_PREFIX/sbin/hadoop-daemon.sh  --config  $HADOOP_CONF_DIR  --script  hdfs  stop  datanode

如果配置了etc/hadoop/slaves和ssh可信访问(参阅 Single Node Setu),那么所有的HDFS进程都可以用一个实用的脚本来停止.作为 hdfs:

[hdfs]$ $HADOOP_PREFIX/sbin/stop-dfs.sh

用下面的命令停止ResourceManager,运行在指定的ResourceManager,作为 yarn:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  stop  resourcemanager

运行一个脚本以停止一个从属服务器器上NodeManager,作为 yarn:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  stop  nodemanager

如果配置了etc/hadoop/slaves和ssh可信访问(参阅 Single Node Setu),那么就可以使用实用脚本程序停止所有yarn进程.作为 hdfs:

[yarn]$ $HADOOP_YARN_HOME/sbin/stop-yarn.sh

停止WebAppProxy服务器.运行在WebAppProxy服务器上,作为 yarn. 如果多个服务器用于负载平衡,则应该在每个服务器上运行:

[yarn]$ $HADOOP_YARN_HOME/sbin/yarn-daemon.sh  --config  $HADOOP_CONF_DIR  stop  proxyserver

用下面的命令停止MapReduce JobHistory服务器,运行在指定的服务器上,作为 mapred:

[mapred]$ $HADOOP_PREFIX/sbin/mr-jobhistory-daemon.sh  --config  $HADOOP_CONF_DIR  stop  historyserver

Web Inerfaces

Hadoop集群启动并运行后,请按照以下说明检查组件的web-ui:

Daemon Web Interface Notes
NameNode http://nn_host:port/ Default HTTP port is 50070.
ResourceManager http://rm_host:port/ Default HTTP port is 8088.
MapReduce JobHistory Server http://jhs_host:port/ Default HTTP port is 19888

4. Commands Reference(命令参考)

Hadoop Commands Guide

概述

所有Hadoop命令都由bin/hadoop脚本调用.在没有任何参数的情况下运行hadoop脚本将打印所有命令的描述.

Usage: hadoop [--config confdir]  [--loglevel  loglevel]  [COMMAND]  [GENERIC_OPTIONS]  [COMMAND_OPTIONS]

FIELD  Description
--config  confdir 覆盖默认的配置目录.默认是${HADOOP_HOME}/conf
--loglevel  loglevel 覆盖日志级别.有效的日志级别包括:FATAL(致命),ERROR(错误),WARN(警告),INFO(信息),DEBUG(调试)和TRACE(跟踪).默认是INFO(信息)
GENERIC_OPTIONS 有多个命令支持的公共选项集合
COMMAND_OPTIONS 本文档中描述了Hadoop公共子项目的各种命令及其选项.HDFS和YARN包含在其它文档中.

GENERIC_OPTIONS

许多子命令使用一组通用的配置选项来改变其行为:

GENERIC_OPTION Description
-archives  <comma  separated  list of archives> 指定要在计算机上不归档的逗号分隔的存档.仅适用于job.
-conf  <configuration  file> 指定应用程序配置文件
-D <property>=<value> 对给定属性使用值
-files  <comma  separated  list  of  files> 指定要复制到map reduce 集群的逗号分隔的文件.仅适用于job.
-fs  <file:///> or <hdfs://namenode:port> 指定要使用的默认文件系统URL.会覆盖配置中的fs.defaultFS属性
-jt  <local>  or  <resourcemanager:port> 指定ResourceManager.仅适用于job.
-libjars  <comma  separated  list  of  jars> 指定要包含在classpath(类路径)中的逗号分隔的jar文件

Hadoop Common Commands

所有这些命令都是从hadoop  shell命令执行的.它们已经被分解成 User Commands 和 Administration Commands.

User Commands

对Hadoop集群用户有用的命令.

archive

创建hadoop 备份.更多信息请参阅 Hadoop Archives Guide.


checknative

Usage: hadoop  checknative  [-a]  [-h]

COMMAND_OPTION Description
-a 检查所有库是否可用
-h 打印帮助

此命令检查Hadoop本机代码的可用性.参阅Native Libaries 以获取更多信息.默认情况下,此命令只检查libhadoop的可用性.


classpath

Usage: hadoop  classpath  [--glob |--jar  <path> |-h  |--help]   

COMMAND_OPTION Description
--glob 扩展通配符
--jar  path 把路径作为清单写入名为 path  的jar中
-h, --help 打印帮助

打印获取Hadoop jar和所需库的类路径.如果不带参数调用,则打印由命令脚本设置的类路径,很有可能在类路径条目中包含通配符.其它选项在通配符扩展后打印类路径,或将类路径写入JAR文件的清单中。后者在不能使用通配符且扩展类路径超过支持的最大命令行长度的环境中很有用。


credential

Usage: hadoop  credential  <subcommand>  [options]

COMMAND_OPTION Description
create  alias [-provider  provider-path]  [-strict]  [-value  credential-value] 提示用户将凭据存储为给定别名.除非指定-provider,否则将使用core-site.xml文件中的hadoop.security.credential.provider.path .如果provider使用默认密码,-strict标志将导致命令失败.使用-value标志提供凭证值(也就是别名密码),而不是提示. 
delete  alias  [-provider  provider-path]  [-strict]  [-f] 使用提供的别名删除凭据.除非指定-provider,否则将使用core-site.xml文件中的hadoop.security.credential.provider.path .如果provider使用默认密码,-strict标志将导致命令失败. 除非指定了-f, 否则命令要求确认.
list  [-provider  provider-path]  [-strict] 列出特定provider包含的所有credential别名,比如core-site.xml中配置的或通过-provider参数指定的.如果provider使用默认密码,-strict标志将导致命令失败.

用于管理凭据provider中的凭据, 密码和机密的命令.

 

Hadoop中的CredentialProvider允许分离应用程序以及它们如何存储所需密码/机密.为了指示特定的provider类型和位置,用户必须在core-site.xml中提供hadoop.security.credential.provider.path配置元素, 或者在以下每个命令上使用命令行选项 -provider.  provider path是一个逗号分隔的URLs列表,这些URLs指示应咨询的provider的列表的类型和位置. 例如,以下路径:

user:///,jceks://file/tmp/test.jceks,jceks://[email protected]/my/path/test.jceks

表示应该通过User Provider 咨询当前用户的凭据; 位于/tmp/test.jceks的本地文件是Java Keystore Provider;  位于HDFS中的nn1.example.com/my/path/test.jceks也是Java Keystore Provider的存储区.

当使用credential命令式,它通常用于向特定凭证存储提供程序提供密码或秘密. 为了指示使用哪个提供程序存储,应该使用-provider选项. 否则,给定多个提供程序的路径,将使用第一个非瞬时提供程序. 这可能是你想要的,也可能不是.

提供程序经常要求提供密码或其它机密.如果提供程序需要一个密码,但找到密码,它将使用默认密码,并发出警告信息,表明正在使用默认密码. 如果提供了-strict标志,警告信息将变为错误信息,命令将立即返回错误状态.

示例:  hadoop  credential  list  --provider  jceks://file/tmp/test.jceks


distcp

Usage: hadoop  distcp  <srcurl>  <desturl>  

递归地复制文件或目录. 有关更多信息,请参阅 Hadoop DistCp Guide.


fs

Usage: hadoop  fs  [generic  options]

运行通用文件系统用户客户端. 此命令记录在 File System Shell Guide. 当使用HDFS时, 它是hdfs  dfs 的同义词.


jar

Usage: hadoop  jar  <jar>  [mainClass]  args...

运行一个jar文件.

注意:使用yarn  jar 来启动YARN应用程序.


key

Usage: hadoop  key  <subcommand>  [options]

COMMAND_OPTIONS Description
create  keyname  [-cipher cipher ]  [-size  size]  [-description  description]  [-attr  attribute=value] [-provider  provider]  [-strict]  [-help]  在-provider参数指定的提供程序中,为keyname参数指定的名称创建一个新密钥. 如果提供程序使用默认密码,-strict标志将导致命令失败。可以使用-cipher参数指定密码。默认密码当前为“AES/CTR/NoPadding”. 默认键大小是128. 可以用-size参数指定请求的密钥长度. 使用-attr参数可以指定任意attribute=value类型的属性.   -attr可以指定多次,每次一个属性.
roll  keyname  [-provider  provider]  [-strict]  [-help] 在使用-provider参数指示的提供程序中为指定密钥创建新版本. 如果提供程序使用默认密码,-strict标志将导致命令失败。
delete keyname  [-provider  provider]  [-strict]  [-f]  [-help] 在使用-provider参数指示的提供程序中删除keyname参数指定的密钥所有版本. 如果提供程序使用默认密码,-strict标志将导致命令失败。 该命令要求用户确认,除非指定了-f。
list  [-provider  provider]  [-strict]  [-metadata]  [-help] 显示正如core-site.xml配置的或由-provider参数指定的特定提供程序所包含的密钥名. 如果提供程序使用默认密码,-strict标志将导致命令失败。-metadata选项显示元数据
-help 打印此命令的用法

通过密钥提供程序(KeyProvider)管理密钥. 有关密钥提供程序详情,请参阅 Transparent Encryption Guide.

提供程序经常要求提供密钥或其它机密. 如果提供程序需要密码,但找不到密码,它将使用默认密码并发出警告消息,说明正在使用默认密码。如果提供了-strict标志,则警告消息将变为错误消息,命令将立即返回错误状态。

注意: 某些 KeyProvider(比如:org.apache.hadoop.crypto.key.JavaKeyStoreProvider)不支持大写密钥名.

注意: 某些密钥提供程序不直接执行密钥删除(例如, 执行软删除,或延迟实际删除,以防出错). 在这种情况下,在删除密钥之后创建/删除具有相同名称的密钥时可能会遇到错误.  请检查基础密钥提供程序以获得详细信息。


trace

查看和修改Hadoop tracing 设置. 请参阅Tracing Guide.


version

Usage: hadoop version


CLASSNAME

Usage: hadoop  CLASSNAME

运行名为CLASSNAME的类.


envvars

Usage: hadoop  envvars

显示计算的Hadoop环境变量

 

Administration Commands

对Hadoop集群管理员有用的命令.

daemonlog

Usage: 

COMMAND_OPTION Description
-getlevel  host:port  classname  [-protocol  (http|https)] 打印在host:port  运行的守护进程中由限定类名(classname)标识的日志等级. -protocol标志指定用于连接的协议
-setlevel  host:port  classname  level  [-protocol  (http|https)] 设置在host:port  运行的守护进程中由限定类名(classname)标识的日志等级. -protocol标志指定用于连接的协议

动态获取/设置守护进程中由限定类名标识的日志的日志级别. 默认情况下,该命令发送一个HTTP请求,但可以通过使用参数-protocol https发送一个HTTPS请求来覆盖它。

例如:

$  bin/hadoop  daemonlog  -setlevel  127.0.0.1:50070  org.apache.hadoop.hdfs.server.namenode.NameNode  

$  bin/hadoop  daemonlog  -getlevel  127.0.0.1:50074  org.apache.hadoop.hdfs.server.namenode.NameNode DEBUG -protocol  https

  注意: 改设置不是永久性的, 重启守护进程时会重置. 此命令通过向守护进程内部的Jetty servlet发送HTTP/HTTPS请求来工作,因此它支持以下守护进程:

  • HDFS
    • name node
    • secondary name node
    • data node
    • journal node
  • YARN
    • resource manager
    • node manager
    • Timeline server

但是,该命令不支持KMS服务器,因为它的Web界面基于不支持servlet的Tomcat.

5. FileSystem Shell

Overview

文件系统(FS)shell包含各种类似shell的命令,这些命令直接和分布式文件系统(HDFS)以及Hadoop支持的其它文件系统交互,例如,Local FS, HFTP FS, S3和其它。FS shell 这样来调用:

bin/hadoop  fs <args>

所有FS shell都把path URIs当作参数。URI格式是:scheme://authority/path. 对于HDFS来说,scheme是hdfs;对于Local FS来说,scheme是file。scheme和authority是可选的,如果未指定,则使用配置中指定的默认scheme。HDFS文件或目录,如/parent/child,可以指定为hdfs://namenodehost/parent/child或仅指定为/parent/child(假定你的配置设置为指向hdfs://namenodehost)。

FS shell中大多数命令的行为类似于相应的Unix命令。每个命令都描述了不同之处。错误信息发送到stderr,输出发送到stdout.

如果使用了HDFS,则hdfs dfs是同义词。

可以使用相对路径。对于HDFS来说,当前工作目录是HDFS主目录/user/<username>,此目录通常需要手动创建。HDFS也可以隐式访问,例如,当使用HDFS垃圾文件夹时, .Trash文件夹就在主目录中.

参阅 Commands Manual知晓通用shell选项.

appendToFile

Usage:  hadoop  fs  -appendToFile  <localfile>  ...   <dst>

将单个或多个src从本地文件系统追加到目标文件系统. 还从stdin读取输入并追加到目标文件系统.

  • hadoop  fs  -appendToFile  localfile  /user/hadoop/hadoopfile
  • hadoop  fs  -appendToFile  localfile1  localfile2  /user/hadoop/hadoopfile
  • hadoop  fs  -appendToFile  localfile  hdfs://nn.example.com/hadoop/hadoopfile     注意:会在HDFS根目录下创建新文件夹hadoop
  • hadoop  fs  -appendToFile  -  hdfs//nn.example.com/hadoop/hadoopfile    从stdin读取输入

退出代码:

成功时返回0,失败时返回1.

cat

Usage: hadoop  fs  -cat  [-ignoreCrc]  URI  [URI ...]

将源路径复制到stdout.

Options

  • -ignoreCrc选项表示禁用校验和验证.

示例:

  • hadoop  fs  -cat  hdfs://nn1.example.com/file1   hdfs://nn2.example.com/file2
  • hadoop  fs  -cat  file:///file3    /user/hadoop/file4

checksum

Usage: hadoop  fs  -checksum  URI 

返回一个文件的校验和信息.

示例:

  • hadoop  fs  -checksum  hdfs://nn1.example.com/file1
  • hadoop  fs  -checksum  file:///etc/hosts

chgrp

Usage: hadoop  fs  -chgrp  [-R]  GROUP  URI  [URI  ...]

改变文件的组关联. 用户必须是文件属主(所有者),否则必须是超级用户. 其它信息在 Permissions Guide.

Options

  • -R选项将通过目录结构递归地进行更改。

chmod

Usage: hadoop  fs  -chmod  [-R]  <MODE[,MODE]...  |  OCTALMODE>  URI  [URI ...]

更改文件权限。使用-R,通过目录结构递归地进行更改。用户必须是文件属主(所有者),否则必须是超级用户. 其它信息在 Permissions Guide.

选项:

  • -R选项将通过目录结构递归地进行更改。

chown

Usage: hadoop  fs  -chown  [-R]  [OWNER][:[GROUP]]  URI  [URI  ...]

改变文件所有者。用户必须是文件属主(所有者),否则必须是超级用户. 其它信息在 Permissions Guide.

Options

  • -R选项将通过目录结构递归地进行更改。

copyFromLocal

Usage: hadoop  fs  -copyFromLocal   <localsrc>  URI

类似于fs  -put 命令,只是源文件仅限于本地文件引用。

选项:

  • -p: 保留访问和修改时间,所有权和权限。(假设权限可以通过文件系统传播)
  • -f: 如果目标已存在就覆盖目标
  • -l: 允许DataNode将文件延迟保存到磁盘,强制复制因子为1。此标志将导致耐久性降低。小心使用。
  • -d: 跳过创建后缀为._COPYING_的临时文件。

copyToLocal

Usage: hadoop  fs  -copyToLocal   [-ignoreCrc]  [-crc]  URI  [localdst]

类似于get命令,只是目标仅限于本地文件引用。

count

Usage: hadoop fs  -count  [-q]  [-h]  [-v]  [-x]  [-t  [<storage type>]]  [-u]  <paths>

计算与指定文件模式匹配的路径下的目录、文件和字节数。获取配额和使用情况。此命令的各输出列分别是:目录数、文件数、内容大小和路径名。

-u和-q选项控制输出列包含哪些列。其中,-q表示显示配额;-u限制输出仅显示配额和使用情况。

使用-count  -q的输出列为:配额、剩余配额、空间配额、剩余空间配额、目录计数、文件计数、内容大小、路径名。

使用-count  -u的输出列为:配额、剩余配额、空间配额、剩余空间配额、路径名。

-t选项显示每种存储类型的配额和使用情况。如果为给定-u或-q选项,则忽略-t选项。可以在-t选项中使用的可能参数列表(不区分大小写,参数""除外):"", "all", "ram_disk", "ssd", "disk", "archive"。

-h选项以认类可读格式显示大小。

-v选项显示标题行。

-x选项从计算结果中排除快照。如果没有-x选项(默认),则始终从所有INodes,包括给定路径下的所有快照计算结果。如果给定了-u或-q选项,则忽略-x选项。

示例:

  • hadoop  fs  -count  hdfs://nn1.example.com/file1   hdfs://nn2.example.com/file2
  • hadoop  fs  -count  -q  hdfs://nn1.example.com/file1
  • hadoop  fs  -count  -q  -h  hdfs://nn1.example.com/file1
  • hadoop  fs  -count  -q  -h  -v  hdfs://nn1.example.com/file1
  • hadoop  fs  -count  -u  hdfs://nn1.example.com/file1
  • hadoop  fs  -count  -u  -h  hdfs://nn1.example.com/file1
  • hadoop  fs  -count  -u  -h  -v  hdfs://nn1.example.com/file1

退出代码:

成功返回0,错误返回-1。

cp

Usage: hadoop  fs  -cp  [-f]  [-p  |  -p[topax]]  URI  [URI  ...]   <dest>

将文件从源复制到目标。此命令也允许多个源,在这种情况下,目标必须是一个文件夹。

如果(1)源文件系统和目标文件系统支持“raw.*”命名空间扩展属性(仅限HDFS),以及所有源路径名和目标路径名都在/.reserved/raw层级结构中,则保留这些属性。

选项:

  • -f选项将覆盖目标如果目标已经存在
  • -p选项将保留文件属性[topx](时间戳、所有权、权限、ACL、XAttr)。如果指定了-p而没有参数,则保留时间戳、所有权和权限。如果指定了-pa,那么还会保留权限,因为ACL是超级权限集。确定是否保留原始命名空间扩展属性独立于-p标志。

示例:

  • hadoop  fs  -cp  /user/hadoop/file1   /user/hadoop/file2
  • hadoop  fs  -cp  /user/hadoop/file1   /user/hadoop/file2   /user/hadoop/dir

退出代码:

成功返回0,错误返回-1.

createSnapshot

参阅HDFS Snapshots Guide

deleteSnapshot

参阅HDFS Snapshots Guide

df

Usage: hadoop  fs  -df  [-h]  URI   [URI ...]

显示可用空间。

选项:

  • -h选项将把以人类可读的方式格式化文件大小。(例如,是64M而不是67108864)

示例:

  • hadoop  fs  -df  /user/hadoop/dir1

du

 Usage: hadoop  fs  -du  [-s]  [-h]  [-x]  URI  [URI  ...]

显示给定目录中包含的文件和目录的大小,如果只是文件,则显示文件的长度。

选项:

  • -s选项将导致显示文件长度的汇总,而不是显示单个文件的长度。如果没有-s选项,计算是通过从给定路径进入1层深度来完成的。
  • -h选项将把以人类可读的方式格式化文件大小。(例如,是64M而不是67108864)
  • -x选项将从结果计算中排除快照。如果没有-x选项(默认),结果始终从所有的INodes,包括给定路径下的所有快照计算结果。

du命令返回如下格式三列内容:

size   disk_space_consumed_with_all_replicas    full_path_name

示例:

  • hadoop  fs  -du  /user/hadoop/dir1   /user/hadoop/file1   hdfs://nn1.example.com/user/hadoop/dir1

退出码:成功返回0,错误返回-1。

dus

Usage: hadoop  fs  -dus  <args>

显示文件长度的汇总。

注意:此命令已弃用,而是使用  hadoop  fs  -du   -s

expunge

Usage: hadoop  fs  -expunge

从垃圾目录永久删除早于保留阈值的检查点中的文件,并创建新的检查点。

创建检查点时,垃圾桶中最近删除的文件将移动到检查点下。在下次调用-expunge命令时,将永久删除超出fs.trash.interval的检查点中的文件。

如果文件系统支持该功能,则用户可以配置为通过存储在fs.trash.checkpoint.interval(在core-site.xml中)的参数定期地创建和删除检查点。此值应小于等于fs.trash.interval。

有关HDFS的垃圾特性的更多信息,请参阅HDFS Architecture guide

find

Usage: hadoop  fs  -find  <path>  ...  <expression>  ...

查找与指定表达式匹配的所有文件,并把所选操作应用于这些文件。如果没有指定路径,则默认为当前工作目录。如果没有表达式,则默认为-print。

可识别以下主要表达式:

  • -name   pattern
  • -iname   pattern

             如果文件的基名与使用标准文件系统通配符模式匹配,则计算为true。如果使用-iname,则匹配不区分大小写。

  • -print
  • -print0

总是计算为true。使当前路径名写入到标准输出。如果使用-print0表达式,则会附加一个ASCII空字符。

可以使用下面的操作符:

  • expression  -a   expression
  • expression  -and   expression
  • expression   expression

用于连接两个表达式的逻辑与运算符。如果两个子表达式都返回true则整体返回true。由两个表达式并置所决定,因此不需要显式指定。如果第一个表达式失败,则不应用第二个表达式。

示例:

hadoop  fs  -find  /  -name  test  -print

退出码:成功返回0,错误返回-1。

get

Usage: hadoop  fs  -get   [-ignoreCrc]  [-crc]  [-p]  [-f]   <src>  ...  <localdest>

复制文件到本地文件系统。CRC检查失败的文件可以使用-ignoreCrc选项进行复制。使用-crc选项时文件和CRCs都会被复制。

示例:

  • hadoop  fs  -get   /user/hadoop/file    localfile
  • hadoop  fs  -get  hdfs://nn.example.com/user/hadoop/file   localfile

退出码:成功返回0,错误返回-1。

选项:

  • -p: 保留访问时间和修改时间,所有权和权限。(假设权限可有跨文件系统传播)
  • -f: 如果目标文件存在就覆盖
  • -ignoreCrc: 跳过对下载文件的CRC检查
  • -crc: 为下载的文件写入CRC校验和。

getfacl

Usage: hadoop  fs   -getfacl  [-R]   <path>

显示文件和目录的权限控制列表。如果一个目录有一个默认的ACL,那么getfacl也会显示默认的acl。

选项:

  • -R:递归地列出所有文件和目录的ACLs.
  • path: 要列出的文件或目录。

示例:

  • hadoop  fs   -getfacl  /file
  • hadoop  fs   -getfacl  -R   /file

退出码:成功返回0,错误返回-1。

getfattr

Usage: hadoop  fs  -getfattr  [-R]  -n  name   |   -d  [-e  en]  <path>

显示文件或目录的扩展属性名称和值(如果有)。

选项:

  • -R:递归地列出所有文件和目录的属性。
  • -n  name:Dump命令的扩展属性值。
  • -d:Dump与路径名关联的所有扩展属性值
  • -e  encoding :检索值后对其进行编码。有效编码为:"text"、"hex" 和 "base64"。编码为文本字符串的值用双引号(“)括起来,编码为十六进制和base64的值分别以0x和0s作为前缀。
  • path: 文件或目录。

示例:

  • hadoop  fs  -getfattr  -d   /file
  • hadop    fs  -getfattr  -R  -n  user.myAttr    /dir

退出码:成功返回0,错误返回-1。

getmerge

Usage: hadoop  fs  -getmerge   [-nl]   <src>  <localdst>

将源目录和目标文件作为输入,并将src中的文件连接到目标本地文件中。可以设置可选项-nl以允许在每个文件的末尾换行符(LF)。

-skip-empty-file可用于在文件为空时避免不必要的换行符。

示例:

  • hadoop  fs   -getmerge  -nl  /src  /opt/output.txt
  • hadoop  fs  -getmerge   -nl  /src/file1.txt  /src/file2.txt  /output.txt

退出码:成功返回0,错误返回-1。

help

Usage: hadoop  fs  -help

返回使用输出。

ls

Usage: hadoop  fs  -ls  [-C]  [-d]  [-h]  [-q]  [-R]  [-t]  [-S]  [-r]  [-u]  <args>

选项:

  • -C:仅显示文件和目录的路径
  • -d:  目录以普通文件列出。
  • -h: 以人类可读的方式格式化文件大小(例如,是64.0M而不是67108864)。
  • -q: 打印?,而不是不可打印字符。
  • -R:递归地列出遇到的子目录
  • -t: 根据修改时间(最近一次)对输出进行排序。
  • -S:根据文件大小对输出排序。
  • -r:颠倒排序顺序。
  • -u:使用访问时间而不是修改来显示和排序。

对于文件,ls返回文件的stat,格式如下:

权限  副本数  用户ID  组ID  文件大小  修改日期  修改时间  文件名

就像在Unix中一样,对于一个文件夹,它返回其直接子目录的列表。目录如下:

权限  用户ID  组ID  修改日期  修改时间  文件名

默认情况下,目录中的文件按文件名排序。

示例:

  • hadoop  fs  -ls  /user/hadoop/file1

退出码:成功返回0,错误返回-1。

lsr

Usage: hadoop  fs  -lsr  <args>

ls的递归版。

注意:此命令已经弃用,使用hadoop  fs  -ls  -R替代。

mkdir

Usage: hadoop  fs  -mkdir  [-p]  <paths>

把路径uri作为参数并创建目录。

选项:

  • -p选项的行为非常类似于Unix  mkdir  -p,它沿着路径创建父目录。

示例:

  • hadoop  fs  -mkdir  /user/hadoop/dir1    /user/hadoop/dir2
  • hadoop  fs  -mkdir  hdfs://nn1.example.com/user/hadoop/dir   hdfs://nn2.example.com/user/hadoop/dir

退出码:成功返回0,错误返回-1。

moveFromLocal

Usage: hadoop  fs  -moveFromLocal  <localsrc>  <dst>

类似于put命令,只是源localsrc在复制后回被删除。

moveToLocal

Usage: hadoop  fs  -moveToLocal  [-crc]  <src>  <dst>

显示一个‘尚未实现’的消息。

mv

Usage:hadoop  fs  -mv  URI  [URI ...]  <dst>

把文件从源移动到目标。此命令也允许多个源,在这种情况下,目标需要是一个目录。不允许跨文件系统移动文件。

示例:

  • hadoop  fs  -mv  /user/hadoop/file1    /user/hadoop/file2
  • hadoop  fs  -mv  hdfs://nn.example.com/file1  hdfs://nn.example.com/file2  hdfs://nn.example.com/file3  hdfs://nn.example.com/dir1

退出码:成功返回0,错误返回-1。

put

Usage: hadoop  fs  -put  [-f]  [-p]  [-l]  [-d]  [ - | <localsrc> ...]  <dst>

从本地文件系统复制单src或多src到目标文件系统。如果源设置为“-”,则从stdin读取输入并写入目标文件系统。如果文件已经存在则复制失败,除非使用-f标识。

选项:

  • -p:保留访问和修改时间,所有权和权限。(假设权限可以跨文件系统传播)
  • -f:如果目标存在就覆盖。
  • -l:允许数据节点延迟地将文件保存到磁盘,强制复制系数为1。此标志将导致耐久性降低。小心使用。
  • -d:跳过以._COPYING_为后缀的临时文件夹的创建。

示例:

  • hadoop  fs  -put  localfile   /user/hadoop/hadoopfile
  • hadoop  fs  -put  localfile1  localfile2  -f  /user/hadoop/hadoopdir

退出码:成功返回0,错误返回-1。

renameSnapshot

参阅HDFS Snapshots Guide

rm

Usage: hadoop  fs  -rm  [-f]  [-r  |  -R]  [-skipTrash]  [-safely]  URI  [URI ...]

删除参数指定的文件。

如果提供了垃圾桶,则文件系统会把删除的文件移动到垃圾桶文件夹中( 由 FileSystem#getTrashRoot提供)。当前,默认情况下禁用垃圾桶。用户可以通过为core-site.xml中的fs.trash.interval参数设置大于零的值来启用垃圾桶。

请参阅删除有关删除垃圾桶中文件的说明。

选项:

  • -f:如果文件不存在,-f选项将不显示诊断消息或修改退出状态以反映错误。
  • -R:递归地删除目录以及它之下任何内容。
  • -r:等同于-R。
  • -skipTrash:此选项将绕过垃圾桶(如果启用的话),并立即删除指定的文件。当需要从超配额的目录中删除文件时,这一点非常有用。
  • -safely:在删除文件总数大于hadoop.shell.delete.limit.files(在core-site.xml中,默认值为100)的目录之前,此选项需要进行安全确认。它可以和-skipTrash一起使用以防止意外删除大文件。在确认之前递归遍历大目录以计算要删除的文件数量时,预期会出现延迟。

示例:

  • hadoop  fs  -rm  hdfs://nn.example.com/file  /user/hadoop/emptydir

退出码:成功返回0,错误返回-1。

rmdir

Usage: hadoop  fs  -rmdir  [--ignore-fail-on-non-empty]  URI  [URI ...]

删除一个文件夹。

选项:

  • --ignore-fail-on-non-empty:使用通配符时,如果目录仍然包含文件,也不会失败。

示例:

  • hadoop  fs  -rmdir  /user/hadoop/emptydir

rmr

Usage: hadoop  fs  -rmr  [-skipTrash]  [URI]  [URI ...]

删除的递归版。

注意:此命令已弃用,请使用hadoop  fs  -rm  -r  替代。

setfacl

Usage :hadoop  fs  -setfacl  [-R]  [-b  |-k  -m  |-x  <acl_spec>  <path>]  |[--set  <acl_spec>  <path>]

设置文件和目录的访问控制列表。

选项:

  • -b:除去基本ACL项之外的所有项。为了与权限位兼容,用户、组和其他用户的条目被保留。
  • -k:删除默认的ACL。
  • -R:递归地对所有文件和目录应用操作。
  • -m:修改ACL。新条目将添加到ACL,现有条目将保留。
  • -x:删除指定的ACL项,保留其它ACL项。
  • --set:完全替换ACL,丢弃所有现有条目。为了与权限位兼容,acl_spec 必须包括user项、group项和other项。
  • acl_spec :逗号分隔的ACL项列表。
  • path :要修改的文件或目录。

示例:

  • hadoop  fs  -setfacl  -m  user:hadoop:rw-  /file
  • hadoop  fs  -setfacl  -x   user:hadoop  /file
  • hadoop  fs  -setfacl  -b   /file
  • hadoop  fs  -setfacl  -k   /dir
  • hadoop  fs  -setfacl  --set user::rw-,user:hadoop:rw-,group::r--,other::r--  file/
  • hadoop  fs  -setfacl  -R  -m  user:hadoop:r-x  /dir
  • hadoop  fs  -setfacl  -m  defautl:user:hadoop:r-x  /dir

退出码:成功返回0,错误返回-1。

setfattr

Usage: hadoop  fs  -setfattr  -n  name  [-v  value]  |  -x  name  <path>

设置文件或目录的扩展属性名或值。

选项:

  • -n  name :扩展属性名。
    • -v  value :扩展属性值。值有三种不同的编码方法。如果参数用双引号括起来,则值是引号内的字符串;如果参数以0x或0X为前缀,则将其视为十六进制数;如果参数以0s或0S开头,则将其视为base64编码。
    • -x  name:删除扩展属性。
    • path :文件或目录

示例:

  • hadoop  fs  -setfattr  -n  user.myAttr  -v  myValue  /file
  • hadoop  fs  -setfattr  -n  user.noValue  /file
  • hadoop  fs  -setfattr  -x  user.myAttr  /file

退出码:成功返回0,错误返回-1。

setrep

Usage: hadoop  fs  -setrep  [-R]  [-w]  <numReplicas>  <path>

更改文件的复制因子。如果 path 是个文件夹,那么该命令将递归地更改以 path 为根目录树下所有文件的复制因子。

选项:

  • -w:    -w标志请求命令等待复制完成。这可能需要很长时间。
  • -R:    为了向后兼容,可以接受-R标志。它没有任何效果。

示例:

  • hadoop  fs   -setrep  -w  3  /user/hadoop/dir

退出码:成功返回0,错误返回-1。

stat

Usage: hadoop  fs   -stat   [format]   <path>  ...

以指定格式打印在path处文件/目录的统计信息。格式接受以八进制(%a)和符号(%A)表示的权限,以字节(%b)表示的文件大小,类型(%F),所有者组名(%g),文件/目录名(%n),块大小(%o),复制因子(%r),访问日期(%x,%X),修改日期(%y,%Y)。%x和%y将UTC日期显示为"yyyy-MM-dd HH:mm:ss",而%X和%Y显示自UTC1970年1月1日起的毫秒数。如果格式没有指定,默认使用%y。

示例:

  • hadoop  fs  -stat  "type:%F  perm:%a  %u:%g  size:%b  mtime:%y  atime:%x  name:%n"  /file

退出码:成功返回0,错误返回-1。

tail

Usage: hadoop  fs  -tail  [-f]  URI

把文件的最后一千字节显示到stdout上。

选项:

  • -f:-f选项将在文件增长时输出附加的数据,就像在Unix中一样.

示例:

  • hadoop  fs  -tail  pathname

退出码:成功返回0,错误返回-1。

test

Usage: hadoop  fs  -test  [-defsz]  URI

选项:

  • -d: 如果路径是一个文件夹则返回0.
  • -e: 如果路径存在则返回0.
  • -f: 如果路径是一个文件则返回0.
  • -s: 如果路径非空则返回0.
  • -r: 如果路径存在并且授予读取权限,则返回0.
  • -w:如果路径存在并且授予写入权限,则返回0.
  • -z:如果文件零长度,则返回0.

示例:

  • hadoop  fs  -test  -e  filename

text

Usage: hadoop  fs  -text  <src>

获取源文件并以文本格式输出该文件。允许的格式是zip和TextRecordInputStream。

touchz

Usage: hadoop  fs  -touchz  URI  [URI ...]

创建零长度文件。如果文件以非零长度存在,则返回错误。

示例:

  • hadoop  fs  -touchz  pathname

退出码:成功返回0,错误返回-1。

truncate

Usage: hadoop  fs  -truncate  [-w]  <length>  <paths>

将所有匹配指定文件模式的文件截断到指定长度。

选项:

  • -w: 如果需要的话,-w标志请求命令等待块恢复完成。如果没有-w标志,文件在恢复过程中可能会保持一段时间未关闭。在此期间,无法重新打开文件进行追加。

示例:

  • hadoop  fs  -truncate  55  /user/hadoop/file1  /user/hadoop/file2
  • hadoop  fs  -truncate  -w  127  hdfs://nn1.example.com/user/hadoop/file1

usage

Usage: hadoop  fs  -usage  command

返回单个命令的帮助。

Working with Object Storage(与对象存储一同工作)

Hadoop文件系统shell与对象存储(如Amzon S3、Azure WASB和OpenStack Swit)一起工作。

?????????????????????????????????????????

?????????????????????????????????????????

?????????????????????????????????????????

?????????????????????????????????????????

?????????????????????????????????????????

?????????????????????????????????????????

6. Hadoop Compatibility

Apache Hadoop Compatibility(Apache Hadoop 兼容性)

Purpose(目的)

本文件记录了Apache Hadoop项目的兼容性目标。列举了影响Hadoop开发人员、下游项目和最终用户的Hadoop版本之间的不同类型的兼容性。对于每种类型的兼容性,我们:

  • 描述对下游项目或最终项目的影响。
  • 在合适的情况下,当允许不兼容的更改时,调用开发人员所采用的策略。

兼容性类型

Java API

将Hadoop接口和类注释以描述预期的设备和稳定性,以便保持与先前版本的兼容性。详见 Hadoop Interface Classification

  • InterfaceAudience(接口设备):记录预期的设备,可能的值为:Public(对于最终用户和外部项目)、LimitedPrivate(对于其它Hadoop组件,以及紧密相关的项目,比如,YARN, MapReduce, HBase等等)、Private(对于内部组件使用)。
  • InterfaceStability(接口稳定性):描述允许哪些类型的接口更改。可能的值为:Stable、Evolving、Unstable和Deprecated。

用例(Use Cases)

  • 需要Public-Stable API兼容性,以确保最终用户程序和下游项目在不进行修改的情况下继续工作。
  • 需要LimitedPrivate-Stable API兼容性,以允许跨次版本升级单个组件。
  • 需要Private-Stable API兼容性来滚动升级。

Policy

  • 在主版本中删除Public-Stable APIs之前,必须至少在一个主版本弃用Public-Stable APIs。
  • LimitedPravite-Stable APIs可以跨主版本更改,但不能在同一个主版本内更改。
  • Pravite-Stable APIs可以跨主版本更改,但不能在同一个主版本内更改。
  • 没有注释的类是隐式的“Private”。没有注释的类成员继承封闭类的注释。

注意:由proto文件生成的APIs需要与滚动升级兼容。有关更多细节,请参见关于wire-compatibility的一节。APIs的兼容性策略和wire-communication需要携手解决这个问题。


Semantic compatibility(语义兼容)

Apache Hadoop 致力于确保APIs的行为在版本上保持一致,尽管对正确性的更改可能导致行为的更改。Tests和javadocs指定API的行为。社区正在更严格的指定一些APIs,并加强测试套件以验证是否符合规范,从而有效的为易于测试的行为子集创建正式的规范。

policy

API的行为可能会被更改以修正不正确的行为,这样的更改将伴随着更新现有bug的测试,或者在更改之前没有测试的情况下添加测试。


Wire compitibility

连接兼容性涉及Hadoop进程之间通过连接传输的数据。Hadoop使用协议缓冲区( Protocol Buffers)进行大多数RPC通信。保持兼容性需要禁止如下所述的修改。还应该考虑非rpc通信,例如,使用HTTP传输 HDFS镜像 作为 快照或传输MapTask输出 的一部分。潜在的通信可以分为以下几类:

  • Client-Server:  Hadoop客户机到服务器之间的通信(例如,HDFS客户机到NameNode协议,或者YARN客户机到ResourceManager协议)。
  • Client-Server(Admin):  值得区分仅由管理命令(例如HAAdmin协议)使用的客户机-服务器协议的一个子集,因为这些协议只影响那些能够容忍终端用户(使用通用客户机-服务器协议)所不能容忍的更改的 管理员。
  • Server-Server: 服务器之间的通信(例如,DataNode和NameNode之间或和NodeManager和ResourceManager之间的协议)。

用例

  • 即使在将服务器(集群)升级到更高版本(反之亦然)之后,仍需要Client-Server(客户机-服务器)兼容性才能允许用户继续使用旧客户机。例如,Hadoop 2.1.0客户机与Hadoop 2.3.0集群通信。
  • 在升级服务器(集群)之前,也需要Client-Server(客户机-服务器)兼容性,以允许用户升级客户机。例如,Hadoop 2.4.0客户机与Hadoop 2.3.0集群通信。这允许在完全集群升级前部署客户端错误修复程序。注意:新客户机APIs或shell命令调用的新集群功能将不可用。尝试使用尚未部署到集群的新APIs(包括数据结构中的新字段)的YARN应用程序可能会出现链接异常。
  • 还需要Client-Server(客户机-服务器)兼容性,以允许在不升级其它组件的情况下升级单个组件。例如,在不升级MapReduce的情况下,把HDFS从版本2.1.0升级到2.2.0。
  • 需要Server-Server(服务器-服务器)兼容性以允许在活动集群内的混合版本,从而可以在不停机的情况下以滚动方式来升级集群。

Policy

  • Client-Server(客户机-服务器)和Server-Server(服务器-服务器)的兼容性都保留在一个主版本中。(针对不同类别的不同策略尚待考虑)
  • 兼容性只能在主要版本中被破坏,但是即使在主要版本中破坏兼容性也会带来严重的后果,应该在Hadoop社区中进行讨论。
  • Hadoop协议是在.proto (ProtocolBuffers)文件中定义的。Client-Server(客户端-服务器)协议和Server-Server(服务器-服务器)协议 .proto文件被标记为稳定。当.proto文件被标记为稳定时,意味着应该按照如下所述的兼容方式进行更改:

。以下更改是兼容的,可以在任何时候进行:

       1. 添加一个可选字段,期望代码能够处理由于与旧版本代码通信而丢失的字段。

       2. 向服务添加新的rpc/方法。

      3.  向消息添加新的可选请求。

      4.  重命名一个字段。

      5.  重命名一个  .proto文件。

      6.  更改影响代码生成的.proto注释(例如java包的名称)。

。以下更改是不兼容的,但只能在主版本中可以考虑:

      1.  更改rpc/方法名字。

      2.  更改rpc/方法参数类型或返回类型

      3.  移除rpc/方法

      4.  更改服务名称

      5.  更改消息名称

      6.  以不兼容的方式(按递归定义)修改字段类型

      7. 将可选字段更改为required

      8.  添加或删除必须字段

      9.  只要可选字段具有允许删除的合理默认值,就删除可选字段

。以下更改是不兼容的,因此永远不允许:

      1.  改变字段id

      2.  重用以前删除的旧字段

      3.  字段编号很便宜,更改和重用不是一个好主意。


Java Binary compatibility for end-user applications i.e. Apache Hadoop ABI(最终用户应用程序(即Apache Hadoop ABI)的Java二进制兼容性)

随着Apache Hadoop修订版的升级,最终用户合理地期望他们的应用程序在不做任何修改的情况下应该继续工作。这是由于支持API 兼容性、Semantic兼容性和Wire兼容性而实现的。

然而,Apache Hadoop是一个非常复杂的分布式系统,服务的用例非常广泛。尤其是,ApacheHadoopMapReduce是一个非常、非常广泛的API;从这个意义上说,最终用户可能会做出广泛的假设,比如在执行map/reduce任务时本地磁盘的布局、任务的环境变量等。在这种情况下,很难完全指定和支持绝对兼容性。

用例

  • 现有的MapReduce应用程序,包括现有打包的终端用户应用程序的jars,以及Apache Pig、Apache Hive、Cascading等项目,在一个主版本中指向一个升级的Apache Hadoop集群时,应该可以不加修改地工作。
  • 现有的YARN应用程序,包括现有打包的终端用户应用程序jars和Apache Tez等项目,在主版本中指向升级的Apache Hadoop集群时,应该可以不加修改地工作。
  • 将数据传入/传出HDFS的现有应用程序,包括现有打包的最终用户应用程序的jars和Apache Flume等框架,在主版本中指向升级的Apache Hadoop集群时应该可以不加修改地工作。

policy

  • 现有的MapReduce、YARN和HDFS应用程序和框架应该可以在一个主版本(即支持Apache Hadoop ABI)中不加修改地工作。
  • 一小部分应用程序可能会受到磁盘布局等更改的影响,开发人员社区将努力将这些更改最小化,并且不会在较小的版本中进行更改。在更恶劣的情况下,我们将考虑强烈地恢复这些破坏性的更改,并在必要时使有问题的版本失效。
  • 特别是对于MapReduce应用程序,开发人员社区将尽力支持跨主要版本提供二进制兼容性,例如使用org.apache.hadoop.mapred的应用程序。

从hadoop-1.x跨到hadoop-2.x支持,APIs支持兼容性。更多详情请参阅 Compatibility for MapReduce applications between hadoop-1.x and hadoop-2.x


REST  APIs

REST API兼容性对应于请求(URLs)和对每个请求(内容,可能包含其他URLs)的响应。Hadoop REST APIs专门用于客户机跨版本(甚至是主版本)的稳定使用。以下是公开的REST APIs:

Policy

上面文本中注释稳定的API至少在一个主版本中保持兼容性,并且可能在一个主版本中被REST API的更新版本所弃。


Metrics/JMX

虽然度量标准API兼容性由Java API兼容性控制,但Hadoop公开的实际度量标准需要与之兼容,以便用户能够自动使用它们(脚本等)。添加额外的度量标准是兼容的。修改(例如更改单元或度量)或删除现有度量会破坏兼容性。类似地,对JMX MBean对象名称的更改也会破坏兼容性。

Policy

度量应该在主版本中保持兼容性。


File formats & Metadata

用户级和系统级数据(包括元数据)存储在不同格式的文件中。对元数据或用于存储数据/元数据的文件格式的更改可能导致版本之间不兼容。

User-level file formats

对最终用户用于存储其数据的格式所做的更改可能会阻止他们在以后的版本中访问数据,因此保持这些文件格式的兼容性非常重要。我们总是可以添加一个新格式,对现有格式加以改进。这些格式的示例包括har、war、SequenceFileFormat等。

Policy

  • 不向前兼容的用户文件格式更改仅限于主版本。当用户文件格式更改时,新版本预计将读取现有格式,但可能以与以前版本不兼容的格式写入数据。此外,社区更愿意创建一种新的格式,程序必须选择该格式,而不是对现有格式进行不兼容的更改。

System-internal file formats(系统内部文件格式)

Hadoop内部数据也存储在文件中,再次更改这些格式可能导致不兼容。虽然这种更改不像用户级文件格式那样具有破坏性,但是关于何时可以破坏兼容性的策略是很重要的。

MapReduce

MapReduce使用I-like这样的格式来存储MapReduce-specific数据。

Policy

对这些格式的更改可能会导致正在运行的作业失败,因此我们应该确保新客户机能够以兼容的方式从旧服务器获取shuffle-data。

HDFS Metadata

HDFS以特定的格式保存原数据(镜像和编辑日志)。对格式或元数据的不兼容更改会阻止后续版本读取旧的元数据。这种不兼容的更改可能需要HDFS “update”来转换元数据以使其可访问。一些更改可能需要多个这样的“updates”。根据变化中的不兼容程度,可能会出现以下潜在情况:

  • Automatic: 镜像自动升级,不需要显示的“update”。
  • Direct:镜像可升级,但可能需要一个显示版本“update”。
  • Indirect:镜像可升级,但可能需要先升级到中间版本。
  • Not upgradeable:镜像不可升级。

Policy

  • 版本升级必须允许群集回滚到旧版本及其旧磁盘格式。回滚需要恢复原始数据,但不需要恢复更新的数据。
  • HDFS元数据更改必须可以通过任何升级路径进行升级——automatic、direct、indirect。
  • 基于升级类型的更详细的策略尚未考虑。

Command Line Interface(CLI)

Hadoop命令行程序可以直接通过系统shell使用,也可以通过shell脚本使用。更改命令的路径,删除或重命名命令行选项、参数的顺序或命令返回代码和输出会破环兼容性,并且可能会对用户产生不利影响。

Policy

在随后的主版本中删除或不兼容地修改CLI命令之前,必须对一个主版本弃用它们(使用时发出警告)。


Web UI

Web UI,尤其是Web页面的内容和布局,更改可能会干扰对Web页面进行筛选以获取信息的尝试。

Policy

Web页面并不是用来剪贴的,因此任何时候都允许对其进行不兼容的更改。用户应该使用REST APIs来获取任何信息。


Hadoop Configuration Files

用户使用(1)Hadoop定义的属性来配置Hadoop并向其提供提示;(2)自定义属性来将信息传递给作业。因此,配置属性的兼容性是双重的:

  • 修改Hadoop定义的属性的键名、值单位和默认值。
  • 自定义配置属性键不应与Hadoop定义的属性的命名空间冲突。通常,用户应该避免使用Hadoop使用的前缀:hadoop, io, ipc, fs, net, file, ftp, s3, kfs, ha, file, dfs, mapred, mapreduce, yarn.

Policy

  • hadoop定义的属性在删除之前至少要在一个主版本中弃用。不允许修改现有属性的单位。
  • Hadoop定义的属性的默认值可以跨次/主版本进行更改,但在次版本中跨点版本保持不变。
  • 目前,对于何时可以添加/删除新前缀,以及自定义配置属性要避免的前缀列表,没有明确的策略。但是,如上所述,用户应该避免使用Hadoop使用的前缀:hadoop, io, ipc, fs, net, file, ftp, s3, kfs, ha, file, dfs, mapred, mapreduce, yarn.

Directory Structure

源代码、工件(源代码和测试)、用户日志、配置文件、输出和作业历史记录都存储在磁盘上,可以是本地文件系统,也可以是HDFS。更改这些用户可访问文件的目录结构会破坏兼容性,即使在通过符号链接保留原始路径的情况下(例如,如果路径由配置为不遵循符号链接的servlet访问)。

Policy

  • 源代码和构建工件的布局可以随时更改,特别是在主版本之间。在主版本中,开发人员将尝试(不保证)保留目录结构;但是,可以添加/移动/删除单个文件。确保补丁与代码保持同步的最佳方法是将它们提交到Apache源代码树。
  • 配置文件、用户日志和作业历史的目录结构将在主版本中的次版本和点版本之间保存。

Java Classpath

基于Hadoop构建的用户应用程序可能会将所有Hadoop jar(包括Hadoop的库依赖项)添加到应用程序的类路径中。添加新的依赖项或更新现有依赖项的版本可能会干扰应用程序类路径中的依赖项。 

Policy

目前,还没有关于何时Hadoop的依赖关系可以更改的策略。


Environments variables

用户和相关项目经常使用导出的环境变量(例如HADOOP_CONF_DIR),因此,删除或重命名环境变量是不兼容的更改。

Policy

目前,没有关于环境变量何时可以更改的策略。开发人员试图限制对主版本的更改。


Build artifacts

Hadoop用内行(Maven)进行项目管理,更改工件可能会影响现有的用户工作流。

Policy

  • Test artifacts: 生成的测试jars完全供内部使用,不应在Hadoop之外使用,类似于注释为@private,@unstable的APIs。
  • Build artifacts: Hadoop客户机工件(maven groupid:artifactid)在主版本中保持兼容,而其它工件可以以不兼容的方式更改。

Hardware/Software Resquirements

为了跟上硬件、操作系统、JVMs和其它软件的最新进展,新的Hadoop版本或其某些功能可能需要相同的更高版本。对于特定的环境,升级Hadoop可能需要升级其它依赖的软件组件。

Policies

  • 硬件
    • 架构:社区没有计划将Hadoop限制在特定的架构上,但是可以进行特定系列的优化。
    • 最少资源:虽然不能保证Hadoop守护进程所需的最小资源,但是社区试图在次版本中不增加需求。
  • 操作系统:社区将尝试在一个次版本中维护相同的操作系统需求(操作系统内核版本)。目前,GNU/Linux和Microsoft Windows是社区正式支持的操作系统,然而众所周知Apache Hadoop在其它操作系统(如苹果MacOSX、Solaris等)上运行得相当好。
  • 在同一次版本中,各点版本之间的JVM需求不会发生变化,除非所讨论的JVM版本不受支持。对于某些/所有受支持的操作系统,次/主版本可能需要更高版本的JVM。
  • 其它软件:社区试图维护Hadoop所需的附加软件的最低版本。例如,ssh、kerberos等。

References(参考文献)

以下是一些与主题相关的JIRAs和页面:

7. Interface Classification

Hadoop Interface Taxonomy: Audience and Stability Classification

Motivation

这里提供的接口分类是为了指导接口的开发人员和用户。分类指导开发人员声明接口的目标访问群体或用户以及其稳定性。

  • 对界面用户的好处:知道要使用或不使用哪些接口及其稳定性。
  • 对开发人员的好处:防止意外更改接口,从而对用户或其他组件或系统造成意外影响。 这在有许多可能并不都具有共享的项目状态/历史的开发人员的大型系统中特别有用。

Interface Classification(接口分类)

Hadoop采用了以下接口分类,这个分类是从OpenSolaris分类法派生出来的,在某种程度上是从Yahoo内部使用的分类法派生出来的。接口有两个主要属性:访问量和稳定性。

Audience

Audience表示接口的潜在消费者。虽然许多接口是实现的内部/私有接口,但其它接口是公共/外部接口,用于应用程序和/或客户机更广泛的使用。例如,在posix中,libc是一个外部接口或公共接口,而内核的大部分是内部接口或私有接口。另外,一些接口针对其他特定的子系统。

识别接口的访问群体有助于定义破坏它的影响。例如,可以打破一个 访问对象是一小部分特定子系统的 接口 的兼容性。另一方面,破坏数百万互联网用户所依赖的协议接口可能是不好的。

Hadoop使用以下类型的受众以提高/扩大可见性:

Hadoop没有公司私有的分类,这是针对公司内其他项目使用的APIs的,因为它不适用于开源项目。此外,某些APIs被注释为@VisibleForTesting (from com.google.common .annotations.VisibleForTesting) - 它们被完全用于单元测试,并且应该被视为“Private” APIs。

Private

该接口用于项目内部(如HDF或MapReduce),不应被应用程序或其他项目使用。随时可更改,恕不另行通知。项目的大多数接口都是私有的(也称为project-private)。

LImited-Private

接口由指定的一组项目或系统(通常是紧密相关的项目)使用。其它接口的变更将与指定的项目进行沟通/协商。项目或系统不应使用接口。例如,在Hadoop项目中,一些接口是LimitedPrivate{HDFS, MapReduce},因为它们是HDFS和MapReduce项目的私有接口。

Public

接口供任何应用程序通用。


Stability

稳定性表示接口的稳定程度,如允许接口不兼容改变时一样。Hadoop APIs具有以下级别的稳定性。

Stable

可以在保持对次要版本边界的兼容性的同时进行改进;换句话说,标记为stable的APIs的不兼容更改只允许在主版本(即在m.0)中进行。

Evolving

正在发展,但允许次版本(即m.x)不兼容的更改。

Unstable

任何时候都允许对不稳定的API进行不兼容的更改。这通常只对私有接口有意义。

然而,对于一个假定的公共接口,我们可以调用它来强调它不应该被用作接口;对于公共接口,将其标记为非接口可能比"Ustable"更合适。

不稳定(即not-an-interface)的公开可见接口示例:GUI、输出格式将更改的CLIs.

Deprecated

将来可能会被删除而不应使用的APIs。

How are the Classifications Recorded?(如何记录分类?)

如何记录Hadoop APIs的分类?

  • 每个接口或类都将使用org.apache.hadoop.classification包中的注释记录访问量和稳定性。
  • maven目标javadoc:javadoc生成的javadoc只列出公共API。
  • 可以通过包含java类和java接口的包的对象派生它们的对象。因此,将每个Java包对象声明为公共的或私有的(以及私人对象的变化)是有用的。

FAQ(常见问题解答)

  • 为什么java作用域(private、pack private and public)还不够好?
    • Java的作用域不是很完整。为了让其它内部组件使用类,通常必须将该类公开。它没有C++那样的朋友或私有子包。
  • 但如果Java是公用的,我可以轻松地访问私有实现接口。保护和控制在哪里?
    • 这样做的目的不是提供绝对访问控制。它的目的是与用户和开发人员进行通信。您可以访问libc中的私有实现函数;但是,如果它们更改了内部实现的详细信息,您的应用程序将中断,并且您将很少得到提供libc的人员的同情。如果您使用非公共接口,则可以理解风险。
  • 何苦还要声明私有接口的稳定性呢?私有接口不是总是不稳定吗?
    • 私有接口并不总是不稳定的。在它们稳定的情况下,它们捕获系统的内部属性,并可以将这些属性传递给内部用户和接口的开发人员。
      • 例如,在HDFS中,NN-DN协议是私有的但也是稳定的,可以帮助实现滚动升级。它传达了此接口尽管是私有的,但也不应该以不兼容的方式更改。
      • 例如,在HDFS中,fsimage稳定性提供了更灵活的回滚。
  • 使用稳定的私有接口的应用程序有什么危害?它与公共稳定接口有什么不同?
    • 虽然标记为稳定的私有接口只在主版本中进行更改,但如果该接口的提供者愿意更改该接口的内部用户,则该接口可能在其他时间中断。此外,公共稳定接口在主版本中不太可能崩溃(即使允许它破坏兼容性),因为更改的影响更大。如果您使用私有接口(不管它的稳定性如何),就会有不兼容的风险。
  • 何必费心Limited-private?这不是给一些项目提供特殊待遇吗?这是不公平的。
    • 首先,大多数接口应该是公共的或私有的;实际上,让我们更强烈地声明一下:将其设置为私有,除非您真的想将其公开通用。
    • Limited-private用于非通用接口。它们暴露于需要特殊钩子的相关项目中。这种分类对有限接口的供应商和消费者都有成本。它们暴露于需要特殊钩子的相关项目中。这种分类对有限接口的供应商和消费者都有成本。如果将来需要破坏接口,那么他们将不得不合作;例如,供应商和消费者必须协同工作,以获得各自项目的协调版本。这一点不应掉以轻心——如果你可以不受惩罚,那么就这样做;如果该接口确实是用于所有应用程序的通用接口,那么就这样做。但是请记住,将接口公开有很大的责任。有时Limited-private正好合适。
    • Limited-private的一个很好的例子是BlockLocations,这是一个相当低级的接口,我们愿意向MR和HBase公开它。我们可能会在将来对其进行更改,并且在那时我们将与MR团队协调发布工作。虽然今天MR和HDF总是同步发布,但它们可能会改变。
    • 如果您有一个Limited-private接口,上面列出了许多项目,那么您就是在愚弄自己。它实际上是公开的。
    • 可能有必要为Hadoop家族声明一个名为Hadoop- Private的特殊受众分类。
  • 让我们将所有的私有接口视为Hadoop-private。Hadoop家族的项目中有哪些危害可以访问私有类?
    • 我们是否希望MR访问HDFS中的实现细节类文件。在过去的几年里,我们一直在清理代码中很多这样的层冲突。我们不希望这种层冲突通过在主要组件(如HDFS和MR)之间不进行分离而卷土重来。
  • 不是所有的公共接口都是稳定的吗?
    • 可以将公共接口标记为在早期发展的。在这里,我们承诺要努力做出兼容的更改,但是可能需要在次版本中打破它。
    • 不稳定的公共接口的一个例子是,提供了一个仍在开发中的基于标准体的接口的实现。例如,许多公司为了率先上市,甚至在IETF没有完全完成协议的情况下,也提供了新的NFS协议的实现。由于稳定性由标准体控制,因此实现者无法以导致最少中断的方式发展接口。因此,将接口标记为不稳定是合适的。

8. FileSystem Specification(文件系统规范)

The Hadoop FileSystem API Definition

这是Hadoop文件系统APIs的规范,它将文件系统的内容建模为一组路径,这些路径可以是目录、符号链接或文件。

在这一领域几乎没有先例。Unix文件系统有多个规范作为索引节点树,但没有公共规范定义了“Unix文件系统作为数据存储访问的概念模型”。

本规范试图做到这一点;定义Hadoop文件系统模型和API,以便多个文件系统可以实现API,并向应用程序提供其数据的一致模型。它不试图正式地指定文件系统的任何并发行为,除了记录HDF显示的行为,因为这些行为通常是Hadoop客户机应用程序所期望的。

  1. Introduction
  2. Notation
  3. Model
  4. FileSystem class
  5. FSDataInputStream class
  6. Testing with the Filesystem specification
  7. Extending the specification and its tests

Ⅰ.Introduction

本文档为Hadoop文件系统的实现者和维护者以及Hadoop文件系统APIs的用户定义了Hadoop兼容文件系统所需的行为。

大多数Hadoop操作都是在Hadoop测试套件中针对HDF进行测试的,最初通过MiniDFSCluster进行测试,发布之前由特定供应商的“生产”测试,并由其上的Hadoop堆栈隐式地进行测试。

HDFS的操作是基于POSIX文件系统行为建模的,使用Unix文件系统操作的操作和返回代码作为参考。即使如此,也有一些HDF与POSIX文件系统的预期行为不同的地方。

其它Hadoop文件系统的行为没有经过严格测试。捆绑的S3N和S3A文件系统客户端使Amazon的S3对象存储(“blobstore”)可以通过文件系统API进行访问。Swift文件系统驱动程序为OpenStack Swift  blobstore提供了类似的功能。Azure对象存储文件系统与微软的Azure等效程序进行对话。

“本地”文件系统提供对平台底层文件系统的访问。它的行为由操作系统定义,并且可以与HDF的行为不同。本地文件系统怪癖的例子包括区分大小写、尝试在另一个文件上重命名文件时的操作,以及是否可以在文件末尾查找()。

还有一些第三方实现的文件系统,它们声称与Apache Hadoop兼容。没有正式的兼容性套件,所以除了以自己的兼容性测试的形式之外,没有人可以声明兼容性。

这些文件并不试图提供兼容性的规范定义。通过关联的测试套件并不能保证应用程序的正确行为。

测试套件所定义的是预期的操作集——如果这些测试失败,将突出潜在的问题。通过使契约测试的每一个方面都可配置,就可以声明文件系统与标准契约的不同之处。这是可以传递给文件系统用户的信息。

Naming

下面的文档遵循关于 MUST, MUST NOT, MAY, and SHALL的使用规则。MUST NOT被视为规范性文件。

Hadoop文件系统APIs的隐式假设

原始的文件系统类及其用法基于一组隐含的假设。HDFS主要是底层文件系统,它提供POSIX文件系统行为的一个子集(或者至少是POSIX文件系统APIs的实现和Linux文件系统提供的模型)。

不管API如何,所有与Hadoop兼容的文件系统都会呈现在Unix中实现的文件系统模型:

  • 它是一个包含文件和目录的分层目录结构。
  • 文件包含零个或多个字节的数据。
  • 不能将文件或目录放在文件下。
  • Directories contain zero or more files。
  • 目录项本身没有数据。您可以将任意二进制数据写入文件。当从集群内或集群外的任何位置读取文件内容时,将返回数据。
  • 您可以在一个文件中存储许多GB的数据。
  • 根目录“/”始终存在,不能重命名。
  • 根目录“/”始终是一个目录,不能被文件写入操作覆盖。
  • 任何递归删除根目录的尝试都将删除其内容(除非没有权限),但不会删除根路径本身。
  • 不能在此目录下重命名/移动目录。
  • 除了源文件本身之外,不能在任何现有文件上重命名/移动目录。
  • 目录列表返回目录中的所有数据文件(即可能存在隐藏的校验和文件,但列出了所有数据文件)。
  • 目录列表中文件的属性(例如所有者、长度)与文件的实际属性匹配,并且与打开的文件引用中的视图一致。
  • 安全性:如果调用者缺少操作权限,它将失败并引发错误。

Path Names

  • 路径由以“/”分隔的路径元素组成。
  • 路径元素是一个包含1个或多个字符的unicode字符串。
  • 路径元素不能包含字符“:”或“/”。
  • path元素不应包含值为0-31的ASCII/UTF-8 字符。
  • 路径元素不能是“.”或“..”。
  • 还请注意,Azure Blob存储区文档表示路径不应使用尾随“.”(因为它们的.NET URI类会将其剥离)。
  • 路径基于Unicode代码点进行比较。
  • 不区分大小写和特定区域 的比较不能用。

Security Assumptions(安全性假设)

除了在关于安全性的特殊部分,本文档假定客户机具有对文件系统的完全访问权。因此,列表中的大多数项目没有添加“假设用户有权使用提供的参数和路径执行操作”的限定条件。

未指定用户缺少安全权限时的故障模式。

Networking Assumptions(网络假设)

本文档假设所有网络操作都成功。所有语句都可以被假定为“假定操作不因网络可用性问题而失败”。

  • 未定义网络故障后文件系统的最终状态。
  • 未定义网络故障后文件系统的即时一致性状态。
  • 如果可以向客户端报告网络故障,则故障必须是IOException或其子类的实例。
  • 异常细节应该包括适合有经验的Java开发人员或操作团队开始诊断的诊断信息。例如,ConnectionRefused异常上的源和目标主机名和端口。
  • 异常细节可能包括适合于没有经验的开发人员开始诊断的诊断信息。例如,Hadoop试图在TCP连接请求被拒绝时包含对ConnectionRefused的引用。

Core Expectations of a Hadoop Compatible FileSystem(Hadoop兼容文件系统的核心期望)

下面是Hadoop兼容文件系统的核心期望。一些文件系统不能满足所有这些期望;因此,一些程序可能无法按预期工作。

Atomicity(原子性)

有些操作必须是原子的。这是因为它们通常用于实现集群中进程之间的锁定/独占访问。

  1. 创建文件。如果overwrite参数为false,则检查和创建必须是原子的。
  2. 删除文件。
  3. 重命名文件。
  4. 重命名目录。
  5. 使用mkdir()创建单个目录。
  • 递归目录删除可能是原子的。虽然HDFS提供原子递归目录删除,但是其他Hadoop文件系统都没有提供这样的保证(包括本地文件系统)。

大多数其他操作都没有原子性的要求或保证。

Consistency(一致性)

Hadoop文件系统的一致性模型是一种拷贝更新语义( one-copy-update-semantics),即传统本地POSIX文件系统的一致性模型。注意,甚至NFS也放宽了关于更改传播速度的一些限制。

  • 创建。一旦写入新创建文件的输出流上的close()操作完成,查询文件元数据和内容的集群内操作必须立即看到此文件及其数据。
  • 更新。一旦写入新创建文件的输出流上的close()操作完成,查询文件元数据和内容的集群内操作必须立即看到信数据。
  • 删除。一旦除“/”之外的路径上的delete()操作成功完成,就不能显示或访问它。具体来说,listStatus()、open()、rename()和append()操作必须失败。
  • 删除然后创建。当删除一个文件时,然后创建一个同名的新文件,这个新文件必须立即可见,并且它的内容可以通过文件系统APIs访问。
  • 重命名。rename()完成后,对新路径的操作必须成功;根据旧路径访问数据的尝试必须失败。
  • 集群内部的一致性语义必须与集群外部的一致性语义相同。查询未被积极操作的文件的所有客户机必须看到相同的元数据和数据,而不管它们的位置如何。

Concurrency(并发性)

不保证隔离访问数据:如果一个客户机与远程文件交互,而另一个客户机更改了该文件,则更改可能可见,也可能不可见。

Operations and failtures(操作和故障)

  • 所有操作最终必须完成,不管成功还是失败。
  • 完成操作的时间没有定义,可能取决于实现和系统的状态。
  • 操作可能引发RuntimeException或其子类。
  • 操作应将所有网络、远程和高级问题作为IOException或其子类引发,并且不应针对此类问题引发RuntimeException。
  • 操作应该通过引发的异常而不是操作的特定返回代码来报告故障。
  • 在文本中,当命名异常类(如IOException)时,引发的异常可能是命名异常的实例或子类。它不能是超类。
  • 如果某个操作未在类中实现,则该实现必须引发UnsupportedOperation异常。实现可能会重试失败的操作,直到成功。如果它们这样做,那么它们应该以任何操作序列之间happens-before关系满足所述一致性和原子性要求的方式这样做。请参阅 HDFS-4849了解此示例:HDFS不实现任何其他调用者可以观察到的重试功能。

Undefined capacity limits(未定义的容量限制)

以下是一些从未明确定义的文件系统容量限制。

  1. 目录中的最大文件数。
  2. 目录中的最大目录数。
  3. 文件系统中条目(文件和目录)的最大总数。
  4. 目录下文件名的最大长度(HDFS:8000)。
  5. MAX_PATH - 引用文件的整个目录树的总长度。Blobstore通常在~1024个字符处停止。
  6. 路径的最大深度(HDFS:1000个目录)。
  7. 单个文件的最大大小。

Undefined timeouts(未定义的超时)

操作超时完全没有定义,包括:

  • 阻塞文件系统操作的最大完成时间。MAPREDUCE-972记录了distcp是如何在s3重命名缓慢时崩溃的。
  • 空闲读取流关闭前的超时。
  • 空闲写入流关闭前的超时。

在HDFS中,阻塞操作超时实际上是可变的,因为站点和客户机可能会调优重试参数,以便将文件系统故障和故障转移转换为操作中的暂停。相反,人们普遍认为,FS操作“速度快,但不如本地FS操作快”,而且数据读写的延迟随数据量的增加而增加。

对于某些操作的开销也有一些隐含的假设。

  1. seek()操作非常快,几乎不会导致网络延迟。[不支持blob存储]。
  2. 目录列表操作对于条目较少的目录来说是快速的。
  3. 目录列表操作对于条目很少的目录来说是快速的,但是可能会产生O(条目)的开销。Hadoop 2增加了迭代式列表,以处理在不以一致性为代价进行缓冲的情况下,列出数百万条目的目录的挑战。
  4. 无论文件操作是否成功,OutputStream的close()是快速的。
  5. 删除目录的时间与子条目数的大小无关。

Object Stores vs. Filesystems

此规范指的是对象存储在某些地方,通常使用术语Blobstore。这就是为什么,尽管Hadoop可以在对象存储中读取和写入数据,但Hadoop提供的两个直接支持(AmazonS3和OpenStack Swift)不能用作HDF的直接替代品。

什么是对象存储( Object Store)?

对象存储是一种数据存储服务,通常通过HTTP/HTTPS访问。对象存储是一种数据存储服务,通常通过HTTP/HTTPS访问。PUT请求上传一个对象/“Blob”;GET请求检索它;远程GET操作允许检索blob的某些部分。要删除该对象,将调用HTTP DELETE操作。

对象按名称存储:一个字符串,其中可能包含“/”符号。没有目录的概念;可以将任意名称分配给对象——这是在服务提供者强加的命名方案的限制范围内。

对象存储总是提供一个操作来检索具有给定前缀的对象;具有适当查询参数的服务根上的GET操作。

对象存储通常优先级可用性-没有与HDFS NameNode(s)等效的单点故障。他们还努力使用简单的非POSIX APIs:HTTP动词是允许的操作。

用于对象存储的Hadoop文件系统客户端试图让这些存储假装它们是一个文件系统,一个具有与HDFS相同的特性和操作的文件系统。。这最终是一种伪装:他们有不同的特点,偶尔幻想也会失败。

  1. Consistency(一致性)。对象存储通常最终是一致的:对对象的更改(创建、删除和更新)可能需要一些时间才能对所有调用方可见。实际上,不能保证刚进行更改的客户机可以立即看到更改。例如,对象test/data1.csv可能会被一组新数据覆盖,但是当更新后不久进行GET test/data1.csv调用时,原始数据会返回。Hadoop假定文件系统是一致的;创建、更新和删除都是立即可见的,并且列出目录的结果对于该目录中的文件是最新的。
  2. Atomicity(原子性)。Hadoop假设目录rename()操作是原子的,就像delete()操作一样。对象存储文件系统客户端将这些操作实现为对名称与目录前缀匹配的单个对象的操作。结果是,每次修改都是一次性的,而且不是原子的。如果一个操作在整个过程中有一部分失败,那么对象存储的状态将反映部分完成的操作。还要注意,客户机代码假设这些操作是0(1)——在对象存储中,它们更可能是0(子条目)。
  3. Durability(持久性)。Hadoop假设OutputStream实现在flush()操作中将数据写到它们的(持久)存储中。对象存储实现将所有写入的数据保存到一个本地文件中,该文件在最后的close()操作中只能PUT到对象存储。因此,从不存在来自不完整或失败操作的任何部分数据。此外,由于写过程只在close()操作中开始,所以该操作可能需要的时间与要上传的数据量成正比,与网络带宽成反比。它也可能失败——升级失败比忽略失败要好。
  4. Authorization(授权)。Hadoop使用FileStatus类表示文件和目录的核心元数据,包括所有者、组和权限。对象存储可能没有保存此元数据的可行方法,因此可能需要用存根值填充FileStatus。即使对象存储持久化此元数据,对象存储以与传统文件系统相同的方式强制执行文件授权仍然可能是不可行的。如果对象存储不能持久保存此元数据,那么推荐的约定是:                                                               。文件所有者被报告为当前用户。                                                                                                                                                           。文件组也被报告为当前用户。                                                                                                                                                               。目录权限被报告为777。                                                                                                                                                                       。文件权限被报告为666。                                                                                                                                                                       。设置所有权(ownership)和权限的文件系统API可以无错误地成功执行,但它们不是操作。                                                         具有这些特征的对象存储,不能用作HDFS的替代品。它们被认为是Hadoop开发社区的支持,但与HDF的支持程度不同。

Timestamps

FileStatus条目有一个修改时间和一个访问时间。

  1. 关于何时设置这些时间戳以及它们是否有效的确切行为,在文件系统之间以及可能在文件系统的各个安装之间有所不同。
  2. 时间戳的粒度同样是特定于文件系统和可能的单个安装的。

HDFS文件系统在写入时不会更新修改时间。

明确地

  • FileSystem.create()创建:列出一个零字节文件;修改时间设置为NameNode上显示的当前时间。
  • 通过create()调用中返回的输出流写入文件:修改时间不变。
  • 调用OutputStream.close()时,将写入所有剩余数据,关闭文件,并使用文件的最终大小更新NameNode。修改时间设置为关闭文件的时间。
  • 通过append()操作为appends(追加)打开文件不会更改文件的修改时间,直到对输出流执行close()调用。
  • FileSystem.setTimes()可用于显式设置文件的时间。
  • 重命名文件时,其修改时间不会更改,但源目录和目标目录的修改时间会更新。
  • 很少使用的操作:FileSystem.concat()、createSnapshot()、createSymlink()和truncate()都更新修改时间。
  • 访问时间粒度以毫秒为单位设置:dfs.namenode.access.time.precision;默认粒度为1小时。如果精度设置为零,则不会记录访问时间。
  • 如果未设置修改或访问时间,则该 FileStatus 字段的值为0。

其它文件系统可能有不同的行为。特别地,

  • 可支持或不支持访问时间;即使底层FS可能支持访问时间,但由于性能原因,常常禁用该选项。
  • 时间戳的粒度是具体实施的细节。

对象存储对时间的看法更加模糊,可以概括为“它是变化的”。

  • 时间戳粒度可能是1秒,即HTTP HEAD和GET请求中返回的时间戳粒度。
  • 访问时间可能未设置。也就是说, FileStatus.getAccessTime() == 0。
  • 新创建文件的修改时间戳可以是调用create()的修改时间戳,也可以是发起PUT请求的实际时间戳。这可能在FileSystem.create()调用中,最后的 OutputStream .close()操作,在这两者之间的某个时期。
  • 修改时间可能不会在close()调用中更新。
  • 时间戳可能在对象存储的UTC或TZ中。
  • 时间戳可能在对象存储的UTC或TZ中。如果客户机位于不同的时区,则对象的时间戳可能在客户机的时间戳之前或之后。
  • 具有缓存的元数据数据库的对象存储(例如:具有内存中的AWS S3或DynamoDB元数据存储)可能具有从本地系统时钟生成的时间戳,而不是服务的时间戳。这是一种避免对对象存储进行往返调用的优化。
  • 文件的修改时间通常与创建时间相同。
  • 设置文件时间戳的FileSystem.setTimes()操作可能会被忽略。
  • FileSystem.chmod()可能会更新修改时间(例如:Azure wasb://)。
  • 如果支持FileSystem.append(),那么更改和修改时间可能只在关闭输出流后才可见。
  • 对象存储中数据的带外操作(即:绕过Hadoop文件系统APIs直接请求对象存储)可能会导致存储和/或返回不同的时间戳。
  • 由于经常模拟目录结构的概念,目录的时间戳可能是人工生成的——可能使用当前系统时间。
  • 由于rename()操作通常是以COPY+DELETE的形式实现的,因此重命名对象的时间戳可能会成为启动对象重命名时的时间戳,而不是源对象的时间戳。
  • 不同对象存储安装之间的时间戳行为可能会有所不同,即使是在同一时间存储客户端上。

最后,请注意,ApacheHadoop项目不能保证远程对象存储的时间戳行为是否会随着时间的推移保持一致:它们是第三方服务,通常通过第三方库访问。

这里最好的策略是“用你打算使用的确切终点进行实验”。此外,如果您打算使用任何缓存/一致性层,请启用该功能进行测试。更新Hadoop版本和端点对象存储更新后重新测试。

Ⅱ.Notation(符号)

一个正式的符号(如Z符号)将是定义Hadoop文件系统行为的最严格的方法,甚至可以用来证明一些公理。

但是,它有许多实际缺陷:

  1. 这样的符号没有得到应有的广泛使用,因此更广泛的软件开发社区将不会有实际经验。
  2. 不使用诸如LaTex和(add-on)附加库之类的工具是很难做到的。
  3. 即使对专家来说,这种符号也很难理解。

由于该规范的目标受众是文件系统开发人员,因此不适合使用正式的符号。相反,广泛的可理解性、易于维护和易于派生测试优先于纯数学形式的符号。

Mathematics Symbols  in this doucment(本文中的数学符号)

本文档在Z语法中使用了符号的子集,但使用的是ASCII格式,并使用Python列表符号来操作列表和集合。

  • iff : iff  If and only if
  • ⇒ : implies
  • → : --> total function
  • ↛ : -> partial function

  • ∩ : ^: Set Intersection(交集)
  • ∪ : +: Set Union(并集)
  • \ : -: Set Difference(差集)

  • ∃ : exists Exists predicate(predicate--判断符)

  • ∀ : forall: For all predicate
  • = : == Equals operator(等于运算符)
  • ≠ : != operator. In Java z ≠ y is written as !( z.equals(y)) for all non-simple datatypes
  • ≡ : equivalent-to equivalence operator. This is stricter than equals.(比等于更严格的等价运算符)
  • ∅ : {} Empty Set. ∅ ≡ {}
  • ≈ : approximately-equal-to operator(近似等于、约等于)
  • ¬ : not Not operator. In Java, !(非操作符)
  • ∄ : does-not-exist: Does not exist predicate. Equivalent to not exists
  • ∧ : and : local and operator. In Java , &&
  • ∨ : or : local or operator. In Java, ||
  • ∈ : in : element of
  • ∉ : not in : not an element of
  • ⊆ : subset-or-equal-to the subset or equality condition
  • ⊂ : subset-of the proper subset condition
  • | p | : len(p) the size of a variable

  • := : = :

  • `:#` : Python-style comments

Sets、LIsts、Maps、and Strings

 python data structures被用作这种语法的基础,因为它是纯ASCII,而且众所周知。

Lists

  • 列表L是一个有序的元素序列[e1,e2,..en]。
  • 一个列表的len(L)的大小是列表中元素的数量。
  • 项目可通过基于0的索引e1==l[0]进行寻址。
  • python切片运算符可以处理列表的子集  ?????????????????????????????????
  • 表列表可以连接L' = L + [e3]
  • 列表可以删除条目L'=L - [e2,e1]。这与python的del操作不同,后者在适当的位置对列表进行操作。注意:python不可以
  • 如果元素在一个lie'b列表中:e2 in L,则成员关系判断符in返回true。
  • 列表推导式可以创建新的列表: L' = [ x for x in l where x < 5]
  • 对于列表L,len(L)返回元素数。

Sets

集合是列表符号的扩展,添加了限制,即集合中不能有重复的条目,并且没有定义的顺序。

  • 集合是由 {  和  } 大括号包围的无序项集合。
  • 当声明一个集合时,使用python构造函数{}。这与Python不同,后者使用函数set([list])。这里的假设是,集合和字典之间的差异可以由内容决定。
  • 空集合{}没有元素。
  • 所有常用的集合概念都适用。
  • 成员关系符是in。
  • 集合推导可以使用列表推导:S' = {s for s in S where len(s)==2}
  • 对于集合s,len(s)返回元素数。
  • -操作符返回一个新集合,不包括运算符右侧集合中列出的所有项。注意:python可以

Maps

Maps(映射)类似于python字典;{“key”:value, “key2”,value2}

  • keys(Map)表示映射中的键集。
  • k in Map holds iff k in keys(Map)
  • 空映射为{:}
  • -运算符返回一个新的映射,该映射排除指定键的条目。注意:python不可以
  • len(Map)返回映射中的条目数。

Strings

字符串是用双引号表示的字符列表。例如,"abc"

"abc"==['a','b','c']

State Immutability(状态不变性)

所有系统状态声明都是不可变的。

后缀“’”(单引号)作为约定,表示操作后系统的状态。

L' = L + ['d','e']

Function Specifications(函数说明)

函数被定义为一组先决条件和一组后置条件,其中后置条件定义系统的新状态和函数的返回值。

Exceptions(异常)

在经典的规范语言中,前置条件定义了必须满足的谓词,否则会引发一些失败条件。

在经典的规范语言中,前置条件定义了必须满足的谓词,否则会引发一些失败条件。

符号raise<exception name>用于指示将引发异常。

如果不满足某个先决条件,可以在if-then-else序列中使用它来定义操作。

示例:

if not exists(FS, Path) : raise IOException

如果实现可能引发一组异常中的任何一个,则通过提供一组异常来表示:

if not exists(FS, Path) : raise {FileNotFoundException, IOException}

如果提供了一组异常,则该组的前面元素优先于后面的条目,因为它们有助于诊断问题。

我们还需要区分必须满足的断言和应该满足的断言。因此,函数规范可以在标记为“Should”的前置条件中包含一个部分:该部分中声明的所有断言都应该满足,如果该部分中有一个条目指定了更严格的结果,则应该首选它。这里有一个should-precondition的例子:

Should:

if not exists(FS, Path) : raise FileNotFoundException

Conditions

在前置条件和后置条件声明中还使用了其他条件。

supported(instance, method)

这个条件声明了一个子类实现了命名方法——一些真正的文件系统类的子类不实现,而是引发UnsupportedOperation例如,FSDataInputStream.seek的一个前提条件是实现必须支持Seekable.seek:

supported(FDIS, Seekable.seek) : else raise UnsupportedOperation

III.Model

A Model of Hadoop FileSystem


Paths and Path Elements

路径是表示文件、目录或符号链接路径的路径元素列表。

路径元素是非空字符串。有效字符串的确切集合可能仅限于特定的文件系统实现。

路径元素不能位于{“”,“.”,“…”,“/”}中。

径元素不能包含字符{'/'、':'}。

文件系统可能有其他在path元素中不允许的字符串。

验证路径元素时,当路径无效时,应引发异常InvalidPathException[HDFS]。

Predicates and Functions

valid-path-element(List[String]): bool

如果路径元素pe中的任何字符位于禁止字符集中,则该元素无效,或者该元素作为一个整体无效。

forall e in pe: not (e in {'/', ':'})
not pe in {"", ".",  "..", "/"}

valid-path(List[PathElement]): bool

如果路径p中的所有路径元素都有效,则该路径p有效。

def valid-path(path): forall pe in path: valid-path-element(pe)

所有可能路径的集合是paths;这是所有有效路径元素列表的无限集合。

由空列表表示的路径,[]是根路径,并由字符串“/”表示。

parent(path:Path): Path

分部函数parent(path:Path):Path提供可以使用列表切片定义父路径。

def parent(pe) : pe[0:-1]

前提条件:

path != []

filename(Path): PathElement

路径中的最后一个路径元素称为文件名。

def filename(p):p[-1]

childElements(Path p, Path q): Path

部分函数childElements:(Path p, Path q):Path 是p中跟随路径q的路径元素的列表。

def childElements(p, q): p[len(q):]

前提条件:

#路径‘q’必须位于路径'p'的头部

q=p[:len(q)]

ancestors(Path): List[Path]

所有路径的列表,这些路径要么是路径p的直接父路径,要么是p的祖先路径的父路径。

Notes for relative paths(相对路径说明)

这个定义处理的是绝对路径,而不是相对路径;它需要重新编写,以便根元素是显式的,这可能是通过声明根(并且只有根)路径元素可以是['/']。

然后可以将相对路径与绝对路径区分开来,将其作为任何函数的输入,并在双参数函数(如rename)中的第二个条目时进行解析。


Defining the FileSystem

一个文件系统FS包含一组目录、一个路径字典和一个符号链接字典。

(Directories:Set[Path], Files:[Path:List[byte]], Symlinks:Set[Path])

访问器函数返回文件系统指定的元素。

def FS.Directories = FS.Directories

def files(FS) = FS.Files

def symlinks(FS) = FS.Symlinks

def filenames(FS) = keys(FS.Files)

所有可能路径的有限子集的整个路径集,以及解析数据路径、目录谓词或符号链接的函数:

def paths(FS) = FS.Directories + filenames(FS) + FS.Symlink

如果路径在此聚合集中,则认为该路径存在:

def exists(FS, p) = p in paths(FS)

根路径“/”是由路径[“/”]表示的目录,该路径必须始终存在于文件系统中。

def isRoot(p) = p ==["/"]

forall FS in FileSystems : ["/"] in FS.Directories

Directory references(目录引用)

路径可以引用文件系统中的目录:

isDir(FS, p) : p in FS.Directories

目录可能有子目录,也就是说,文件系统中可能存在路径以目录开头的其他路径。只有目录可以有子目录。这可以通过说每个路径的父级都必须是一个目录来表示。

然后可以声明一个路径没有父目录,在这种情况下,它是根目录,或者它必须有一个父目录:

forall p in paths(FS) : isRoot(p) or isDir(FS, parent(p))

由于所有目录的父目录本身必须满足此条件,因此,隐式地说,只有叶子节点可以是文件或符号链接:

此外,因为每个文件系统都包含根路径,所以每个文件系统必须至少包含一个目录。

目录可以有子目录:

def chileren(FS, p) = {q for q in paths(FS) where parent(q) == p}

子路径中没有重复的名称,因为所有路径都取自路径元素的列表集。集合中不能有重复的条目,因此没有具有重复名称的子项。

如果路径D是路径P的直接子级或祖先是路径P的直接子级,则路径D是路径P的后代:

def isDescendant(P, D) = parent(D) ==P where isDescendant(P, parent(D))

目录P的后代是文件系统中以路径P开头的所有路径——即它们的父路径是P,或者祖先路径是P

def descendants(FS, D) = {p for p in paths(FS) where isDescendant(D, p)}

File references(文件引用)

路径可以引用文件系统中包含数据的文件;它的路径是数据字典中的一个键。

def isFile(FS, p) = p in FS.Files

Symbolic references

路径可以引用符号链接:

def isSymlink(FS, p) = p in symlinks(FS)

File Length(文件长度)

文件系统FS中路径p的长度是存储数据的长度,如果是目录,则为0:

def length(FS, p) = if isFile(p) : return length(data(FS, p)) else return 0

User home

用户的主目录是文件系统的隐式部分,由使用文件系统的进程的用户标识派生而来:

def getHomeDirectory(FS) : Path

函数gethomedirectory返回文件系统和当前用户帐户的主目录。对于某些文件系统,路径是["/","users", System.getProperty("user-name")].  但是,对于HDFS,用户名来自使用HDFS对客户机进行身份验证的凭据。This may differ from the local user account name。

Exclusivity

路径不能引用多个文件、目录或符号链接

FS.Directories  ^  keys(data(FS)) =={}

FS.Deirctories  ^  symlinks(FS) =={}

keys(data(FS))(FS)  ^  symlink(FS) =={}

这意味着只有文件可以有数据。

这个条件是不变的,并且是操作文件系统FS状态的所有操作的隐式后置条件。

Encryption Zone(加密区)

如果文件处于加密区域,则对数据进行加密。

def inEncryptionZone(FS, path) :  bool

加密的性质和创建加密区域的机制是本规范未涉及的实现细节。我们不保证加密的质量。元数据没有加密。

加密区域中目录下的所有文件和目录也在加密区域中。

forall d in directories(FS) : inEncryptionZone(FS, d) impies

  forall c in chileren(FS, d) where (isFile(FS, c) or isDir(FS, c)) :

    inEncryptionZone(FS, c)

对于加密区域中的所有文件,数据都是加密的,但没有定义加密类型和规范。

forall f in files(FS) where inEncyptionZone(FS, f):
  isEncrypted(data(f))

NOTES

不包括:文件系统中的硬链接。如果文件系统支持Paths(FS)中的多个引用来指向同一数据,那么操作的结果是未定义的。

文件系统的这个模型足以描述除元数据和权限操作之外的所有文件系统查询和操作。Hadoop FileSystemFileContext接口可以根据查询或更改文件系统状态的操作来指定。

Ⅳ. FileSystem class

class org.apache.hadoop.fs.FileSystem

抽象FileSystem类是访问Hadoop文件系统的原始类;所有hadoop支持的文件系统都有非抽象的子类。

所有采用此接口路径的操作都必须支持相对路径。在这种情况下,它们必须相对于setWorkingDirectory()定义的工作目录进行解析。

因此,对于所有客户机,我们还添加了状态组件PWD的概念:它表示客户机的当前工作目录。对此状态的更改不会反映在文件系统本身:它们对于客户机的实例是唯一的。

Implementation Note(实施说明):静态文件系统get(URI uri,Configuration conf)方法可能返回文件系统客户机类的一个预先存在的实例,该类也可能在其他线程中使用。ApacheHadoop附带的FIleSystem的实现(implementations)没有试图同步对工作目录字段的访问。


Invariants

有效文件系统的所有要求都被认为是隐含的前提条件和后置条件:有效文件系统上的所有操作都必须生成一个同样有效的新文件系统。


Predicates and other state access operations

boolean exists(Path p)

def exists(FS, p) = p in paths(FS)

boolean isDirectory(Path p)

def isDirectory(FS, p) = p in directories(FS)

boolean isFile(Path p)

def isFile(FS, p) = p in files(FS)

FileStatus getFileStatus(Path p)

获取路径的状态

Preconditions

if not exists(FS, p) : raise FileNotFoundException

Postconditions

result = stat: FileStatus where:
    if isFile(FS, p) :
        stat.length = len(FS.Files[p])
        stat.isdir = False
        stat.blockSize > 0
    elif isDir(FS, p) :
        stat.length = 0
        stat.isdir = True
    elif isSymlink(FS, p) :
        stat.length = 0
        stat.isdir = False
        stat.symlink = FS.Symlinks[p]
    if inEncryptionZone(FS, p) :
        stat.isEncrypted = True
    else
        stat.isEncrypted = False
 

Path  getHomeDirectory()

函数getHomeDirectory返回文件系统和当前用户帐户的主目录。

对于某些文件系统,路径是["/", "users", System.getProperty("user-name")]。

但是,对于HDFS,用户名来自使用HDFS对客户机进行身份验证的凭据。这可能与本地用户帐户名称不同。

文件系统的职责是确定调用者的实际主目录。

Preconditions

Postconditions

result = p where valid-path(FS, p)

不要求在调用方法时存在路径,或者如果存在路径,则不要求路径指向目录。然而,代码倾向于假定not isFile(FS, getHomeDirectory())在某种程度上可以防止后续代码失败。

实施说明:

  • FTPFileSystem从远程文件系统查询值,如果存在连接问题,则可能会出现RuntimeException或其子类 失败。执行操作的时间不受限制。

FileStatus[]  listStatus(Path path, PathFilter filter)

列出路径path下的条目。如果路径引用一个文件,并且过滤器接受它,那么该文件的FileStatus条目将在一个单元素数组中返回。

如果路径引用一个目录,调用将返回一个列表,其中包含筛选器接受的所有直接子路径,并且不包括目录本身。

PathFilter filter是一个过滤器,当路劲path满足此过滤器的条件时,它的accept(path)方法返回true。

前提条件

路径path必须存在:

if not  exists(FS,  paht) : raise FileNotFoundException

后置条件

if  isFile(FS, path) and filter.accept(path) :

  result = [ getFileStatus(path) ]

elif isFile(FS, path) and not filter.accept(P) :

  result = []

elif isDir(FS, path) :

  result = [

     getFileStatus(c)  for c in children(FS, path) if filter.accepts(c)

  ]

Implicit invariant(隐式不变量): 通过listStatus()检索的子文件状态的内容等于从getFileStatus()调用到相同路径的内容

forall fs in listStatus(path) :

  fs == getFileStatus(fs.path)

Ordering of results(结果排序): 不保证对列出的条目进行排序。当HDFS当前返回一个字母数字排序的列表时,Posix readdir()和JavaFile.listFiles()API调用都不定义返回值的任何排序。对结果要求统一排序顺序的应用程序必须自己执行排序。

Atomicity and Consistency(原子性和一致性)

当listStatus()操作返回给调用方时,无法保证响应中包含的信息是最新的。详细信息可能过时,包括任何目录的内容、任何文件的属性以及提供的路径的存在。

在评估过程中,目录的状态可能会更改。

  • 在创建路径P的条目之后,以及对文件系统进行任何其它更改之前,listStatus(P)必须找到该文件并返回其状态。
  • 在删除路径P的条目之后,以及对文件系统进行任何其它更改之前,listStatus(P)必须抛出FileNotFoundException。
  • 在创建路径P的条目之后,以及对文件系统进行任何其它更改之前,listStatus(parent(P))的结果应该包括getFileStatus(P)的值。
  • 在删除路径P的条目之后,以及对文件系统进行任何其它更改之前,listStatus(parent(P))的结果应该包括getFileStatus(P)的值。

这不是理论上的可能性,当一个目录包含数千个文件时,可以在HDFS中观察到。

考虑目录“/d”,内容如下:

a

part-0000001

part-0000002

...

part-9999999

如果文件的数量使得HDFS在每个响应中返回一个部分列表,那么,当列表listStatus("/d")和操作rename("/d/a", "/d/z")同时发生时,结果可能是下面其一:

[a, part-0000001, ... , part-9999999]
[part-0000001, ... , part-9999999, z]
[a, part-0000001, ... , part-9999999, z]
[part-0000001, ... , part-9999999]

虽然这种情况可能很少见,但也可能发生。在HDF中,这些不一致的视图只有在列出包含许多子目录的目录时才可能出现。其他文件系统可能具有更强的一致性保证,或者更容易返回不一致的数据。

FileStatus[]  listStatus(Path path)

这完全等同于listStatus(Path, DEFATUL_FILTER),其中对于所有的路径DEFAULT_FILTER.accept(path) = True。

原子性和一致性约束与listStatus(Path, DEFAULT_FILTER)相同。

FileStatus[]  listStatus(Path[]  paths, PathFilter filter)

枚举在传入目录列表中找到的所有文件,对每个文件调用listStatus(path,filter)。

与listStatus(path, filter)一样,结果可能不一致。即:文件系统的状态在操作期间发生了更改。

对于路径是否按特定的顺序列出没有任何保证,只保证它们必须全部列出,并且在列出时存在。

前提条件

所有路径都必须存在。不需要唯一性。

forall  p  in  paths :

  exists(fs, p)  else  raise  FileNotFoundException

后置条件

结果是一个数组,其条目包含在路径列表中找到的每个状态元素,而没有其他元素。

result = [listStatus(p, filter) for p in paths]

实现可以合并重复条目;和/或通过重新编码重复路径和只列出一次条目来优化操作。

FileStatus[]  listStatus(Path[] paths)

默认实现遍历列表;它不执行任何优化。

原子性和一致性约束与listStatus(Path, PathFilter)相同。

FileStatus[]  listStatus(Path[] paths)

枚举在传入目录列表中找到的所有文件,对每个文件调用listStatus(path,DEFATUL_FILTER),其中DEFATUL_FILTER接受所有的路径名。

RemoteIterator[LocatedFileStatus] listLocatedStatus(Path path, PathFilter filter)

返回一个迭代器,枚举路径下的LocatedFileStatus条目。这类似于listStatus(Path),只是返回值是FileStatus的LocatedFileStatus子类的一个实例,并且不是返回整个列表,而是返回一个迭代器。

这实际上是一个受保护的方法,由listLocatedStatus(路径路径)直接调用。对它的调用可以通过分层文件系统(如FilterFileSystem)进行委托,因此必须将其实现视为强制的,即使listLocatedStatus(Path Path)是以不同的方式实现的。有公开的JIRAs提议将这一方法公之于众;这可能在未来发生。

迭代器不需要提供路径子条目的一致视图。默认实现确实使用listStatus(Path)列出其子级,并且已经记录了其一致性约束。其他实现可能更动态地执行枚举。例如,获取子条目的窗口子集,从而避免构建大型数据结构和传输大型消息。在这种情况下,对文件系统的更改更有可能变得可见。

调用方必须假定,如果在此调用返回和完全执行迭代之间对文件系统进行更改,迭代操作可能会失败。

前提条件

路径path必须存在:

exists(FS, path) : raise FileNotFoundException

后置条件

该操作生成结果集resultset,等于listStatus(path, filter)的结果:

if isFile(FS, path) and filter.accept(path) :
  resultset =  [ getLocatedFileStatus(FS, path) ]

elif isFile(FS, path) and not filter.accept(path) :
  resultset = []

elif isDir(FS, path) :
  resultset = [
    getLocatedFileStatus(FS, c)
     for c in children(FS, path) where filter.accept(c)
  ]

操作getLocatedFileStatus(FS, path: path): LocatedFileStatus定义为LocatedFileStatus实例ls的生成器,其中:

fileStatus = getFileStatus(FS, path)

b1 = getFileBlockLocations(FS, path, 0, fileStatus.len)

locatedFileStatus = new LocatedFileStatus(fileStatus, b1)

迭代器中返回的结果集元素的顺序未定义。

原子性和一致性约束与listStatus(Path、PathFilter)相同。

RemoteIterator[LocatedFileStatus]  listLocatedStatus(Path, path)

等于listlocatedStatus(path, DEFAULT_FILTER),其中DEFAULT_FILTER接受所有的路径名。

RemoteIterator[LocatedFileStatus] listFiles(Path path, boolean recursive)

在目录中/下的所有文件上创建一个迭代器,可能递归到子目录中。此操作的目标是通过减少在单个RPC调用中必须收集的数据量,允许文件系统更高效地处理大型递归目录扫描。

前提条件

exists(FS, path) else raise FileNotFoundException

后置条件

结果是一个iterator,它的iterator.next()调用序列的输出可以定义为集合 iteratorset:

if not recursive:

  iteratorset == listStatus(path)

else:

  iteratorset =[

    getLocateFileStatus(FS, d)

        for d in descendants(FS, path)

  ]

函数getLocatedFileStatus(FS, d)在listLocatedStatus(Path, PathFilter)中定义。

原子性和一致性约束与listStatus(Path、PathFilter)相同。

BlockLocation[] getFileBlockLocations(FileStatus f, int s,  int  l)

前提条件

if s<0  or  l<0 : raise {HadoopIllegalArgumentException, InvalidArgumentException}
  • HDFS为无效的偏移量或长度抛出HadoopIllegalArgumentException;这个扩展了IllegalArgumentException。

后置条件

如果文件系统知道位置,它必须返回可以找到范围[s:s+l]中数据的块位置列表。

if f == null :

     result = null

elif f.getLen() <= s:

     result = []

else result = [ locations(FS, b) for b in blocks(FS, p, s, s+l)]

             where

def locations(FS, b) = a list of all locations of a block in the filesystem    (文件系统中一个块的所有位置列表)

def blocks(FS, p, s, s + l) = a list of the blocks containing data(FS, path)[s:s+1]     (包含数据data(FS, path)[s:s+1]的块列表)

注意:由于如果isDir(FS, f)那么length(FS, f)定义为0,则目录上getFileBlockLocations()的结果是[]。

如果文件系统不知道位置,它应该返回

[

  BlockLocation(["localhost:50010"],

                          ["localhost"],

                          ["/default/localhost"]

                           0, f.getLen())

];

             *Hadoop 1.0.3中的bug意味着必须提供与集群拓扑相同数量元素的拓扑路径,因此文件系统应返回该“/default/localhost”路径。虽   然这已不再是一个问题,但约定一般仍予以保留。 

BlockLocation[]  getFileBlockLocations(Path P, int S, int L)

前提条件

if  p == null : raise NullPointerException

if not exists(FS, p) : raise FileNotFoundException

后置条件

result = getFileBlockLocations(getFileStatus(FS, P), S, L)

long getDefaultBlockSize()

获取文件系统的“默认”块大小。这通常在拆分计算期间用于在一组工作进程中以最佳方式划分工作。

前提条件

后置条件

result = interger > 0

虽然这个结果没有定义最小值,因为它用于在作业提交期间对工作进行分区,但是如果块大小过小,就会导致分区工作负载严重分区,甚至会导致JobSubmissionClient在计算分区时内存耗尽。

任何实际上没有将文件分割成块的文件系统都应该为此返回一个数字,从而实现有效的处理。文件系统可能会使这个用户可配置(S3和Swift文件系统客户机这样做)。

long getDefaultBlockSize(Path p)

获取路径的默认块大小,即即将对象写入文件系统中的路径时使用的块大小。

前提条件

后置条件

result = integer >=0

此操作的结果通常与getDefaultBlockSize()相同,不检查给定路径是否存在。

支持挂载点的文件系统对于不同的路径可能有不同的默认值,在这种情况下,应该返回目标路径的特定默认值。

如果路径不存在,则不是错误:必须返回文件系统该部分的默认值/建议值。

long  getBlockSize(Path p)

这个方法完全等价于查询getFileStatus(p)中返回的FileStatus结构的块大小。为了鼓励用户对getFileStatus(p)进行一次调用,然后使用结果检查文件的多个属性(例如长度、类型、块大小),不推荐使用这种方法。如果查询多个属性,这将成为一项重要的性能优化—减少文件系统上的负载。

前提条件

if not exists(FS, ) : raise FileNotFoundException

后置条件

if  len(FS, P) > 0 : getFileStatus(P).getBlockSize > 0

result = getFileStatus(P).getBlockSize()

             1. 此操作的结果必须与getFileStatus(P). getblocksize()的值相同。

             2. 通过推断,任何长度大于0的文件都必须大于0。


State Changing Operations(状态更改操作)

boolean mkdirs(Path p, FsPermission permission)

创建目录及其所有父目录

前置条件

if exists(FS, p)  and  not  isDir(FS, p) :

   raise  [ParentNotDirectoryException, FileAlreadyExistsException, IOException]

后置条件

FS'  where  FS'.Directories  =  FS.Directories + [p] + ancestors(FS, p)

result = True

             文件系统目录、文件和符号链接的条件独占性要求必须保持不变。    

             路径和目录创建的存在性和类型 的探测必须是原子性的。包括mkdirs(parent(F))在内的组合操作可以是原子性的。

             即使没有创建新目录(这是在HDFS中定义的),返回值也始终为真。

实施说明Local FileSystem

如果在存在且是文件的路径上调用mkdirs(p)),则本地文件系统不会引发异常。相反,该操作返回false。

if  ifFile(FS, p) :

    FS' = FS

    result =False

FSDataOutputStream  create(Path, ...)

FSDataOutStream create(

             FsPermission  permission,

             boolean  overwrite,

             int  bufferSize,

             short  replication,

             long  blockSize,

             Progressable  process)  throws  IOException;

前提条件

不覆盖创建的文件不能存在:

if not overwrite and isFile(FS, p) : raise FileAlreadyExistsException

写入或覆盖目录必须失败。

if isDir(FS, p) : raise {FileAlreadyExistsException, FileNotFoundException, IOException}

文件系统可能会因为其他原因拒绝请求,比如FS是只读的(HDFS)、块大小低于允许的最小值(HDFS)、复制计数超出范围(HDFS)、超出名称空间或文件系统的配额、保留名称,等等。所有拒绝都应该是IOException或其子类,并且可以是RuntimeException或子类。例如,HDFS可能会引发InvalidPathException。

后置条件

FS'  where :

        FS'.File'[p]  ==  []

        ancestors(p)  is-subset-of  FS'.Directories'

result = FSDataOutputStream

更新(有效)的文件系统必须包含mkdirs(parent(p))创建的路径的所有父目录。

其结果是FSDataOutputStream,通过它的操作可以生成由FS.Files[p]的值更新的新文件系统状态。

实施说明

  • 一些实现将创建拆分为检查实际创建中存在的文件。这意味着操作不是原子的:如果文件是由两个测试之间的另一个客户机创建的,则使用overwrite==true创建文件的客户机可能会失败。
  • 在输出流close()操作完成之前,S3N、S3A、Swift和可能的其他对象存储当前不会更改FS状态。这可能是一个bug,因为它允许>1客户端使用overwrite==false创建一个文件,并且可能混淆文件/目录逻辑。
  • 当试图在目录上创建文件时,本地文件系统会引发一个fileNotFoundException,因此它被列为 当此前提条件失败时 可能引发的异常。
  • 不包括:符号链接。symlink的解析路径用作create()操作的最终路径参数。

FSDataOutputStream  append(Path p, int bufferSize, Progressable  progress)

实现可能引发UnsupportedOperationException。

前提条件

if not  exists(FS, p) : raise FileNotFoundException

if not isFile(FS, p) : raise [FileNotFoundException, IOException]

这是一个关键的前提条件。一些文件系统的实现(例如对象存储)可以通过将HTTP GET操作延迟到返回的FSDataInputStream上的第一个read()来缩短一次往返。然而,许多客户机代码确实依赖于open()操作时执行的生存检查。实现必须在创建时检查文件是否存在。这并不意味着该文件及其数据在以下read()或任何后续读取时仍然存在。

后置条件

result = FSDataInputStream(0, FS.Files[p])

结果提供对FS.Files[p]定义的字节数组的访问;在调用open()操作时,该访问是否是对内容的访问,或者它是否以及如何在FS的后续状态中获取对该数据的更改,这些都是实现细节。

对于操作的本地调用者和远程调用者,结果必须相同。

HDFS实施说明:

  1. 当试图遍历符号链接时,HDFS可能会抛出Unresolvedpathexception。
  2. 如果元数据中存在路径,但无法找到其块的副本,则HDFS抛出IOException(“Cannot open filename "”+ src);-FileNotFoundException似乎更准确和有用。

boolean   delete(Path p, boolean  rescursive)

删除路径,无论是文件、符号链接还是目录。递归标志指示是否应该进行递归删除——如果未设置,则无法删除非空目录。

除了根目录的特殊情况外,如果此API调用成功完成,则路径末尾没有任何内容。即:结果是期望的。返回标志只是告诉调用者是否对文件系统的状态进行了任何更改。

注意:此方法的许多使用都围绕着它,检查返回值是否为假,如果是,则引发异常。例如:

if  (!fs.delete(path, true))  throw  new  IOException("Could not delete " + path );

 不需要这种模式。代码应该只调用delete(path, recursive)并假设目标不再存在——除非在根目录的特殊情况下,根目录将始终存在(有关根目录的特殊覆盖,请参阅下面的内容)。

前提条件

不能删除由子目录且recursive == False的目录。

if  isDir(FS, p) and not  recursive and (children(FS, p)  != {}) : raise  IOException

(HDFS在这里引发PathIsNotEmptyDirectoryException。)

后置条件

Nonexist path(不存在的路径)

如果文件不存在则文件状态不会修改

if not  exists(FS, p) :

    FS'  =  FS

    result = False

结果应该为False,表示没有删除任何文件。

简单文件

删除引用文件的路径,返回值:True

if  isFile(FS, p) :

    FS' = (FS.Directories,  FS.Files - [p], FS.Symlinks)

    result = True

空根目录,recursive==false

删除空根不会更改文件系统状态,可能返回true或false。

if  isDir(FS, p)  and  Root(p)  and  children(FS, p)  == {} :

   FS' = FS

   result  = (undetermined)

删除根目录的尝试没有一致的返回代码。实现应该返回true;

这避免了检查错误返回值是否反应过度的代码。

空(非根)目录 recursive==false

删除非根目录的空目录将从FS中删除路径并返回true。

if  isDir(FS, p)  and  not isRoot(p)  and  children(FS, p) == {} :

    FS' = (FS.Directories - [p], FS.Files, FS.Symlinks)

    result = True

递归删除非空根目录

删除具有子目录且 recursive==True 的根路径可以做两件事之一。

  1. POSIX模型假定,如果用户拥有删除所有内容的正确权限,那么他们可以自由删除这些内容(从而导致一个空文件系统)。                     

    if  isDir(FS, p)  and  isRoot(p)  and  recursive :

        FS'  =  ({["/"]},  {},  {},  {})

         result = True

  2. HDFS从不允许删除文件系统的根目录。

HDFS有保护目录的概念,在选项fs.protected.directories中声明。任何删除此类目录或其父目录的尝试都会引发AccessControlException。因此,如果存在受保护的目录,任何删除根目录的尝试都将导致引发此类异常。

本规范不建议采取任何具体措施。但是,请注意,POSIX模型假定存在权限模型,这样普通用户就没有删除该根目录的权限;只有系统管理员才能执行该操作。

任何与缺乏这种安全模型的远程文件系统交互的文件系统客户机,都可能拒绝删除(“/”,true)调用,因为它太容易丢失数据。

非根目录的递归删除

删除有子目录且recursive==true的非根路径将删除该路径和所有后代

if  isDir(FS, p)  and not  isRoot(p)  and  rescursive :

    FS'  where  :

            not  isDir(FS', p) 

            and  forall  d  in  descendants(FS, p):

                    not  isDir(FS', d)

                    not  isFile(FS', d)

                    not  isSymlink(FS', d)

    result  =  True

原子性

  • 删除文件必须是原子操作。
  • 删除空目录必须是原子操作。
  • 目录树的递归删除必须是原子的。

实施说明

  • 对象存储和其它模拟目录树的非传统文件系统,通常将delete()实现为递归列表和逐项删除操作。对象存储和其他模拟目录树的非传统文件系统,通常将delete()实现为递归列表和逐项删除操作。

boolean  rename(Path src,  Path  d)

就其规范而言,rename()是文件系统中最复杂的操作之一。

就其实现而言,对于何时返回错误与引发异常,它是最模糊的。

重命名包括目标路径的计算。如果目标存在并且是目录,则重命名的最终目标将成为目标+源路径的文件名。

let  dest  =  if (isDir(FS, src)  and  d != src) :

                  d + [filename(src)]

      else :

              d

前提条件

所有对目标路径的检查必须在最终dest路径计算完成之后进行。

源路径src必须存在:

exists(FS, src)  else  raise  FileNotFoundException

dest不能是src的后代:

if  isDescendant(FS, src, dest) :raise  IOException

这隐式的涵盖了 isRoot(FS, src)的特殊情况。

dest必须是根目录,或具有存在的父目录:

isRoot(FS, dest)  or  exists(FS, parent(dest))  else  raise  IOException

目标的父路径不能是文件:

if  isFile(FS, parent(dest))  :  raise IOException

这隐式地涵盖了父代的所有祖先。

在目标路径的末端不能是一个存在的文件:

if isFile(FS, dest)  : raise  FileAlreadyExistsException,  IOException

后置条件

将目录重命名到其本身是无操作(no-op);没有指定返回值。

在POSIX中,结果为False;在HDFS中,结果为True。

if  isDir(FS, src)  and  src  ==  dest :

   FS'  = FS

   result = (undefined)

 

将文件重命名为自身

重命名文件本身是一种无操作(no-op);结果是真的。

if  isFile(FS, src)  and  src == dest :

    FS' = FS

    result = True

Renaming a file onto a nonexistent path(将文件重命名为不存在的路径 )

重命名目标为目录的文件会将文件作为目标目录的子级移动,并保留源路径的文件名元素。

if  isFile(FS, src)  and  src  !=  dest :

    FS'  where :

            not  exists(FS', src) 

            and  exists(FS', dest)

            and  data(FS', dest) ==  data(FS, dest)

    result =True

将目录重命名为目录

如果src是一个目录,那么它的所有子级都将存在于dest下,而路径src及其后代将不再存在。dest下的路径名称将与src下的路径名称相匹配,内容也一样:

if isDir(FS, src) isDir(FS, dest) and src != dest :
    FS' where:
        not exists(FS', src)
        and dest in FS'.Directories]
        and forall c in descendants(FS, src) :
            not exists(FS', c))
        and forall c in descendants(FS, src) where isDir(FS, c):
            isDir(FS', dest + childElements(src, c)
        and forall c in descendants(FS, src) where not isDir(FS, c):
                data(FS', dest + childElements(s, c)) == data(FS, c)
    result = True

重命名为父路径不存在的路径

not  exists(FS, parent(dest))

这里没有一致的行为。

HDFS

结果不会更改文件系统状态,返回值为false。

FS'  =  FS ; result = False

Local Filesystem, S3N

结果是一个普通的重命名,带有目标的父目录也存在的额外(隐式)特性。

exists(FS', parent(dest))

Other Filesystems (including Swift)

其他文件系统严格拒绝该操作,引发FileNotFoundException

并发性要求

  • rename()的核心操作—将文件系统中的一个条目移动到另一个条目必须是原子的。一些应用程序依赖于这种方式来协调对数据的访问。
  • 一些文件系统实现在重命名之前和之后对目标文件系统执行检查。其中一个例子是ChecksumFileSystem,它提供对本地数据的校验和访问。整个序列可能不是原子的。

实施说明

打开文件以供读取、写入或追加

没有指定在打开的文件上rename()的行为:无论是否允许,以后尝试读取或写入打开流时会发生什么。

将目录重命名为其自身

未指定将目录重命名为其自身的返回代码。

目标存在并且是一个文件

在现有文件上重命名文件被指定为失败,引发异常。

  • 本地文件系统:重命名成功;目标文件有源文件替代。
  • HDFS:重命名失败,不引发异常。相反,该方法调用只返回false。

缺少源文件

如果源文件src不存在,应该抛出FileNotFoundException。

HDFS失败而不引发异常;rename()只返回false。

FS' = FS

result = false

这里不应该将HDFS的行为视为要复制的特性。FileContext显式地更改了行为以引发异常,并且将该操作重新安装到DFSFileSystem实现中是一个值得讨论的问题。

void  concat(Path p, Path  source[])

将多个块连接在一起以创建单个文件。这是一个很少使用的操作,目前仅由HDFS实现。

实现可能引发UnsupportedOperationException。

前提条件

if not exists(FS, p) : raise  FileNotFoundException

if  source == [] : raise IllegalArgumentException

所有源必须在同一目录中:

for  s  in  sources : if parent(S)  != parent(p)  raise IllegalArgumentException

所有块大小必须与目标块大小匹配:

for  s in  sources:  getBlockSize(FS, S) == getBlockSize(FS, p)

没有重复的路径:

not  (exists p1, p2  in (sources + [p]) where p1  == p2)

HDFS:除最后一个文件外,所有源文件都必须是完整的块:

for  s  in  (sources[0:length(sources)-1] + [p]): 

      (length(FS, s)  mod  getBlockSize(FS, p) ) == 0

后置条件

FS'  where :

      (data(FS' , T) = data(FS, T)  + data(FS, sources[0]) + ... + data(FS, srcs[length(srcs)-1]))

       and  for  s in srcs: not  exists(FS', S)

HDFS的限制可能是它如何通过更改inode引用将它们按顺序连接在一起来实现concat的实现细节。由于Hadoop核心代码库中没有其他文件系统实现此方法,因此无法区分实现细节和规范。

boolean  truncate(Path p, longnewLength)

把文件p截断为指定的newlength。

实现可能会引发UnsupportedOperationException。

前提条件

if  not exists(FS, p)  : raise  FileNotFoundException

if  isDir(FS, p)  : raise  [FileNotFoundException, IOException]

if  newLength < 0 || newLength > len(FS.FIles[p])  :  raise  HadoopIllegalArgumentException

HDFS: 源文件必须关闭。无法对打开以进行写入或追加的文件执行截断。

后置条件

FS'  where :

        len(FS.Files[p]  =  newLength)

返回:如果截断完成,可以立即打开文件进行附加,则返回true,否则false。

HDFS:HDFS返回false,表示已经启动了调整最后一个块的长度的后台进程,客户机应该等待它完成后才能继续进行进一步的文件更新。

并发性

如果在truncate()发生时输入流是打开的,则与被截断的文件部分相关的读取操作的结果是未定义的。


interface RemoteIterator

RemoteIterator接口用作与java.util.Iterator等效的远程访问,允许调用者遍历远程数据元素的有限序列。

核心区别在于

  1. 不支持迭代器的可选void remove()方法。
  2. 对于那些受支持的方法,可能会引发IOException异常。

public  interface  RemoteIterator<E>  {

    boolean  hasNext()  throws  IOException;

    E  next()  throws  IOException;

}

该接口的基本观点是,hasNext()为true意味着next()将成功返回列表中的下一个条目:

while  hasNext() : next()

同样,成功调用next()意味着如果在调用next()之前调用hasNext(),那么它将是正确的。

boolean  elementAvailable = hasNext();

try {

   next();

   assert  elementAvailable;

}  catch (NoSuchElementException e) {

   assert  !elementAvailable

}

next()运算符必须遍历可用结果列表,即使没有调用hasNext()。

也就是说,可以通过仅在引发NoSuchElementException异常时终止的循环枚举结果。

try {

   while  (true) {

        process(iterator.next());

   }

}  catch  (NoSuchElementException  ignored) {

   //  the end of list has been reached   (已经到达列表末尾)

}

迭代的输出等价于循环

while  (iterator.hasNext()) {

    process(iterator.next());

}

由于引发异常在JVMs中是一项昂贵的操作,因此while(hasNext())循环选项更有效。(有关此主题的讨论,请参见 Concurrency and the Remote Iterator)。

接口的实现者必须支持两种形式的迭代;测试的作者应该验证两种迭代机制是否都有效。Hadoop代码库中接口的所有实现都满足这个要求;所有的使用者都认为它是有效的。

boolean  hasNext()

当且仅当对next()的后续单个调用返回元素而不是引发异常时 返回true。

前提条件

后置条件

result = True ==> next()  will succed.

result =False ==> next() will raise an exception

多个对hasNext()的调用(没有任何中间的next()调用)必须返回相同的值。

boolean  has1 = iterator.hasNext();

boolean  has2 = iterator.hasNext();

assert  has1  == has2 

 

E  next()

返回迭代中的下一个元素。

前提条件

hasNext()  else  raise  java.util.NoSuchElementException

后置条件

result  = the next element in the iteration

重复调用next()返回序列中的后续元素,直到返回整个序列。

Concurrency  and  the  Remote  Iterator

在文件系统APIs中,RemoteIterator的主要用途是列出(可能是远程)文件系统上的文件。这些文件系统总是并发地访问;在hasNext()探测和调用next()调用之间,文件系统的状态可能会发生变化。

因此,通过RemoteIterator进行的健壮迭代将捕获并丢弃在进程中引发的NoSuchElementException异常,这可以通过上面的while(true)迭代示例完成,也可以通过具有外部try/catch子句的hasNext()/next()序列来捕获NoSuchElementException以及其它在失败期间引发(例如,一个FileNotFoundException)的异常。

try {

   while  (iterator.hasNext()) {

       process(iterator.hasNext());

   }

}  catch (NoSuchElementException ignored) {

   //  the  end of  the  list  has  been  reached

}

值得注意的是,这不是在Hadoop代码库中完成的。这并不意味着不建议使用健壮的循环——更重要的是,在这些循环的实现过程中没有考虑并发性问题。


interface  StreamCapabilities

StreamCapability提供了一种以编程方式查询 OutputStream、InputStream或其它文件系统类支持的 功能的方法。

public  interface  StreamCapabilities {

   boolean  hasCapability(String  capability);

}

boolean  hasCapability(capability)

如果OutputStream、InputStream或其它文件系统类具有所需的功能,则返回true。

调用方可以使用字符串值查询流的功能。下面是一个可能的字符串值表:

String Constant(常数) Implements Description
hflush HFLUSH Syncable 刷新客户端用户缓冲区中的数据。在此调用返回后,新读者将看到数据。
hsync HSYNC Syncable 将客户机的用户缓冲区中的数据一直刷新到磁盘设备(但是磁盘的缓存中可能有它)。类似于POSIX fsync。
in:readahead READAHEAD CanSetReadahead 在输入流上设置预读。
dropbehind DROPBEHIND CanSetDropBehind 删除缓存。
in:unbuffer UNBUFFER CanUnbuffer 减少输入流上的缓冲。

V. FSDataInputStream  class

class org.apache.hadoop.fs.FSDataInputStream

class  FSDataInputStream  extends  DataInputStream

FSDataInputStream的核心行为是由java.io.DataInputStream定义的,带有向系统添加关键假设的扩展。

  1. 源文件系统是本地或远程文件系统。
  2. 正在读取的流引用有限字节数组。
  3. 在读取过程中,数据的长度不会改变。
  4. 数据的内容在处理过程中不会改变。
  5. 在读取过程中,源文件仍然存在。
  6. 调用方可以使用Seekble.Seek()在字节数组内进行偏移,将来的读取将从该偏移开始。
  7. 向前和向后查找的成本很低。
  8. 流实现不需要线程安全。
  9. 但是,如果一个流实现了PositionedReadable,“positioned reads”必须是线程安全的。

文件通过FileSystem.open(p)打开,如果成功,返回:

result = FSDataInputStream(0, FS.Files[p])

输入流可以建模为:

FSDIS = (pos, data[], isOpen)

具有访问功能:

pos(FSDIS)

data(FSDIS)

isOpen(FSDIS)

隐式不变量:数据流的大小等FfileSystem.getFileStatus(Path p)返回的文件大小。

forall   p  in  dom(FS.Files)

len(data(FSDIS))  ==  FS.getStatus(p).length

Closeable.close()

java.io.Closeable的语义在JRE中的接口定义中定义。

操作必须是等幂的;以下序列不是错误:

FSDIS.close();

FSDIS.close();

实施说明

  • 实现应该对失败具有健壮性如果内部流已关闭,则应首先检查它是否为空。
  • 在此操作期间,实现不应引发IOException异常(或任何其他异常)。客户机应用程序经常忽略这些,或者可能会意外失败。

后置条件

FSDIS' = ((undefined), (undefined), False)

Seekable.getPos()

返回当前位置。输入流关闭时的结果未定义。

前提条件

isOpen(FSDIS)

后置条件

result = pos(FSDIS)

InputStream.read()

返回当前位置的数据。

  1. 当输入流关闭时,实现应该失败。
  2. read()完成所需的时间没有限制。

前提条件

isOpen(FSDIS)

后置条件

if  (pos < len(data)) :

    FSDIS'  =  (pos +1,  data, True)

    result = data[pos]

else

    result = -1

InputStream.read(buffer[], offset, length)

从偏移量offset开始,将length字节的数据读取到目标缓冲区。数据源是输入流的当前位置,正如在pos中隐式设置的那样。

前提条件

isOpen(FSDIS)
buffer != null else raise NullPointerException
length >= 0
offset < len(buffer)
length <= len(buffer) - offset
pos >= 0 else raise EOFException, IOException

在前提条件失败时可能引发的异常是

InvalidArgumentException
ArrayIndexOutOfBoundsException
RuntimeException

并非所有文件系统都检查isOpen状态。

后置条件

if length == 0 :
  result = 0

else if pos > len(data):
  result = -1

else
  let l = min(length, len(data)-length) :
    buffer' = buffer where forall i in [0..l-1]:
       buffer'[o+i] = data[pos+i]
    FSDIS' = (pos+l, data, true)
    result = l

java.io API声明,如果要读取的数据量(即length),那么调用必须阻塞,直到可用数据量大于零,也就是说,直到有一些数据为止。

当缓冲区已满时,不需要返回调用,或者直到流中没有剩余的数据时才真正阻塞调用。

也就是说,l不是简单地定义为min(length, len(data)-length),而是严格地定义为1范至min(length, len(data)-length)围内的整数。尽管调用者可能希望尽可能多地填充缓冲区,但在实现的规范中,总是返回一个较小的数字,可能只有一个字节。

关键是,除非目标缓冲区大小为0,否则调用必须阻塞,直到至少返回一个字节。因此,对于长度大于零的任何数据源,重复调用此read()操作将最终读取所有数据。

Seekable.seek(s)

前提条件

并非所有子类都实现查找(seek)操作:

supported(FSDIS, Seekable.seek) else raise [UnsupportedOperationException, IOException]

如果支持该操作,则应打开该文件:

isOpen(FSDIS)

有些文件系统不执行此检查,而是依赖read()协议拒绝对关闭的流(例如RawLocalFileSystem)进行读取。

seek(0)必须始终成功,因为查找位置必须为正且小于流的长度:

s > 0 and ((s==0) or ((s < len(data)))) else raise [EOFException, IOException]

如果不满足此条件,则某些文件系统不会引发异常。相反,它们在任何read()操作上返回-1,其中,在读取时,len(data(FSDIS)) < pos(FSDIS)。

后置条件

FSDIS' = (s, data, True)

有一个隐式不变量:对当前位置的搜索是一个  非选项(no-op)。

seek(getPos())

实现可以识别此操作并绕过所有其他前提检查,从而保持输入流不变。

Seekable.seekToNewSource(offset)

只有当文件系统支持一个文件的多个副本,并且偏移量offset处有一个以上的数据副本时,这才相关。

前提条件

并非所有子类都实现了此操作,而是引发异常或返回False。

supported(FSDIS, Seekable.seekToNewSource) else raise [UnsupportedOperationException, IOException]

示例: CompressionInputStream (压缩输入流), HttpFSFileSystem

如果支持,文件必须打开:

isOpen(FSDIS)

后置条件

没有实现此操作的大多数子类都会失败。

if not supported(FSDIS, Seekable.seekToNewSource(s)):
    result = False

示例:RawLocalFileSystem , HttpFSFileSystem

如果支持该操作,并且有新的数据位置:

    FSDIS' = (pos, data', true)
    result = True
 

新数据是原始数据(或其更新版本,如下面一致性部分所述),但包含offset处数据的块来自不同的副本。如果没有其他副本,则不会更新FSDIS;响应表明:

result = False

在测试方法之外,此方法的主要用途是 {{FSInputChecker}}类中,它可以通过尝试从其他地方获取数据来响应读取中的校验和错误。如果可以找到新的源,它将尝试重新读取并重新检查文件的该部分。

interface  PositionedReadable

PositionedReadable操作提供"positioned reads"("pread" --定位读取)。它们提供将数据从数据流中的指定位置读取到缓冲区中的能力;

定位读取等同于在指定偏移量处Seekable.seek ,然后是 InputStream.read(buffer[], offset, length),只有一个方法调用,而不是seek后read,并且两个定位读取可以选择性地在 FSDataInputStream流的单个实例上并发运行。

接口声明定位读取线程安全(一些实现不遵循此保证)。

与流操作并发的任何位置读取运行--Seekable.seekSeekable.getPos() InputStream.read() — 比如,必须单独运行;不能有相互干扰。

并发的位置读取和流操作必须是可序列化的;一个可以阻塞另一个,以便它们串联运行,但是为了获得更好的吞吐量和“活跃性(liveness)”,它们应该并发运行。

给定两个并行位置读取,一个在pos1处读取len1到buffer dest1,另一个在pos2处读取len2到buffer dest2,并给定一个在查找pos3后并发的流读取运行,即使结果缓冲区恰好在底层流上重叠,也必须按如下方式填充:

// Positioned read #1
read(pos1, dest1, ... len1) -> dest1[0..len1 - 1] =
  [data(FS, path, pos1), data(FS, path, pos1 + 1) ... data(FS, path, pos1 + len1 - 1]

// Positioned read #2
read(pos2, dest2, ... len2) -> dest2[0..len2 - 1] =
  [data(FS, path, pos2), data(FS, path, pos2 + 1) ... data(FS, path, pos2 + len2 - 1]

// Stream read
seek(pos3);
read(dest3, ... len3) -> dest3[0..len3 - 1] =
  [data(FS, path, pos3), data(FS, path, pos3 + 1) ... data(FS, path, pos3 + len3 - 1]

注意,实现不一定是原子的;操作的中间状态(getPos()值的变化)可能是可见的。

Implementation preconditions

并不是所有的FSDataInputStream实现都支持这些操作。这些没有实现Seekable.seek()的实现不实现PositionedReadable接口。

supported(FSDIS, Seekable.seek) else raise [UnsupportedOperationException, IOException]

这可以被认为是显而易见的:如果流不能Seekable,则客户机无法查找位置。它也是使用Seekble.Seek()的基类实现的副作用。

隐式不变量:对于所有PositionedReadable操作,操作结束时pos值不变。

pos(FSDIS') ==pos(FSDIS)

Failture states

对于任何失败的操作,目标buffer的内容都是未定义的。实现可能会在报告失败之前覆盖部分或全部缓冲区。

int  PositionedReadable.read(position, buffer, offset, length)

将尽可能多的数据读取到分配给它的缓冲区空间中。

前提条件

position >= 0 else raise [EOFException, IOException, IllegalArgumentException, RuntimeException]
len(buffer) - offset >= length else raise [IndexOutOfBoundException, RuntimeException]
length >= 0
offset >= 0

后置条件

读取的数据量小于指定位置的可用数据的长度或量:

let available = min(length, len(data)-position)
buffer'[offset..(offset+available-1)] = data[position..position+available -1]
result = available
  1. 返回值-1表示流没有更多可用数据。
  2. length==0的隐式调用不读取任何数据;实现可以缩短操作并省略任何IO。在这种情况下,可以忽略对文件末尾的流的检查。
  3. 如果在读取操作期间发生IO异常,则缓冲区的最终状态为未定义。

void  PositionedReadable.readFully(position, buffer, offset, length)

将精确的length字节数据读取到缓冲区中,如果没有足够的可用数据,则会失败。

前提条件

position >= 0 else raise [EOFException, IOException, IllegalArgumentException, RuntimeException]
length >= 0
offset >= 0
len(buffer) - offset >= length else raise [IndexOutOfBoundException, RuntimeException]
(position + length) <= len(data) else raise [EOFException, IOException]

如果在读取操作期间发生IO异常,则buffer的最终状态为未定义。

如果输入流中没有足够的数据来满足请求,则buffer的最终状态是未定义的。

后置条件

来自偏移量offset的缓冲区由从position开始的数据填充。

buffer'[offset..(offset+length-1)] = data[position..(position + length -1)]

PositionedReadable.readFully(position, buffer)

它的语义完全等价于

readFully(position, buffer, 0, len(buffer))

也就是说,缓冲区完全由position处的输入源内容填充。


Consistency

  • 从FileSystem.open(p)提供的数据流FSDIS的所有本地和远程读卡器在打开时都将接收对FS.Files[p]数据的访问。
  • 如果在读取过程中更改了基础数据,则这些更改可能可见,也可能不可见。
  • 这些可见的变化可能部分可见。

At time  t0

FSDIS0 = FS'read(p) = (0, data0[])

At time t1

FS'  =  FS'  where  FS'.Files[p] = data1
从时间 t >= t1 开始,FSDIS0的值未定义。

它可能是不变的

FSDIS0.data == data0

forall l in len(FSDIS0.data):
  FSDIS0.read() == data0[l]

它可能会获取新的数据

FSDIS0.data == data1

forall l in len(FSDIS0.data):
  FSDIS0.read() == data1[l]

它可能是不一致的,因此,对偏移量的读取会从任一数据集中返回数据。

forall l in len(FSDIS0.data):
  (FSDIS0.read(l) == data0[l]) or (FSDIS0.read(l) == data1[l]))

也就是说,读取的每个值都可能来自原始或更新的文件。

在相同偏移量的重复读取时,也可能不一致,即t2>t1时:

r2 = FSDIS0.read(l)

当时间t3>t2时:

r3 = FSDIS0.read(l)

它可能是r3 != r2。(也就是说,一些数据可能是缓存的或复制的,并在后续读取时返回文件内容的不同版本)。 类似地,如果路径p上的数据被删除,那么在对FSDIS0执行读取操作期间,这个更改可能是可见的,也可能是不可见的。

Ⅵ. Testing with the Filesystem specification

Testing the Filesystem Contract

Running the tests

正常的Hadoop测试运行将测试那些可以通过本地文件系统进行本地测试的文件系统。这通常意味着file://及其底层的本地文件系统,以及hdfs://通过HDFS MiniCluster。

除非提供文件系统的远程服务器有特定的配置,否则将跳过其他文件系统。

必须在一个XML配置文件中定义这些文件系统绑定,通常是hadoop-common-project/hadoop-common/src/test/resources/contract-test-option.xml。此文件被排除在外,不应签入。

ftp://

在contract-test-options.xml中,文件系统名必须在属性fs.contract.test.fs.ftp中定义。然后必须提供连接到FTP服务器的特定登录选项。选项fs.contract.test.ftp.testdir中还必须提供测试目录的路径。这是进行操作的目录。

示例:

<configuration>
  <property>
    <name>fs.contract.test.fs.ftp</name>
    <value>ftp://server1/</value>
  </property>

  <property>
    <name>fs.ftp.user.server1</name>
    <value>testuser</value>
  </property>

  <property>
    <name>fs.contract.test.ftp.testdir</name>
    <value>/home/testuser/test</value>
  </property>

  <property>
    <name>fs.ftp.password.server1</name>
    <value>secret-login</value>
  </property>
</configuration>

swift://

OpenStack Swift登录细节必须在文件 /hadoop-tools/hadoop-openstack/src/test/resources/contract-test-options.xml中定义。不能使用标准的hadoop-common contract-test-options.xml资源文件,因为该文件不包含在hadoop-common-test.jar中。

在/hadoop tools/hadoop-openstack/src/test/resources/contract-test-options.xml中,必须在fs.contract.test.fs.swift属性中定义swift bucket名称,以及在其中发布bucket的特定swift服务提供者的登录详细信息。

<configuration>
  <property>
    <name>fs.contract.test.fs.swift</name>
    <value>swift://swiftbucket.rackspace/</value>
  </property>

  <property>
    <name>fs.swift.service.rackspace.auth.url</name>
    <value>https://auth.api.rackspacecloud.com/v2.0/tokens</value>
    <description>Rackspace US (multiregion)</description>
  </property>

  <property>
    <name>fs.swift.service.rackspace.username</name>
    <value>this-is-your-username</value>
  </property>

  <property>
    <name>fs.swift.service.rackspace.region</name>
    <value>DFW</value>
  </property>

  <property>
    <name>fs.swift.service.rackspace.apikey</name>
    <value>ab0bceyoursecretapikeyffef</value>
  </property>

</configuration>
  1. 通常,不同的公共云Swift基础设施会表现出不同的行为(尤其是身份验证和节流)。我们建议测试人员在尽可能多的提供者上创建帐户,并针对每个提供者进行测试。
  2. 它们可能很慢,尤其是远程。远程链接也最有可能使最终的一致性行为可见,这是一个混合的好处。

Testing a  new  filesystem

向协议测试添加新文件系统的核心是添加一个新的协议类,然后为您希望测试的每个测试套件创建一个新的非抽象测试类。

  1. 不要尝试将这些测试添加到Hadoop本身。它们不会被添加到源树中。它们不会被添加到源树中。测试必须与您自己的文件系统源共存。
  2. 在您自己的测试源树中(通常)根据合同为文件和测试创建一个包。
  3. 用于自己协议实现的子类AbstractFSContract。
  4. 对于您计划支持的每个测试套件,创建一个以test和文件系统名称开头的非抽象子类,例子:TestHDFSRenameContract。
  5. 这些非抽象类必须实现抽象方法createContact()。
  6. 识别并记录必须在特定项目的src/test/resources/contract-test-options.xml文件中定义的任何文件系统绑定。
  7. 运行测试直到它们工作。

例如,这里是本地文件系统的create()测试的测试实现。

package org.apache.hadoop.fs.contract.localfs;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.contract.AbstractCreateContractTest;
import org.apache.hadoop.fs.contract.AbstractFSContract;

public class TestLocalCreateContract extends AbstractCreateContractTest {
  @Override
  protected AbstractFSContract createContract(Configuration conf) {
    return new LocalFSContract(conf);
  }
}

AbstractfsContract子类的标准实现技术完全由存储在测试资源树中的一个Hadoop XML配置文件驱动。最好的做法是使用文件系统的名称将其存储在 /contract 下,例如 contract/localfs.xml。让XML文件定义所有文件系统选项,可以立即看到文件系统行为的列表。

LocalFSContract是一种特殊情况,因为它必须根据运行它的操作系统调整其区分大小写策略:对于Windows和OS/X,文件系统都不区分大小写,因此ContractOptions.IS_CASE_SENSITIVE选项必须设置为false。此外,Windows文件系统不支持Unix文件和目录权限,因此还必须设置相关的标志。这是在从资源树加载XML协议文件之后完成的,只需更新现在加载的配置选项:

getConf().setBoolean(getConfKey(ContractOptions.SUPPORTS_UNIX_PERMISSIONS), false);

Handling test failures

如果新的文件系统测试案例未能通过其中一个协议测试,您可以做什么?

这取决于问题的原因

  1. 案例:自定义filesystem子类类不能正确实现规范。FIX。
  2. 案例:底层文件系统的运行方式不符合Hadoop的预期。理想情况下,FIX。或者尝试让文件系统子类隐藏差异,例如通过转换异常
  3. 案例:文件系统和Hadoop之间的基本体系结构差异。示例:不同的并发性和一致性模型。建议:记录并明确文件系统与HDFS不兼容。
  4. 案例:测试与规范不匹配。修复:补丁测试,将补丁提交给Hadoop。
  5. 案例:规范不正确的。底层规范是(除了少数例外)HDFS。如果规范与HDFS不匹配,那么通常应该假设HDFS是文件系统应该做什么的真正定义。如果有不匹配,请在hdfs-dev邮件列表中提出。请注意,虽然文件系统测试位于核心Hadoop代码库中,但拥有文件系统规范及其附带测试的是HDFS团队。

如果由于不支持某个功能而需要跳过测试,请在ContractOptions类中查找现有的配置选项。如果没有方法,短期修复方法是重写该方法,并使用ContractTestUtils.skip()消息记录跳过测试的事实。使用此方法将消息打印到日志中,然后通知测试运行程序跳过了测试。这突出了问题。推荐的策略是调用超类,捕捉异常,并验证异常类和错误字符串的一部分与当前实现所引发的匹配。如果超类实际上成功了,那么它也应该fail()——即它以实现当前没有的方式失败了。这将确保测试路径仍然被执行,测试的任何其他失败—可能是回归—都会被发现。而且,如果这个特性真的实现了,那么改变就会被接受。

一个长期的解决方案是增强基本测试,以添加一个新的可选特性键。这将需要与hdfs-dev邮件列表上的开发人员协作。

'Lax vs Strict'  exceptions

协议测试包括严格异常和松散异常的概念。严格的异常报告意味着:使用IOException的特定子类(如FileNotFoundException、EOFException等)报告失败。松散的报告意味着抛出IOException。

虽然文件系统应该引起更严格的异常,但是它们不能引起异常的原因可能是有的。仍然允许出现松散异常,这只会妨碍对用户应用程序故障的诊断。要声明文件系统不支持更严格的异常,请设置选项fs.contract.supports-strict-exceptions为false。

Supporting FileSystem with login and authentication parameters(支持带有登录和身份验证参数的文件系统)

针对远程文件系统的测试将需要指定文件系统的URL;针对需要登录细节的远程文件系统的测试需要用户名/ IDs和密码。所有这些详细信息都必须放在文件src/test/resources/contract-test-options.xml中,并且配置您的SCM工具为从不将此文件提交给subversion、git或等效工具。此外,必须将构建配置为从不将此文件捆绑到生成的任何-test工件中。Hadoop构建会这样做,从JAR文件中排除src/test/**/*.xml。此外,还需要创建src/test/resources/auth-keys.xml。它可以是contract-test-options.xml的副本。AbstractFSContract类会自动加载此资源文件(如果存在);可以添加特定测试用例的特定键。

作为一个例子,下面是S3N测试键的样子:

<configuration>
  <property>
    <name>fs.contract.test.fs.s3n</name>
    <value>s3n://tests3contract</value>
  </property>

  <property>
    <name>fs.s3n.awsAccessKeyId</name>
    <value>DONOTPCOMMITTHISKEYTOSCM</value>
  </property>

  <property>
    <name>fs.s3n.awsSecretAccessKey</name>
    <value>DONOTEVERSHARETHISSECRETKEY!</value>
  </property>
</configuration>

如果属性fs.contract.test.fs.%s中未定义文件系统URL,AbstractBondedFSContract将自动跳过测试套件,其中%s与文件系统的架构名称匹配。

运行测试时,需要关闭maven.test.skip,因为在这些测试中默认情况下它是真的。这可以通过诸如mvn test-Ptests on之类的命令来完成。

Important: passing the tests does not guarantee compatibility

通过所有文件系统协议测试并不意味着可以将文件系统描述为“与HDFS兼容”。测试试图查看每个操作的独立功能,并将重点放在每个操作的前提条件和后置条件上。未涉及的核心领域是跨分布式系统的并发性和故障方面。

  • 一致性:所有更改是否立即可见?
  • 原子性:在新的文件系统上,HDFS是否保证原子操作相同?
  • 幂等(idempotency):如果文件系统实现了任何重试策略,那么即使在其他客户机操作文件系统时,它也是幂等的吗?
  • 可扩展性(scalability):它支持与hdfs一样大的文件,还是同一个目录中的文件一样多?
  • 持久性(durability):文件真的能保存多久?

Amazon S3和OpenStack Swift对象存储最终是具有非原子重命名和删除操作的一致对象存储,这证明了这一点。单线程测试用例不太可能看到一些并发问题,而一致性通常只在跨数据中心的测试中可见。

文件系统API的使用也有一些具体的方面:

  • 与 hadoop  -fs CLI 的兼容性。
  • 块大小策略是否产生适合分析工作的文件分割。(例如,块大小为1与规范匹配,但是它告诉MapReduce作业一次只工作一个字节,不可用)。

当然,欢迎使用验证这些行为的测试。


Adding a  new  test  suite

  1. 每个操作都应该使用一个测试类来拆分新的测试,如seek()、rename()、create()等。这是为了匹配文件系统契约规范按操作拆分的方式。它还使文件系统实现人员更容易一次在一个测试套件上工作。
  2. 使用一个新的抽象测试套件类子类AbstractFSContractTestBase。同样,在标题中使用Abstract。
  3. 查看org.apache.hadoop.fs.contract.ContractTestUtils,了解帮助测试的实用程序类,以及许多以文件系统为中心的断言。使用它们对文件系统状态进行断言,并在断言实际失败时插入诊断信息,如目录清单和不匹配文件的转储。
  4. 为本地文件系统、原始本地文件系统和HDFS文件系统编写测试—如果其中一个测试失败,那么就有问题的迹象—但是要注意它们确实有不同之处
  5. 一旦核心文件系统通过测试,对象上的测试就会存储。
  6. 试着尽可能详细地记录故障——调试故障的人会感激的。

Root  manipulation tests

有些测试直接针对根文件系统工作,尝试执行诸如重命名“/”和类似操作。根目录是“特殊的”,测试它非常重要,尤其是在non-POSIX文件系统(如对象存储)上。这些测试可能对本机文件系统造成非常大的破坏,因此谨慎使用。

  1. 在AbstractRootDirectoryContractTest下添加测试,或创建一个新测试,其中(a)根位于标题中,(b)签入设置方法以在禁用根测试时跳过测试:                                                                                                                                                                                          
     skipIfUnsupported(TEST_ROOT_TESTS_ENABLED);
               
  2. 不要提供此测试套件的实现来针对本地FS运行。

Scalability tests(可扩展性测试)

设计用于生成可伸缩负载的测试——其中包括大量小文件以及较少的大文件,应设计为可配置,以便测试套件的用户可以配置文件的数量和大小。

注意,在对象存储上,目录重命名操作通常是0(files)*0(data),然而删除操作是0(files)。后者意味着即使任何目录清理操作都可能需要时间,并且可能会超时。设计针对所有操作中可能存在延迟的远程文件系统的测试非常重要。


扩展规范

 规范不完整。它没有对文件系统类进行完整的覆盖,并且可能有一些现有的指定类没有覆盖。

  1. 查看类/接口/方法的实现,了解它们的功能,尤其是HDFS和本地的功能。这些是今天所做的记录。
  2. 查看POSIX API规范。
  3. 在HDFS JIRAs中搜索关于文件系统主题的讨论,并尝试理解将要发生什么,以及发生了什么。
  4. 使用IDE了解如何在Hadoop、HBase和堆栈的其他部分中使用方法。尽管这假定这些是典型的Hadoop应用程序,但它至少会显示应用程序期望文件系统如何运行。
  5. 看看java.io源代码来查看绑定的文件系统类如何工作,并仔细阅读它们的javadoc。
  6. 如果有什么不清楚-如hdfs-dev列表中所示。
  7. 不要害怕编写测试来充当实验并澄清实际发生的事情。使用HDFS行为作为规范指南。

 

Ⅶ. Extending the specification and its test

扩展文件系统规范及其测试

文件系统规范不完整。它不包括文件系统API中的所有操作甚至接口和类。它所涵盖的那些问题可能存在一些小问题,例如角点案例、故障模式和其它意外结果。也可能是标准文件系统与规范有显著的的差异,并且认为需要在测试中对其进行记录和处理。

最后,文件系统类和方法不是永久固定的。它们可以通过对现有类以及潜在的全新类和接口的新操作进行扩展。

因此,不要将此规范视为一个完整的静态文档,而应将其视为Hadoop代码的其余部分

  1. 将其视为伴随 参考实现(HDFS)和用于验证文件系统的测试 的实时文档。
  2. 不要害怕扩展或更正它。
  3. 如果您建议对文件系统api进行增强,那么您应该扩展该规范以进行匹配。

How to update this specification

  1. 尽管可以在hadoop-common代码库中找到,但HDFS团队拥有文件系统和FileContext APIs的所有权。在hdfs-dev邮件列表上与他们合作。
  2. 在HADOOP项目组件fs中创建JIRA问题,以覆盖APIs和/或规范中的更改。
  3. 代码更改当然需要测试。理想情况下,对规范本身的更改伴随着新的测试。
  4. 如果改变涉及到具有Abstract*ContractTest的操作,则为此类添加新的方法,并且验证这些方法是否适用于特定文件系统的子类化的测试。其中包括对象存储以及本地和HDFS文件系统。
  5. 如果更改添加了一个新操作,则添加一个与现有的协议驱动体系结构相同的体系结构的新的抽象测试类,和一个为支持该操作的所有文件系统实现子类。
  6. 添加测试方法,以验证无效的先决条件会导致预期的失败。
  7. 添加测试方法以验证有效的先决条件是否导致文件系统的预期最终状态。每次测试尽可能少地测试有助于跟踪问题。
  8. 如果可能,添加测试以显示并发期望。

如果文件系统在新添加的测试中失败,那么可能是因为:

  • 规格不对。
  • 测试错误。
  • 测试正在查找错误的异常(即它太严格)。
  • 规范和测试都是正确的——而且文件系统与预期不一致。

HDFS的行为必须是正确的。如果测试和规范与此行为不匹配,则需要更新规范。即便如此,仍可能有一些FS会改变的情况:

  1. 引发的异常是一个通用IOException,此时可以引发更具信息性的子类,如EOFException。
  2. 当传递一组无效的参数时,文件系统不会正确地失败。这可能是正确的,但必须谨慎行事。

如果不匹配发生在本地文件系统中,那么可能无法纠正,因为这是通过Java IO APIs访问的本地文件系统。

对于其他文件系统,可以更新它们的行为以更准确地反映HDF和/或本地文件系统的行为。对于大多数操作来说,这是很简单的,尽管rename()的语义非常复杂,因此还不清楚HDFS是否是正确的引用。

如果测试失败,并且认为这是一个不可修复的文件系统特定问题,那么应该向ContractOptions接口、响应选项存在/不存在的已修改的测试和用于已更新的标准文件系统以指示何时出现功能/故障模式的XML协议文件添加一个允许对结果进行不同解释的新协议选项。

二. Common(共同)

1. CLI Mini Cluster

HADOOP:CLI MiniCluster.

目的

使用CLI MiniCluster,用户只需使用一个命令启动和停止单节点Hadoop集群,无需设置任何环境变量或管理配置文件。CLI MiniCluster启动YARN/MapReduce和HDFS集群。

当用户希望快速试验真正的Hadoop集群或测试依赖于重要Hadoop功能的non-java程序时,这非常有用。

HadoopTarball

应该能够从发布版中获得Hadoop tarball。此外,还可以直接从源创建tarball:

$ mvn clean install -DskipTests
$ mvn package -Pdist -Dtar -DskipTests -Dmaven.javadoc.skip

注意:You will need protoc 2.5.0 installed.

tarball应该在hadoop dist/target/目录中可用。

运行MiniCluster

$ HADOOP_CLASSPATH=share/hadoop/yarn/test/hadoop-yarn-server-tests-2.9.2-tests.jar bin/hadoop  jar  ./share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.9.2-tests.jar  minicluster  -rmport RM_PORT  -jhsport JHS_PORT

在上面的示例命令中,应使用用户选择的这些端口号替换RM_PORT和JHS_PORT。如果未指定,将使用随机空闲端口。

用户可以使用许多命令行参数来控制要启动的服务,以及传递其他配置属性。可用的命令行参数:

-D <property=value>    Options to pass into configuration object (要传到配置对象的选项)
-datanodes <arg>       How many datanodes to start (default 1)  (要启动多少个数据节点(默认1)

-format                Format the DFS (default false)  (格式化DFS(默认false))
-help                  Prints option help.
-jhsport <arg>         JobHistoryServer port (default 0--we choose)
-namenode <arg>       URL of the namenode (default is either the DFS cluster or a temporary dir)(名称节点的URL(默认为DFS群集或临时目录))
-nnport <arg>          NameNode port (default 0--we choose)
-nnhttpport <arg>      NameNode HTTP port (default 0--we choose)
-nodemanagers <arg>    How many nodemanagers to start (default 1)(要启动多少节点管理器(默认1))

-nodfs                 Don't start a mini DFS cluster
-nomr                  Don't start a mini MR cluster
-rmport <arg>          ResourceManager port (default 0--we choose)
-writeConfig <path>    Save configuration to this XML file
-writeDetails <path>   Write basic information to this JSON file

要显示可用参数的完整列表,用户可以将-help参数传递给上面的命令。

2. Native Libraries

Native Libraires Guide

概述

本指南介绍本地Hadoop库,并包括有关本地共享库的小讨论

注意:Depending on your environment, the term “native libraries” could refer to all *.so’s you need to compile; and, the term “native compression” could refer to all *.so’s you need to compile that are specifically related to compression. Currently, however, this document only addresses the native hadoop library (libhadoop.so). The document for libhdfs library (libhdfs.so) is here.

本地Hadoop库

由于性能原因和Java实现不可用,Hadoop拥有某些组件的本地实现。这些组件在一个名为本地hadoop库的动态链接的本地库中可用。在*nix平台上,库名为libhadoop.so。

用法

使用本机Hadoop库相当容易:

  1. 查看组件。
  2. 查看支持的平台。
  3. 下载一个Hadoop版本,它将包含本机Hadoop库的预构建版本,或者构建您自己的本机Hadoop库版本。无论是下载还是构建,库的名称都是相同的:libhadoop.so。
  4. 安装压缩编解码器开发包(>zlib-1.2,>gzip-1.2):
    1. 如果您下载了这个库,请安装一个或多个开发包——无论您希望在部署中使用哪个压缩编解码器。
    2. 如果构建库,则必须安装这两个开发包。
  5. 检查运行时日志文件。

组件

本机hadoop库包含各种组件:

支持的平台

本机Hadoop库仅在*nix平台上受支持。该库不适用于Cygwin或Mac OS X平台。

本机Hadoop库主要用于GNU/Linus平台,并在以下分发版上进行了测试:

  • RHEL4/Fedora
  • Ubuntu
  • Gentoo

在所有上述发行版上,32/64位本机Hadoop库将与相应的32/64位JVM一起工作。

下载

预构建的32位i386-Linux本地hadoop库是hadoop发行版的一部分,位于lib/native目录中。您可以从hadoop公共发行版下载hadoop发行版。

请确保安装zlib和/或gzip开发包--无论您希望在部署中使用哪个压缩编解码器。

构建

本地hadoop库使用ANSI C编写,使用GNU autotools-chain (autoconf、autoheader、automake、autoscan、libtool)构建。这意味着在任何平台上使用符合标准的C编译器和GNU autotools-chain(请参阅受支持的平台)构建库都应该非常简单。

您需要在目标平台上安装的软件包包括:

  • C compiler (e.g. GNU C Compiler)
  • GNU Autools Chain: autoconf, automake, libtool
  • zlib-development package (stable version >= 1.2.0)
  • openssl-development package(e.g. libssl-dev)

一旦安装了前提条件包,就可以使用标准hadoop pom.xml文件并传递本机标志以构建本机hadoop库:

$ mvn package -Pdist,native -DskipTests -Dtar

您应该可以在以下位置看到新建的库:

   $ hadoop-dist/target/hadoop-2.9.2/lib/native

请注意下面:

  • 为了构建本机Hadoop库,必须在目标平台上同时安装zlib和gzip开发包;但是,对于部署,如果您只想使用一个编解码器,只需安装一个包就足够了。
  • 了构建和部署本地hadoop库,需要为zlib提供正确的32/64库,具体取决于目标平台的32/64位jvm。

运行时

bin/hadoop脚本通过系统属性确保本机hadoop库位于库路径上:-Djava.library.path=<path>

在运行时,检查MapReduce任务的hadoop日志文件。

  • 如果一切正常,那么: DEBUG util.NativeCodeLoader - Trying to load the custom-built native-hadoop library... INFO util.NativeCodeLoader - Loaded the native-hadoop library
  • 如果出了问题,那么: INFO util.NativeCodeLoader - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable

检查

NativeLibraryChecker是一个检查本机库是否正确加载的工具。可以按如下方式启动NativeLibraryChecker:

   $ hadoop checknative -a
   14/12/06 01:30:45 WARN bzip2.Bzip2Factory: Failed to load/initialize native-bzip2 library system-native, will use pure-Java version
   14/12/06 01:30:45 INFO zlib.ZlibFactory: Successfully loaded & initialized native-zlib library
   Native library checking:
   hadoop: true /home/ozawa/hadoop/lib/native/libhadoop.so.1.0.0
   zlib:   true /lib/x86_64-linux-gnu/libz.so.1
   snappy: true /usr/lib/libsnappy.so.1
   zstd: true /usr/lib/libzstd.so.1
   lz4:    true revision:99
   bzip2:  false

本地共享库

您可以使用DistributedCache加载任何本机共享库,以分发和符号链接库文件。

这个例子向您展示了如何分发共享库mylib.so,并从MapReduce任务中加载它。

  1. 首先将库复制到HDFS:bin/hadoop fs -copyFromLocal mylib.so.1 /libraries/mylib.so.1
  2. 作业启动程序应包含以下内容: DistributedCache.createSymlink(conf); DistributedCache.addCacheFile("hdfs://host:port/libraries/mylib.so. 1#mylib.so", conf);
  3. MapReduce任务可以包含: System.loadLibrary("mylib.so");

注意:如果您下载或构建了本地hadoop库,则不需要使用DistibutedCache使该库对MapReduce任务可用。

3. Proxy User

Proxy user - Superusers Acting On Behalf Of Other Users(代理用户-代表其他用户的超级用户

介绍

本文档描述超级用户如何代表其他用户提交作业或访问HDF。

用例

下一节中描述的代码示例(code example)适用于以下用例。

用户名为“super”的超级用户希望代表用户joe提交作业并访问hdfs。超级用户有Kerberos凭据,但用户Joe没有任何凭据。任务需要以用户joe的身份运行,并且需要以用户joe的身份访问namenode上的任何文件。要求用户joe可以通过super的kerberos凭据进行身份验证的连接连接到namenode或job tracker。换句话说,super正在模仿用户joe。

一些产品,如Apache Oozie需要这个。

代码示例(Code example)

在本例中,super的凭证用于登录,并为joe创建代理用户ugi对象。这些操作在代理用户ugi对象的doAs方法中执行。

...
//create ugi for joe. The login user is 'super'.
UserGroupInformation ugi = 
        UserGroupInformation.createProxyUser("joe", UserGroupInformation.getLoginUser());
ugi.doAs(new PrivilegedExceptionAction<Void>){
  public Void run() throws Exception {
    //Submit a job
    JobClient jc = new JobClient(conf);
    jc.submitJob(conf);
    //OR access hdfs
    FileSystem fs = FileSystem.get(conf);
    fs.mkdir(someFilePath);
  }
}

配置

可以使用属性hadoop.proxy user.$superuser.hosts以及hadoop.proxy user.$superuser.groups和hadoop.proxy user.$superuser.users中的一个或两个来配置代理用户。

通过在core-site.xml中指定如下,名为super的超级用户只能从host1和host2连接,以模拟属于group1和group2的用户。

   <property>
     <name>hadoop.proxyuser.super.hosts</name>
     <value>host1,host2</value>
   </property>
   <property>
     <name>hadoop.proxyuser.super.groups</name>
     <value>group1,group2</value>
   </property>

如果不存在这些配置,则不允许模拟,连接将失败。如果更宽松的安全性是首选的,通配符*可用于允许从任何主机或任何用户进行模拟。例如,通过在core-site.xml中如下指定,名为oozie的用户从任何主机访问都可以模拟属于任何组的任何用户。

  <property>
    <name>hadoop.proxyuser.oozie.hosts</name>
    <value>*</value>
  </property>
  <property>
    <name>hadoop.proxyuser.oozie.groups</name>
    <value>*</value>
  </property>

hadoop.proxyuser.$superuser.hosts接受IP地址列表、CIDR格式的IP地址范围和/或主机名。例如,通过如下所示指定,名为super的用户从10.222.0.0-15范围内和10.113.221.221的主机访问可以模拟user1和user2。

   <property>
     <name>hadoop.proxyuser.super.hosts</name>
     <value>10.222.0.0/16,10.113.221.221</value>
   </property>
   <property>
     <name>hadoop.proxyuser.super.users</name>
     <value>user1,user2</value>
   </property>

警告

如果群集以Secure Mode(安全模式)运行,则超级用户必须具有Kerberos凭据才能模拟另一个用户。

它不能为此特性使用委托令牌。如果超级用户将自己的委托令牌添加到代理用户ugi中,则是错误的,因为它将允许代理用户使用超级用户的特权连接到服务。

但是,如果超级用户确实想给joe一个委派令牌,那么它必须首先模拟joe并为joe获取委派令牌,方法与上面的代码示例相同,并将其添加到joe的ugi中。这样,授权令牌将拥有joe的所有者。

4. Rack Awareness

机架感知

Hadoop组件支持机架。例如,HDFS块放置将使用机架感知,通过将一个块副本放在不同的机架上来实现容错。这在集群中的网络交换机故障或分区时提供了数据可用性。

Hadoop主守护进程通过调用配置文件指定的外部脚本或java类来获得集群从属机器的机架id。使用java类或外部脚本进行拓扑,输出必须遵循java  org.apache.hadoop.net.DNSToSwitchMapping接口。接口要求维护一对一的通信,拓扑信息格式为'/myrack/myhost',其中'/'是拓扑分隔符,'myrack'是机架标识符,'myhost'是单个主机。假设每个机架有一个/24子网,可以用'/192.168.100.0/192.168.100.5'格式作为唯一的 rack-host(机架主机)拓扑映射。

要使用Java类进行拓扑映射,类名由配置文件中的net.topology.node.switch.mapping.impl参数指定。示例,NetworkTopology.java包含在Hadoop发行版中,可以由Hadoop管理员自定义。使用Java类而不是外部脚本具有性能优势,因为Hadoop在新从节点注册自身时不需要派生外部进程。

如果实现外部脚本,将使用配置文件中的net.topology.script.file.name参数指定它。与java类不同,外部拓扑脚本不包含在Hadoop分发版中,由管理员提供。Hadoop在派生拓扑脚本时,会向ARGV发送多个IP地址。发送到拓扑脚本的IP地址的数量由net.topology.script.number.args控制,默认值为100。如果net.topology.script.number.args被更改为1,则DataNodes和/或NodeManagers提交的每个IP都会生成一个拓扑脚本。

如果未设置net.topology.script.file.namenet.topology.node.switch.mapping.impl,则会为任何传递的IP地址返回机架id '/default rack'。虽然这种行为看起来是可取的,但它会导致HDFS块复制的问题,因为默认行为是将一个复制的块从机架上写下来,但由于只有一个名为“/default-rack”的机架,所以无法这样做。

python Example

#!/usr/bin/python
# this script makes assumptions about the physical environment.
#  1) each rack is its own layer 3 network with a /24 subnet, which
# could be typical where each rack has its own
#     switch with uplinks to a central core router.
#翻译:这个脚本对物理环境进行假设。 
#1)每个机架都有自己的具有/24子网的3层网络,这可能是典型的,每个机架都有自己的交换机,通过上行链路连接到一个中央核心路由器。
#
#             +-----------+
#             |core router|
#             +-----------+
#            /             \
#   +-----------+        +-----------+
#   |rack switch|        |rack switch|
#   +-----------+        +-----------+
#   | data node |        | data node |
#   +-----------+        +-----------+
#   | data node |        | data node |
#   +-----------+        +-----------+
#
# 2) topology script gets list of IP's as input, calculates network address, and prints '/network_address/ip'.
#2)拓扑脚本获取作为输入的IP列表,计算网络地址,并打印“/network_address/ip”

import netaddr
import sys
sys.argv.pop(0)                                                  # discard name of topology script from argv list as we just want IP addresses。丢弃argv列表中拓扑脚本的名称,因为我们只需要IP地址

netmask = '255.255.255.0'                                        # set netmask to what's being used in your environment.  The example uses a /24。将netmask设置为您的环境中正在使用的内容。示例使用a /24

for ip in sys.argv:                                              # loop over list of datanode IP's。循环datanode IP的列表
    address = '{0}/{1}'.format(ip, netmask)                      # format address string so it looks like 'ip/netmask' to make netaddr work。设置地址字符串的格式,使其看起来像“ip/netmask”以使netaddr工作
    try:
        network_address = netaddr.IPNetwork(address).network     # calculate and print network address。计算并打印网络地址
        print "/{0}".format(network_address)
    except:
        print "/rack-unknown"                                    # print catch-all value if unable to calculate network address。如果无法计算网络地址,则打印catch-all值

bash Example

#!/bin/bash
# Here's a bash example to show just how simple these scripts can be
# Assuming we have flat network with everything on a single switch, we can fake a rack topology.
# This could occur in a lab environment where we have limited nodes,like 2-8 physical machines on a unmanaged switch.
# This may also apply to multiple virtual machines running on the same physical hardware.
# The number of machines isn't important, but that we are trying to fake a network topology when there isn't one.
#翻译:下面是一个bash示例,它展示了这些脚本是多么简单,假设我们有一个平面网络,所有东西都在一个交换机上,那么我们可以伪造一个机架拓扑。
#这可能发生在我们有有限节点的实验室环境中,比如非托管交换机上的2-8台物理机器。
#这也适用于在相同物理硬件上运行的多个虚拟机。
#机器的数量并不重要,但是我们试图在没有网络拓扑的情况下伪造网络拓扑。
#
#       +----------+    +--------+
#       |jobtracker|    |datanode|
#       +----------+    +--------+
#              \        /
#  +--------+  +--------+  +--------+
#  |datanode|--| switch |--|datanode|
#  +--------+  +--------+  +--------+
#              /        \
#       +--------+    +--------+
#       |datanode|    |namenode|
#       +--------+    +--------+
#
# With this network topology, we are treating each host as a rack.  This is being done by taking the last octet
# in the datanode's IP and prepending it with the word '/rack-'.  The advantage for doing this is so HDFS
# can create its 'off-rack' block copy.
# 1) 'echo $@' will echo all ARGV values to xargs.
# 2) 'xargs' will enforce that we print a single argv value per line
# 3) 'awk' will split fields on dots and append the last field to the string '/rack-'. If awk
#    fails to split on four dots, it will still print '/rack-' last field value
#利用这种网络拓扑,我们将每个主机视为机架。这是通过获取数据节点IP中的最后一个八位字节并在其前面加上单词“/rack-”来完成的。这样做的好处是HDF可以创建它的“off-rack”块拷贝。

#1)“echo$@”将把所有argv值回送到xargs。

#2)“xargs”将强制我们每行打印一个argv值。

#3)“awk”将用点字段,并将最后一个字段附加到字符串“/rack-”。如果awk未能在四个点上拆分,它仍将打印'/rack-'最后一个字段值

echo $@ | xargs -n 1 | awk -F '.' '{print "/rack-"$NF}'

5. Secure Mode

6. Service Level Authorization

7. HTTP Authentication

8. Credential Provider API

9. Hadoop KMS

10. Tracing

三. HDFS

1. Architecture

2. User Guide

3. Commands Reference

4. NameNode HA With QJM

5. NameNode HA With NFS

6. Federation

7. ViewFs

8. Snapshots

9. Edits Viewer

10. Image Viewer

11. Permissions and HDFS

12. Quotas and HDFS

13. HFTP

14. libhdfs (C API)

15. WebHDFS (REST API)

16. HttpFS

17. Short Circuit Local Reads

18. Centralized Cache Management

19. NFS Gateway

20. Rolling Upgrade

21. Extended Attributes

22. Transparent Encryption

23. Multihoming

24. Storage Policies

25. Memory Storage Support

26. Upgrade Domain

27. DataNode Admin

28. Router Federation

四. MapReduce

1. Tutorial

MapReduce Tutorial

Purpose

本文档全面描述了Hadoop MapReduce框架的所有面向用户的方面,并提供了一个教程。

前提条件

确保Hadoop已安装、配置并正在运行。更多细节:

概述

Hadoop Map/Reduce是一个使用简易的软件框架,基于它写出来的应用程序能够运行在由上千个商用机器组成的大型集群上,并以一种可靠容错的方式并行处理上T级别的数据集。

一个Map/Reduce 作业(job) 通常会把输入的数据集切分为若干独立的数据块,由 map任务(task)以完全并行的方式处理它们。框架会对map的输出先进行排序, 然后把结果输入给reduce任务。通常作业的输入和输出都会被存储在文件系统中。 整个框架负责任务的调度和监控,以及重新执行已经失败的任务。

通常,Map/Reduce框架和分布式文件系统是运行在一组相同的节点上的,也就是说,计算节点和存储节点通常在一起。这种配置允许框架在那些已经存好数据的节点上高效地调度任务,这可以使整个集群的网络带宽被非常高效地利用。

MapReduce架构包含一个单独的master ResourceManager,每个集群节点一个slave NodeManager和每个应用的MRAppMaster组成。(参考 YARN Architecture Guide)。

应用程序至少应该指明输入/输出的位置(路径),并通过实现合适的接口或抽象类提供map和reduce函数。这些,和其他作业的参数,就构成了作业配置(job configuration)

然后,Hadoop的 job client提交作业(jar包/可执行程序等)和配置信息给ResourceManager,后者负责分发这些软件和配置信息给slave、调度任务并监控它们的执行,同时提供状态和诊断信息给job-client。

虽然Hadoop框架是用JavaTM实现的,但Map/Reduce应用程序则不一定要用 Java来写 。

  • Hadoop Streaming是一种运行作业的实用工具,它允许用户创建和运行任何可执行程序 (例如:Shell工具)来做为mapper和reducer。
  • Hadoop Pipes是一个与SWIG兼容的C++ API (没有基于JNITM技术),它也可用于实现Map/Reduce应用程序。

输入和输出

Map/Reduce框架运转在<key, value> 键值对上,也就是说, 框架把作业的输入看为是一组<key, value> 键值对,同样也产出一组 <key, value> 键值对做为作业的输出,这两组键值对的类型可能不同。

框架需要对key和value的类(classes)进行序列化操作, 因此,这些类需要实现 Writable接口。 另外,为了方便框架执行排序操作,key类必须实现 WritableComparable接口。

一个Map/Reduce 作业的输入和输出类型如下所示:

(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)

例子:WordCount v1.0

在深入细节之前,让我们先看一个Map/Reduce的应用示例,以便对它们的工作方式有一个初步的认识。

WordCount是一个简单的应用,它可以计算出指定数据集中每一个单词出现的次数。

这个应用适用于 单机模式伪分布式模式完全分布式模式 三种Hadoop安装方式。

源代码

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class WordCount {

  public static class TokenizerMapper
       extends Mapper<Object, Text, Text, IntWritable>{

    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object key, Text value, Context context
                    ) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString());
      while (itr.hasMoreTokens()) {
        word.set(itr.nextToken());
        context.write(word, one);
      }
    }
  }

  public static class IntSumReducer
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values,
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }

  public static void main(String[] args) throws Exception {
    Configuration conf = new Configuration();
    Job job = Job.getInstance(conf, "word count");
    job.setJarByClass(WordCount.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class);
    job.setReducerClass(IntSumReducer.class);
    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
}

用法

假设环境变量设置如下:

export JAVA_HOME=/usr/java/default
export PATH=${JAVA_HOME}/bin:${PATH}
export HADOOP_CLASSPATH=${JAVA_HOME}/lib/tools.jar

编译wordcount.java并创建jar包:

$ bin/hadoop com.sun.tools.javac.Main WordCount.java
$ jar cf wc.jar WordCount*.class

假设:

  • /user/joe/wordcount/input - HDFS 中的输入目录
  • /user/joe/wordcount/output - HDFS中的输出目录

作为输入的示例文本文件:

$ bin/hadoop fs -ls /user/joe/wordcount/input/
/user/joe/wordcount/input/file01
/user/joe/wordcount/input/file02

$ bin/hadoop fs -cat /user/joe/wordcount/input/file01
Hello World Bye World

$ bin/hadoop fs -cat /user/joe/wordcount/input/file02
Hello Hadoop Goodbye Hadoop

运行应用程序:

$ bin/hadoop jar wc.jar WordCount /user/joe/wordcount/input /user/joe/wordcount/output

输出:

$ bin/hadoop fs -cat /user/joe/wordcount/output/part-r-00000
Bye 1
Goodbye 1
Hadoop 2
Hello 2
World 2

应用程序能够使用-files选项来指定一个由逗号分隔的路径列表,这些路径是task的当前工作目录。使用选项-libjars可以向map和reduce的classpaths中添加jar包。使用-archives选项程序可以传递档案文件做为参数,这些归档文件是未归档的,并且在tasks的当前工作目录中创建了一个带有归档文件名称的链接。有关命令行选项的更多细节请参考 Commands manual

Running wordcount example with -libjars, -files and -archives:

bin/hadoop jar hadoop-mapreduce-examples-<ver>.jar wordcount -files cachefile.txt -libjars mylib.jar -archives myarchive.zip input output

在这里,myarchive.zip将被放置并解压到一个名为“myarchive.zip”的目录中。

用户可以使用#通过-files和-archives选项为文件和归档文件指定不同的符号名。

bin/hadoop jar hadoop-mapreduce-examples-<ver>.jar wordcount -files dir1/dict.txt#dict1,dir2/dict.txt#dict2 -archives mytar.tgz#tgzdir input output

在这里,task可以分别使用符号名dict1和dict2访问dir1/dict.txt和dir2/dict.txt文件。归档文件mytar.tgz被置换,并解压到名为tgzdir的目录中。

解释

WordCount应用程序非常直截了当。

public void map(Object key, Text value, Context context
                ) throws IOException, InterruptedException {
  StringTokenizer itr = new StringTokenizer(value.toString());
  while (itr.hasMoreTokens()) {
    word.set(itr.nextToken());
    context.write(word, one);
  }
}

Mapper实现,通过map方法,由TextInputFormat指定的格式一次处理一行。然后,它通过StringTokenizer 以空格为分隔符将一行切分为若干tokens,之后,输出< <word>, 1> 形式的键值对。

对于示例中的第一个输入,map输出是:

< Hello, 1>
< World, 1>
< Bye, 1>
< World, 1>

第二个输入,map输出是:

< Hello, 1>
< Hadoop, 1>
< Goodbye, 1>
< Hadoop, 1>

关于组成一个指定作业的map数目的确定,以及如何以更精细的方式去控制这些map,我们将在教程的后续部分学习到更多的内容。

 job.setCombinerClass(IntSumReducer.class);

WordCount还指定了一个combiner 。因此,每次map运行之后,会对输出按照key进行排序,然后把输出传递给本地的combiner(按照作业的配置与Reducer一样),进行本地聚合。

第一个map的输出是:

< Bye, 1>
< Hello, 1>
< World, 2>

第二个map的输出是:

< Goodbye, 1>
< Hadoop, 2>
< Hello, 1>
public void reduce(Text key, Iterable<IntWritable> values,
                   Context context
                   ) throws IOException, InterruptedException {
  int sum = 0;
  for (IntWritable val : values) {
    sum += val.get();
  }
  result.set(sum);
  context.write(key, result);
}

Reducer的实现,通过reduce方法仅仅是对每个键(比如本例中的Hello)出现的次数求和。

因此这个作业的输出就是:

< Bye, 1>
< Goodbye, 1>
< Hadoop, 2>
< Hello, 2>
< World, 2>

作业中的main方法中指定了作业的几个方面, 例如:通过命令行传递过来的输入/输出路径、key/value的类型、输入/输出的格式等等。其然后调用job.waitforcompletion提交作业并监视其进度。

我们将在本教程的后续部分学习更多的关于Job, InputFormat, OutputFormat和其他接口及类(class)。

MapReduce - 用户界面

这部分文档为用户将会面临的Map/Reduce框架中的各个环节提供了适当的细节。这将帮助用户以细粒度的方式实现、配置和优化他们的作业。然而,请注意每个类/接口的javadoc文档提供最全面的文档;本文只是想起到指南的作用。

我们会先看看Mapper和Reducer接口。应用程序通常会通过提供map和reduce方法来实现它们。

然后,我们会讨论其他的核心接口,其中包括:Job, Partitioner, InputFormat, OutputFormat和其它。

最后,我们将通过讨论框架中一些有用的功能点(例如:DistributedCache, IsolationRunner等等)来收尾。

核心功能描述

应用程序通常会通过提供map和reduce来实现 Mapper和Reducer接口,它们组成作业的核心。

Mapper

Mapper将输入键值对(key/value pair)映射到一组中间格式的键值对集合。

Map是一类将输入记录集转换为中间格式记录集的独立任务。 这种转换的中间格式记录集不需要与输入记录集的类型一致。一个给定的输入键值对可以映射成0个或多个输出键值对。

Hadoop MapReduce框架为  由该作业的InputFormat产生的  每一个InputSplit产生一个map任务。

总的来说,mapper实现是通过job.setMapperClass(class)方法传递给作业的。然后,框架为这个task的InputSplit中每个键值对调用一次 map(WritableComparable, Writable, Context)操作。然后,应用程序可以重写cleanup(Context)方法来执行任何必需的清理。

输出键值对不需要与输入键值对的类型一致。一个给定的输入键值对可以映射成0个或多个输出键值对。通过调用context.write(writableComparable,writable)收集输出键值对。

应用程序可以使用counter--计数器报告其统计信息。

框架随后会把与给定key关联的所有中间值(value)分组,并传递给reducer以确定最终输出。用户可以通过job.setGroupingComparatorClass(类)指定  Comparator--比较器  来控制分组。

Mapper的输出被排序后,就按每个Reducer分区。分区总数与作业的reduce任务数相同。用户可以通过实现自定义的 Partitioner来控制哪个key(以及记录)指向哪个 Reducer。

用户可选择通过 Job.setCombinerClass(Class)指定一个combiner,它负责对中间过程的输出进行本地的聚集,这会有助于降低从Mapper到 Reducer数据传输量。

这些被排好序的中间过程的输出结果保存的格式是(key-len, key, value-len, value),应用程序可以通过 Configuration 控制对这些中间结果是否进行压缩以及怎么压缩,使用哪种 CompressionCodec

需要多少个Map?

Map的数目通常是由输入数据的大小决定的,一般就是所有输入文件的总块(block)数。

Map正常的并行规模大致是每个节点(node)大约10到100个map,对于CPU 消耗较小的map任务可以设到300个左右。由于每个任务初始化需要一定的时间,因此,比较合理的情况是map执行的时间至少超过1分钟。

这样,如果你输入10TB的数据,每个块(block)的大小是128MB,你将需要大约82,000个map来完成任务,除非使用 setNumMapTasks(int)(注意:这里仅仅是对框架进行了一个提示,实际决定因素见这里)将这个数值设置得更高。

Reducer

Redcuer把一个 共享key的中间值集合规约为 一个更小的值集合。

用户可以通过 JobConf.setNumReduceTasks(int)设定一个作业中reduce任务的数目。总的来说,reducer实现是通过job.setReducerClass(class)方法为作业传递的,并且可以重写此方法来初始化自己。然后,框架为成组的输入数据中的每个<key, (list of values)>对调用reduce(WritableComparable, Iterable<Writable>, Context)方法。然后,应用程序可以重写cleanup(Context)方法来执行任何必需的清理。

Reducer有3个主要阶段:shuffle、sort和reduce。

Shuffle

Reducer的输入就是Mapper已经排好序的输出。在这个阶段,框架通过HTTP为每个Reducer获得所有Mapper输出中与之相关的分区。

Sort

这个阶段,框架将按照key的值对Reducer的输入进行分组 (因为不同mapper的输出中可能会有相同的key)。

Shuffle和Sort两个阶段是同时进行的;map的输出也是一边被取回一边被合并的。

Secondar Sort

如果要求 中间键分组的等价规则与reduce前键分组的等价规则不同,则可以通过job.setsortComparatorClass(class)指定一个比较器。由于job.setGroupingComparatorClass(Class)可用于控制如何对中间键进行分组,因此可以结合使用它们来模拟值的二级排序。

reduce

在这个阶段,框架为已分组的输入数据中的每个 <key, (list of values)>对调用reduce(WritableComparable, Iterable<Writable>, Context) 。Reduce任务的输出通常是通过调用 Context.write(WritableComparable, Writable)写入 文件系统的。

应用程序可以使用counter报告状态信息。

Reducer的输出是没有排序的

需要多少个Reduce?

Reduce的数目建议是0.95或1.75乘以 (<no. of nodes> * <no. of maximum containers per node>)。

用0.95,所有reduce可以在maps一完成时就立刻启动,开始传输map的输出结果。用1.75,速度快的节点可以在完成第一轮reduce任务后,可以开始第二轮,这样可以得到比较好的负载均衡的效果。

增加reduce的数目会增加整个框架的开销,但可以改善负载均衡,降低由于执行失败带来的负面影响。

上述比例因子比整体数目稍小一些是为了给框架中的推测性任务(speculative-tasks) 或失败的任务预留一些reduce的资源。

无Reducer

如果没有归约要进行,那么设置reduce任务的数目为是合法的。

这种情况下,map任务的输出会直接被写入由 FileOutputFormat.setOutputPath(Job, Path)指定的输出路径。框架在把它们写入FileSystem之前没有对它们进行排序。

Partitioner

Partitioner用于划分键值空间(key space)。

Partitioner负责控制map输出结果key的分割。Key(或者一个key子集)被用于产生分区,通常使用的是Hash函数。分区的数目与一个作业的reduce任务的数目是一样的。因此,它控制将中间过程的key(也就是这条记录)应该发送给m个reduce任务中的哪一个来进行reduce操作。

HashPartitioner是默认的 Partitioner。

Counter

Counter是MapReduce应用程序报告其统计信息的工具。

Mapper和Reducer实现可以使用Counter来报告统计信息。

Hadoop MapReduce与一个通常有用的mapper、reducer和partitioner库捆绑在一起。

作业配置

Job代表一个MapReduce作业的配置。

Job是用户向Hadoop框架描述一个Map/Reduce作业如何执行的主要接口。框架会按照Job描述的信息忠实地去尝试完成这个作业,然而:

  • 一些参数可能会被管理者标记为 final,这意味它们不能被更改。
  • 一些作业的参数可以被直截了当地进行设置(例如: setNumReduceTasks(int)),而另一些参数则与框架或者作业的其他参数之间微妙地相互影响,并且设置起来比较复杂(例如: setNumMapTasks(int))。

通常,Job会指明Mapper、Combiner(如果有的话)、 Partitioner、Reducer、InputFormat和 OutputFormat的具体实现。 FileInputFormat一组输入文件(FileInputFormat.setInputPaths(Job, Path…)FileInputFormat.addInputPath(Job, Path))和 (FileInputFormat.setInputPaths(Job, String…)FileInputFormat.addInputPaths(Job, String)) 以及输出文件应该写在哪儿 (FileOutputFormat.setOutputPath(Path))。

Job可选择地对作业设置一些高级选项,例如:设置Comparator; 放到DistributedCache上的文件;中间结果或者作业输出结果是否需要压缩以及怎么压缩;作业是否允许预防性(speculative)任务的执行 (setMapSpeculativeExecution(boolean))/ setReduceSpeculativeExecution(boolean)); 每个任务最大的尝试次数 (setMaxMapAttempts(int)setMaxReduceAttempts(int))等等。

当然,用户能使用 Contribution.set(String, String)/get(String, String) 来设置或者取得应用程序需要的任意参数。然而,DistributedCache的使用是面向大规模只读数据的。

任务的执行和环境

MRAppMaster 是在一个单独的jvm上以子进程的形式执行 Mapper/Reducer任务(Task)的。

子任务会继承父MRAppMaster的环境。用户可以通过Job中的 mapreduce.{map|reduce}.java.opts和配置参数来设定子jvm上的附加选项,例如: 通过-Djava.library.path=<> 将一个非标准路径设为运行时的链接用以搜索共享库,等等。如果 mapreduce.{map|reduce}.java.opts参数,包含一个符号@taskid@, 它会被替换成mapreduce的taskid的值。

下面是一个包含多个参数和替换的例子,其中包括:记录jvm GC日志; JVM JMX代理程序以无密码的方式启动,这样它就能连接到jconsole上,从而可以查看子进程的内存和线程,得到线程的dump;还把分别map和reduce子jvm的最大堆尺寸设置为512MB和1024MB,

并为子jvm的java.library.path添加了一个附加路径。

<property>
  <name>mapreduce.map.java.opts</name>
  <value>
  -Xmx512M -Djava.library.path=/home/mycompany/lib -verbose:gc -Xloggc:/tmp/@[email protected]
  -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
  </value>
</property>

<property>
  <name>mapreduce.reduce.java.opts</name>
  <value>
  -Xmx1024M -Djava.library.path=/home/mycompany/lib -verbose:gc -Xloggc:/tmp/@[email protected]
  -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
  </value>
</property>

内存管理

用户/管理员还可以使用mapreduce.{map|reduce}.memory.mb指定已启动子任务及其递归启动的任何子进程的最大虚拟内存。请注意,这里设置的值是每个进程的限制。mapreduce.{map|reduce}.memory.mb的值应以兆字节(MB)为单位指定。而且该值必须大于或等于传递给JavaVM的-Xmx,否则VM可能无法启动。

注意:mapreduce.{map|reduce}.java.opts仅用于配置从MRAppMaster启动的子任务。 配置守护进程的内存选项在Configuring the Environment of the Hadoop Daemons中有说明。

框架的某些部分可用的内存也是可配置的。在map和reduce任务中,调整 影响操作并发性的参数以及数据撞击磁盘的频率 可能会影响性能。监视某个作业的文件系统计数器——特别是相对于从map到reduce的字节计数——对于调优这些参数是非常宝贵的。

Map参数

从map输出的记录将被序列化到缓冲区中,元数据将存储到会计缓冲区中。如以下选项所述,当序列化缓冲区或元数据超过阈值时,缓冲区的内容将在后台排序并写入磁盘,而map将继续输出记录。如果其中任何一个缓冲区在溢出过程中被完全填满,map线程将阻塞。当map完成时,所有剩余的记录都写到磁盘上,所有磁盘上的段都合并到一个单一文件中。最小化磁盘溢出的数量可以减少map时间,但是更大的缓冲区也会减少mapper程序可用的内存。

Name Type Value Description
mapreduce.task.io.sort.mb int

100

(默认)

(存储从map输出的记录 的 序列化和记帐 缓冲区的累积大小,以兆字节为单位。)

在对文件进行排序时要使用的缓冲内存总量,单位为兆字节。默认情况下,为每个合并流提供1MB的内存,这应该会使查找最小化。

mapredue.map.sort.spil.percent float

0.80

(默认)

序列化缓冲区中的软限制。一旦到达,线程在后台将开始将内容溢出到磁盘。请注意,如果在溢出过程中超过此阈值,收集并不会阻塞,因此当溢出设置为小于0.5时,溢出可能大于此阈值。

 

其它说明:

  • 如果在溢出过程中超过任一溢出阈值,收集将继续进行,直到溢出完成。例如,如果将mapreduce.map.sort.spill.percent设置为0.33,并且在溢出运行时填充缓冲区的剩余部分,则下一次溢出将包括所有收集的记录或缓冲区的0.66,并且不会生成其他溢出。换句话说,阈值定义的是触发器,而不是阻塞。 
  • 大于序列化缓冲区的记录将首先触发溢出,然后溢出到单独的文件中。这个记录是否首先通过合并器--combiner是不确定的。

Shuffle/Reduce参数

如前所述,每个reduce通过HTTP将分区器--Partitioner分配给它的输出提取到内存中,并定期将这些输出合并到磁盘。如果启用了map输出的中间压缩,则将每个输出解压缩到内存中。以下选项影响这些在reduce之前合并到磁盘的频率,以及在reduce期间分配到map输出的内存。

Name Type Value Description
mapreduce.task.io.soft.factor int

10

(默认)

(排序文件时要同时合并的流的数目。这将确定打开的文件句柄数。)

它限制了合并过程中打开的文件数量和压缩编解码器数量。如果文件的数量超过这个限制,合并将进行多次。虽然这个限制也适用于map,但是大多数作业都应该进行配置,以使其不太可能达到这个限制。

mapreduce.reduce.merge.inmen.threholds int

1000

(默认)

(以内存合并进程的文件数表示的阈值。当我们累积阈值文件数时,我们启动内存合并并溢出到磁盘。值为0或小于0表示我们不希望有任何阈值,而是只依赖ramfs的内存消耗来触发合并。)

在合并到磁盘之前,已拉取到内存中的已排序的map输出数。与前面注释中的溢出阈值一样,这不是定义分区单元,而是一个触发器。在实践中,这通常设置得非常高(1000)或禁用(0),因为从内存中合并段通常比从磁盘中合并要(请参阅下表中的注释)。这个阈值只影响洗牌期间内存中合并的频率。

mapreduce.reduce.shuffle.merge.percent float

0.66

(默认)

(启动内存内合并的使用阈值,表示为分配给 存储 内存内map输出的总内存的百分比,由mapreduce.reduce.shuffle.input.buffer.percent定义。)

启动内存合并之前拉取map输出的内存阈值,表示为分配给内存中存储map输出的内存百分比。由于无法装入内存的map输出可能会暂停,因此设置此高值可能会降低获取和合并之间的并行性。相反,高达1.0的值对于输入完全可以在内存中容纳的reduces是有效的。这个参数只影响混排期间内存中合并的频率。

mapreduce.reduce.shuffle.input.buffer.percent float

0.70

(默认)

(在混排期间相对于最大堆大小分配的用来存储map输出的内存百分比。)

内存的百分比——相对于通常在mapreduce.reduce.java.opts中指定的最大堆大小——可以在混排期间分配用于存储映射map输出。尽管应该为框架预留一些内存,但是通常将内存设置得足够高以存储大量的map输出是有利的。

mapreduce.reduce.input.buffer.percent float

0.0

(默认)

(在reduce期间保留map输出的内存百分比(相对于最大堆大小)。当混排结束时,内存中任何剩余的map输出消耗必须小于此阈值,然后才能开始reduce。)

相对于最大堆大小的内存百分比,在reduce期间可以保留map输出。当reduce开始时,map输出将被合并到磁盘中,直到剩余的输出在这个定义的资源限制之下。默认情况下,在reduce开始将可用内存最大化之前,所有map输出都合并到磁盘。对于更少内存密集型的reduce,应该增加内存密集型,以避免磁盘访问。

其他说明:

  • 如果map输出大于分配给复制map输出的内存的25%,则将直接将其写入磁盘,而不需要首先通过内存暂存。
  • 当使用合并器--combiner运行时,关于高合并阈值和大缓冲区的推理可能不成立。对于在获取所有映射输出之前启动的合并--merge,合并器--combiner将在溢出到磁盘时运行。在某些情况下,您可以通过将资源与map输出结合使用来获得更好的reduce时间—使磁盘溢出量变小,并将溢出和获取并行化—而不是大幅增加缓冲区大小。
  • 在将内存中的map输出合并到磁盘以开始reduce时,如果由于存在要溢出的段而需要进行中间合并,并且至少磁盘上已经存在mapreduce.task.io.sort.factor段,则内存中的map输出将是中间合并的一部分。

配置的参数

以下属性在每个任务执行的作业配置中进行了本地化:

Name Type Value Description
mapreduce.job.id String   The job id
mapreduce.job.jar String   job.jar location in job directory(job.jar在job目录中的位置)
mapreduce.job.local.dir String   The job specific shared scratch space(特定于作业的共享暂存空间)
mapreduce.task.id String   The task id
mapreduce.task.attempt.id String   The task attempt id(任务尝试id)
mapreduce.task.is.map boolean   Is this a map task(这是一个map任务吗)
mapreduce.task.partition int   The id of the task within the job(作业中任务的id)
mapreduce.map.input.file String    The filename that the map is reading from(map正在读取的文件名)
mapreduce.map.input.start long   The offset of the start of the map input split(map输入分片开始的偏移量)
mapreduce.map.input.length long   The number of bytes in the map input split(map输入分片中的字节数)
mapreduce.task.output.dir Sting   The task’s temporary output directory(任务的临时输出目录)

 

 

备注:在执行流作业期间,将转换“mapreduce”参数的名称。点(.)变成下划线(_)。比如:mapreduce.job.id变为 mapreduce_job_id,以及mapreduce.job.jar变为 mapreduce_job_jar。要获取流作业的mapper/reducer中的值,请使用带下划线的参数名称。

任务日志

标准输出(stdout)和错误(stderr)流以及任务的系统日志由节点管理器--NodeManager读取并记录到${HADOOP_LOG_D}/userlog。

分发库

分布式缓存还可以用于分发jar和本机库,以用于map或reduce任务。子JVM总是将其当前工作目录添加到java.library.path和 LD_LIBRARY_PATH中,因此,可以通过 System.loadLibrary or System.load加载缓存的库。有关如何通过分布式缓存加载共享库的详细信息,请参见 Native Libraries

作业的提交和监控

Job是用户作业--user-job与资源管理器--ResourceManager交互的主要界面。

 

 

 

 

2. Commands Reference

3. Compatibility with 1.x

4. Encrypted Shuffle

5. Pluggable Shuffle/Sort

6. Distributed Cache Deploy

7. Support for YARN Shared Cache

五. MapReduce REST APIs

1. MR Application Master

2. MR History Server

六. YARN

1. Architecture

2. Commands Reference

3. Capacity Scheduler

4. Fair Scheduler

5. ResourceManager Restart

6. ResourceManager HA

7. Node Labels

8. Web Application Proxy

9. Timeline Server

10. Timeline Service V.2

11. Writing YARN Applications

12. YARN Application Security

13. NodeManager

14. DockerContainerExecutor

15. Running Applications in Docker Containers

16. Using CGroups

17. Secure Containers

18. Registry

19. Reservation System

20. Graceful Decommission

21. Opportunistic Containers

22. YARN Federation

23. Shared Cache

七. YARN REST APIs

1. Introduction

2. Resource Manager

3. Node Manager

4. Timeline Server

5. Timeline Service V.2

八. Hadoop compatible File Systems

1. Amazon S3

2. Azure Blob Storage

3. OpenStack Swift

九. Auth

1. Overview

2. Examples

3. Configuration

4. Building

十. Tools

1. Hadoop Streaming

2. Hadoop Archives

3. Hadoop Archive Logs

4. DistCp

5. GridMix

6. Rumen

7. Resource Estimator Service

8. Scheduler Load Simulator

9. Hadoop Benchmarking

十一. Reference

1. Changelog and Release Notes

2. API docs

3. Metrics

十二. Configuration

1. core-default.xml

2. hdfs-default.xml

3. mapred-default.xml

4. yarn-default.xml

5. Deprecated Properties

猜你喜欢

转载自blog.csdn.net/xiaozhu_you/article/details/85298549
今日推荐