本文档用于定义hadoop文件系统模型和API,以便于其他的文件系统实现这些API,这样不同的文件系统就可以展示一致性的模型给应用。接下来几天开始学习这个内容。这一部分主要介绍hadoop兼容的文件系统的一些要求。目前hadoop不仅支持hdfs,还支持亚马逊s3,openstack swift和微软Azure。
1、关于hadoop文件系统API的一些假设。
FileSystem类的使用是基于一些假设的。首先HDFS是潜在的文件系统,其次它要求有一些POSIX(可移植操作系统接口,Portable Operating System Interface of UNIX,它定义了操作系统应为应用程序提供的标准接口,是IEEE为各种UNIX操作系统上运行的软件定义的一系列API标准的总称,其正式称呼为IEEE 1003)文件系统的特性。
所有hadoop兼容的文件系统首先要是一个Unix中实现的文件系统模型,它需要满足:
它需要是由目录和文件组成的存在继承关系的目录结构。
文件包含0个或多个字节的数据。
不能将文件和目录置于文件之下。
目录包含0个或多个文件。
目录本身不包含任何数据。
可以在文件中写入任何的二进制数据。当文件内容被读取时,无论读取方是来自集群内还是集群外,数据都将会返回。
可以在单个文件中存储以GB为单位的数据。
总是存在以“/”命名的根节点,且根节点无法被更名。
根节点总是一个目录,它不能被一个文件取代。
一个针对根目录的循环删除操作将只会删除根目录中的内容,而不会删除根目录本身。
不能在某个目录时,对它更名或删除操作。
不能针对某个存在的文件,对它之上的目录进行更名或删除操作。只能对它本身进行这些操作。
列举目录操作将会返回目录中所有的数据文件(可能存在隐藏的checksum文件,但是数据文件都将被返回)。
列举目录操作显示的文件属性和真实属性一致,且和打开该文件时显示的属性一致。
如果操作者没有某个操作的权限,那么在他执行该操作时将会失败,并抛出错误。
(1)Path Names路径名
一个路径名由路径元素和分隔符"/"组成。
一个路径元素是一个或多个unicode字符。
路径元素不能包含":"或"/"字符。
路径元素不能包含ASCII/UTF-8在0-31之间的值。
路径元素不能是"."或".."。
MS Azure blob store文档要求路径元素也不能是多个连续的“.”。
路径会根据unicode码点(code-points)进行比较。
不区分大小写的,或者本地特殊定义的比较方式必须不能被采用。
(2)关于网络的假设
文件系统遭遇网络错误时的状态是未定义的。
网络错误可以反馈给客户端,它需要是一个IOException或它的子类。
异常的细节需要有诊断价值。
2、兼容hadoop的文件系统的核心要求
如果某些文件系统不满足下列的这些要求,那么它们可能不会像预期的那样运行。
(1)原子操作
原子操作用于在集群中锁定和释放访问权限。
hadoop要求的原子操作包括:
1、创建文件(如果没有选择overwrite,那么check和creation分别都需要是原子操作)
2、删除文件
3、重命名文件
4、重命名目录
5、创建单个目录(mkdir)
在HDFS中循环删除操作也是原子操作,但其他hadoop文件系统并未这样设置,包括本地文件系统。
(2)一致性
hadoop文件系统的一致性模型是one-copy-update-semantics,这是个典型的POSIX文件系统。
创建。当写入一个新创建文件的output stream的close()方法结束以后,集群中任何查看该文件元数据和数据的操作将会立即返回正确的结果。
更新。当写入一个文件的output stream的close()方法结束以后,集群中任何查看该文件元数据和数据的操作将会立即返回更新后的结果。
删除。但一个针对某个路径的删除操作(除了针对根节点的删除操作)成功执行完毕以后,该路径将立即不可见也 不可访问。特别地,listStatus(),open(),rename(), append()操作都将会失败。
删除后创建。当一个文件被删除接着一个同名文件被创建时,新的文件需要立即可见,它的内容也要立即对文件系统的API开放。
重命名。当重命名操作完成后,新的路径需要立即生效,旧的路径需要不可访问。
集群内和集群外对文件系统的访问需要保持一致性,所有客户端访问同一个文件将会得到相同的元数据和数据,返回的结果和客户端所在的位置无关。
(3)并发性
访问一个正在被修改的远程文件时,修改的内容将会不可见。
(4)操作和失败
所有的操作最终都会结束,成功或者失败。
操作完成的时间取决于该操作的具体实现和系统的状态。
操作可能抛出RuntimeException或其子类。
针对网络,远程或high-level的问题,操作需要抛出IOException或其子类。
在遇到异常时,操作需要打印异常的原因,而不是代码。
如果一个异常的类名被打印,那么抛出的异常只能是它或它的子类而不能是父类。
如果一个操作没有被实现,那么需要抛出UnsupportedOperationException。
操作的具体实现可能会在失败后进行重试,这类操作需要确保一致性和原子性要求。
(5)未定义的容量限制
有些未被明确定义的文件系统容量
1、目录中的最大文件数目。
2、目录中的最大目录数目。
3、文件系统中最大的目录和文件总数。
4、文件名的长度(HDFS是8000)
5、MAX_PATH,指向文件的完整路径长度。
6、路径的最大深度(目录嵌套的层数,HDFS可嵌套1000层)。
7、单个文件的最大大小。
(6)未定义的超时时长
在HDFS中,阻塞操作超时是可以可变量,sites和客户端可以调整和操作重试有关的参数。关于某些操作有隐含的假设:
1、seek()操作很快,且不会引入网络延时。
2、如果子项不是非常多,那么列举目录清单操作也很快。
3、针对子项非常多的情况,hadoop2引入了迭代列举目录清单操作。
4、OutputStream的close()很快,无论针对文件的操作是否成功。
5、删除一个目录的耗时和它所包含的子项的多少与大小无关。
(7)对象存储VS文件系统
hadoop为很多对象存储系统(Blobstore)提供了文件系统客户端,虽然它们中很多都和hadoop文件系统的要求有冲突。这也是为什么虽然hadoop可以读写对象存储系统中的内容,但是即使是hadoop直接支持的对象存储系统(亚马逊s3和OpenStack Swift&mdash)也不能直接取代HDFS的地位。
回归到问题:什么是对象存储?
对象存储是一个数据存储服务,通常使用http或https进行访问。一个PUT请求上载一个对象/"Blob";一个GET请求获取一个对象/"Blob"。一些受限的GET请求可以获取对象/"Blob"的部分内容。DELETE请求用于删除某个对象。
对象根据名称进行存储。名称是一个字符串,可能带有"/"字符,它没有目录的概念。任何满足server provider要求的命名都可以用于对象。
对象存储一般提供一个前缀来检索对象;一个在服务根目录执行的GET操作需要合适的查询参数。
对象存储通常不提供POSIX的API,而只是提供简单的http请求。
hadoop文件系统客户端尝试将对象存储系统包装成类似于HDFS的文件系统。但这仅是一个假设,因为对象存储系统有不同的特性:
1、一致性。对象存储最终一致性的。创建、删除、更新这样的操作完成之后需要一段时间才对所有访问者可见。甚至一个修改对于修改者来说都不能做到立即可见。例如一个对象test/data1.csv被一个新的数据集覆盖了。但是在覆盖操作刚刚完成的这段时间里,GET test/data1.csv依然会返回覆盖前的数据。hadoop假设文件系统是一致性的,创建、更新、删除都是立即可见的,同样展示目录清单操作也会显示目录当前的内容及其属性。
2、原子操作。hadoop假设目录的重命名操作是原子操作,删除操作也是原子操作。对象存储系统的客户端实现这些操作时会对每个具有匹配目录前缀的对象执行操作,这些操作每次修改多个对象,因此不再是原子操作了。如果这个操作在执行途中挂掉,对象的状态将会是操作执行完一般的状态(即有些对象执行了操作,有些没有)。
3、耐用性。hadoop假设OutputStream的实现通过一个flush()操作将数据写入目标。对象存储的实现将它们所有的数据存储在一个本地文件,并在close()操作时将这个本地文件提交到对象存储系统。因此当遭遇异常时,所有的数据都将会丢失,不存在局部提交的情况。更重要的是,真实的写入操作在close()之后才进行,写入的时长和数据量成正相关和贷款成反相关。如果操作失败,最好进行相关的异常处理,而不是忽略它。