Hadoop-0.20.0源代码分析(04)

前面,分析了org.apache.hadoop.fs.Filesystem抽象类,我们已经了解到,要实现一个最基本的文件系统都需要包含哪些要点。下面具体到某个实现Filesystem的具体实现类,基于该抽象类Filesystem派生的类的继承关系:

  1. ◦org.apache.hadoop.fs.FileSystem (implements java.io.Closeable)   
  2.     ◦org.apache.hadoop.fs.FilterFileSystem  
  3.         ◦org.apache.hadoop.fs.ChecksumFileSystem  
  4.             ◦org.apache.hadoop.fs.InMemoryFileSystem  
  5.             ◦org.apache.hadoop.fs.LocalFileSystem  
  6.         ◦org.apache.hadoop.fs.HarFileSystem  
  7.     ◦org.apache.hadoop.fs.RawLocalFileSystem  

 

? extends  FileSystem

 

下面分析FileSystem类的两个直接子类FilterFileSystem与RawLocalFileSystem的实现:

  • FilterFileSystem类

这里,首先分析FileSystem抽象类的子类org.apache.hadoop.fs.FilterFileSystem类,它是一个文件系统的实现类,该文件系统包含了一些其他的Filesystem文件系统,并以这些Filesystem文件系统作为基本的文件系统,可能用来转换数据或者提供附加功能。

该类主要是在其内部定义了一个Filesystem属性:

  1. protected FileSystem fs;  

其实现了基类Filesystem的全部方法,是通过fs直接调用Filesystem抽象类中的方法达到的,似乎并没有增加任何其它的功能,但是可以作为一个具有最基本功能的文件系统。

  • RawLocalFileSystem类

RawLocalFileSystem类是实现了FileSystem的API的一个原生本地文件系统,它的属性如下所示:

  1. static final URI NAME = URI.create("file:///"); // 用"file:///"来标识该文件系统是一个本地文件系统  
  2. private Path workingDir; // 用户的当前工作目录  

workingDir在该类的构造方法中,通过获取系统的属性“user.dir”属性初始化的:

  1. public RawLocalFileSystem() {  
  2.   workingDir = new Path(System.getProperty("user.dir")).makeQualified(this);  
  3. }  

该类中包含了3个与文件读写相关的内部类,分别为:

TrackingFileInputStream类通过继承自基类FileSystem类的Statistics statistics属性,来跟踪文件系统中数据流动。

LocalFSFileInputStream类包装了一个FileInputStream流属性,通过构造方法构造一个TrackingFileInputStream来完成输入流的相关操作。

LocalFSFileOutputStream类包装了一个FileOutputStream流属性,以来FileOutputStream定义的操作来实现对文件系统中的输出流的操作。该类还实现了org.apache.hadoop.fs.Syncable接口,使得该文件输出流类能够实现并调用该接口定义的sync()方法,实现强制对基本设备的缓冲区执行同步操作。

还有一个内部类,是封装文件信息的实体类RawLocalFileStatus,实现了getGroup、setOwner与getOwner、setPermission与getPermission、execCommand等方法,其中set前缀的方法设置对应的信息都是通过执行Shell命令来实现的;而get前缀的方法获取对应的信息也是通过执行Shell命令,并调用一个私有方法loadPermissionInfo加载必要的信息。

 另外,RawLocalFileSystem继承实现了FileSystem抽象类中基本的文件操作,例如文件创建、打开、删除等等。

 

 ? extends FilterFileSystem

 

下面分析FilterFileSystem类的两个直接子类ChecksumFileSystem与HarFileSystem的实现:

  • ChecksumFileSystem类

该类是一个基于校验和的文件系统的抽象类,它继承自FilterFileSystem类,它的特点就是在客户端为每一个原生文件(raw file)创建一个校验和文件,扩展名为“.crc”,用它可以校验原生文件的完整性。

1、增加的属性

这个基于校验和文件的文件系统,增加了如下两个属性:

  1. private int bytesPerChecksum = 512// 校验和文件的大小   
  2. private boolean verifyChecksum = true// 是否验证校验和  

其中,bytesPerChecksum默认值为512字节,你可以通过配置Hadoop的core-default.xml文件中的I/O属性io.bytes.per.checksum:

  1. <property>  
  2.   <name>io.bytes.per.checksum</name>  
  3.   <value>512</value>  
  4.   <description>The number of bytes per checksum.  Must not be larger than io.file.buffer.size.</description>  
  5. </property>  

每个校验和文件的大小不能比一个文件所对应的的缓存大,一个文件的缓存大小通过io.file.buffer.size属性可以在core-default.xml文件中进行配置。

verifyChecksum属性默认为true,表示基于ChecksumFileSystem文件系统的文件,在读写过程中,需要检查校验和文件,验证原生文件的完整性。

2、校验和文件

ChecksumFileSystem提供了与校验和文件相关的一些基本的操作:

(1)通过setConf方法,设置文件系统的校验和文件大小:

  1. public void setConf(Configuration conf) {  
  2.   super.setConf(conf);  
  3.   if (conf != null) {  
  4.     bytesPerChecksum = conf.getInt("io.bytes.per.checksum"512);  
  5.   }  
  6. }  

从基于core-default.xml配置文件的配置类Configuration的实例来获取,默认大小512字节。

(2)判断文件系统中的文件是否是校验和文件:

  1. public static boolean isChecksumFile(Path file) {  
  2.   String name = file.getName();  
  3.   return name.startsWith(".") && name.endsWith(".crc");  
  4. }  

一个Path file文件的名称为“.crc”时,才被认为是校验和文件。

(3)获取文件系统校验和文件大小参数:

  1. public int getBytesPerSum() {  
  2.   return bytesPerChecksum;  
  3. }  

(4)获取指定大小的文件对应的校验和文件大小:

  1. public long getChecksumFileLength(Path file, long fileSize) {  
  2.   return getChecksumLength(fileSize, getBytesPerSum());  
  3. }  

 可以看一下,getChecksumLength方法是如何计算一个校验和文件的大小的:

  1. public static long getChecksumLength(long size, int bytesPerSum) {  
  2.   // 这里给出一计算校验和文件大小的公式:   
  3.   // 校验和文件大小 = ((文件大小 + bytesPerSum + 1)/bytesPerSum)*4 + CHECKSUM_VERSION长度 + 4    
  4.   return ((size + bytesPerSum - 1) / bytesPerSum) * 4 +  
  5.            CHECKSUM_VERSION.length + 4;    
  6. }  

(5) 计算校验和文件所分配的缓存的大小:

  1. private int getSumBufferSize(int bytesPerSum, int bufferSize) {  
  2.   int defaultBufferSize = getConf().getInt("io.file.buffer.size"4096); // 获取文件缓存大小,默认为4096   
  3.   int proportionalBufferSize = bufferSize / bytesPerSum; // 计算每一个校验和文件应该分配的文件缓存大小   
  4.   return Math.max(bytesPerSum,  
  5.                   Math.max(proportionalBufferSize, defaultBufferSize));  
  6. }  

校验和文件如果全部放入到文件缓存中,至少分配的缓存的大小,等于校验和文件的大小。但是,可能文件缓存bufferSize可能会足够大,从而在为校验和文件分配缓存的时候,可能会比一个校验和文件的大小要大。检验和文件需要加入到文件缓存中,然后系统从缓存中执行读写操作操作。

3、基本文件操作

实现的基本操作包括文件创建、打开、读写、拷贝、移动。最主要的是真正实现了文件在不同文件系统之间的拷贝操作,如下所示:

  1. @Override  
  2. public void copyFromLocalFile(boolean delSrc, Path src, Path dst)  
  3.   throws IOException {  
  4.   Configuration conf = getConf();  
  5.   FileUtil.copy(getLocal(conf), src, this, dst, delSrc, conf);  
  6. }  
  7. @Override  
  8. public void copyToLocalFile(boolean delSrc, Path src, Path dst)  
  9.   throws IOException {  
  10.   Configuration conf = getConf();  
  11.   FileUtil.copy(this, src, getLocal(conf), dst, delSrc, conf);  
  12. }  

其中,文件工具类org.apache.hadoop.fs.FileUtil的copy方法是实现拷贝操作的核心方法,实现过程比较复杂,可以从此处开始一步步跟踪代码,了解拷贝的实现细节。

4、读写校验和文件

当读校验和文件时,是为了验证数据文件的完整性,在该文件系统实现类中,与检查校验和文件相关的类的继承层次关系如下所示:

  1. ◦java.io.InputStream  
  2.     ◦org.apache.hadoop.fs.FSInputStream(implements org.apache.hadoop.fs.Seekable, org.apache.hadoop.fs.PositionedReadable)  
  3.         ◦org.apache.hadoop.fs.FSInputChecker  
  4.             ◦org.apache.hadoop.fs.ChecksumFileSystem.ChecksumFSInputChecker  

ChecksumFSInputChecker类对一个打开的文件(以FSInputStream输入流的形式),验证该文件与其对应的校验和文件是否匹配,做一个匹配性检查。

该类封装了ChecksumFileSystem、FSDataInputStream、FSDataInputStream三个类的对象,如下所示:

  1. private ChecksumFileSystem fs; // 文件系统   
  2. private FSDataInputStream datas; // 原生文件的输入流对象   
  3. private FSDataInputStream sums; // 校验和文件的输入流对象  

当一个原生文件需要被存储到指定的主机文件系统中,同时计算该文件对应的校验和信息,并将该校验和信息流式写入到对应的校验和文件中。与校验和文件创建与写操作相关的类的继承层次关系如下所示:

  1. ◦java.io.OutputStream  
  2.     ◦org.apache.hadoop.fs.FSOutputSummer  
  3.         ◦org.apache.hadoop.fs.ChecksumFileSystem.ChecksumFSOutputSummer  

ChecksumFSOutputSummer类为一个校验和文件提供了一个FSDataOutputStream输出流对象,它为原生文件数据生成一个校验和,并流式写入到FSDataOutputStream sums输出流中。

在读写校验和文件的时候,都是将校验和文件数据放到文件缓冲区,通过流对象来执行读写操作。

  • HarFileSystem类

 HarFileSystem是Hadoop归档文件系统(Hadoop Archive Filesystem)的实现,该文件系统具有索引文件及其相关内容。用来标识归档文件系统的URI的形式如下所示:

   har://underlyingfsscheme-host:port/archivepath
   或者 
   har:///archivepath 

 

首先看该文件系统的属性:

  1. public static final int VERSION = 1// HarFileSystem的版本号   
  2. private URI uri; // 标识HarFileSystem的URI   
  3. private int version; // 等于版本号HarFileSystem.VERSION    
  4. private URI underLyingURI; // HarFileSystem的基本URI   
  5. private Path archivePath; // 归档的路径Path   
  6. private Path masterIndex; // masterIndex文件   
  7. private Path archiveIndex; // 索引文件   
  8. private String harAuth; // HarFileSystem文件系统的URI授权部分字符串  

 该文件系统类其它文件系统实现类一样,实现了归档文件系统的中对文件的基本操作。

 

? extends ChecksumFileSystem

 

下面分析ChecksumFileSystem类的直接子类LocalFileSystem类的实现。

LocalFileSystem类实现了FileSystem的API,它是一个基于校验和的本地文件系统。

通过构造方法可以看到,它是以org.apache.hadoop.fs.RawLocalFileSystem为基本文件系统的,如下所示:

  1. public LocalFileSystem() {  
  2.   this(new RawLocalFileSystem());  
  3. }  
  4. public LocalFileSystem(FileSystem rawLocalFileSystem) {  
  5.   super(rawLocalFileSystem);  
  6.   rfs = rawLocalFileSystem;  
  7. }  

该类实现了ChecksumFileSystem类中定义但未实现的,用于向文件系统报告校验和文件出错的方法,同时把出错的校验和文件重命名后,移动到指定的目录(bad_files)中,在该目录中的文件是不能够被重新使用的,如下所示:

  1. public boolean reportChecksumFailure(Path p, FSDataInputStream in,  
  2.                                      long inPos,  
  3.                                      FSDataInputStream sums, long sumsPos) {  
  4.   try {  
  5.     File f = ((RawLocalFileSystem)fs).pathToFile(p).getCanonicalFile(); // 得到Path的规范化抽象路径名称   
  6.     String device = new DF(f, getConf()).getMount(); // 根据路径名称f查询:找到同一设备上可写的最顶层目录,这里使用了Unix系统的df命令获取磁盘使用情况统计数据,确定一个有足够空间可以进行写入操作的目录   
  7.     File parent = f.getParentFile();  
  8.     File dir = null;  
  9.     while (parent!=null && parent.canWrite() && parent.toString().startsWith(device)) {  
  10.       dir = parent;  
  11.       parent = parent.getParentFile();  
  12.     }  
  13.     if (dir==null) {  
  14.       throw new IOException(  
  15.                             "not able to find the highest writable parent dir");  
  16.     }  
  17.         
  18.     File badDir = new File(dir, "bad_files");  
  19.     if (!badDir.mkdirs()) {  
  20.       if (!badDir.isDirectory()) {  
  21.         throw new IOException("Mkdirs failed to create " + badDir.toString());  
  22.       }  
  23.     }  
  24.     String suffix = "." + rand.nextInt();  
  25.     File badFile = new File(badDir, f.getName()+suffix);  
  26.     LOG.warn("Moving bad file " + f + " to " + badFile);  
  27.     in.close();                               // 关闭文件输入流   
  28.     f.renameTo(badFile);                      // rename it   
  29.     // 移除校验和文件   
  30.     File checkFile = ((RawLocalFileSystem)fs).pathToFile(getChecksumFile(p));  
  31.     checkFile.renameTo(new File(badDir, checkFile.getName()+suffix));  
  32.   } catch (IOException e) {  
  33.     LOG.warn("Error moving bad file " + p + ": " + e);  
  34.   }  
  35.   return false;  
  36. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值