Hadoop-0.20.0源代码分析(08)

1602人阅读 评论(1) 收藏 举报

这里,继续对FsShell类中一些命令进行阅读分析,主要是看与拷贝文件有关的几个命令。

  • cp命令

该命令实现对文件的拷贝操作,并且支持在不同的文件系统之间进行文件的拷贝。拷贝文件涉及的操作比较复杂,核心拷贝操作还是调用了org.apache.hadoop.fs.FileUtil类的copy方法实现的。 先看该类中定义的其中一个copy方法的实现:

  1. private int copy(String argv[], Configuration conf) throws IOException {  
  2.   int i = 0;  
  3.   int exitCode = 0;  
  4.   String cmd = argv[i++];    
  5.   String dest = argv[argv.length-1]; // 命令行中最后一个参数   
  6.   // 若指定了多个输入源文件,则最后一个参数必须是一个目录   
  7.   if (argv.length > 3) {  
  8.     Path dst = new Path(dest);  
  9.     if (!fs.isDirectory(dst)) { // 最后一个参数必须是目录   
  10.       throw new IOException("When copying multiple files, " + "destination " + dest + " should be a directory.");  
  11.     }  
  12.   }  
  13.   // 循环对每一个文件进行拷贝操作   
  14.   for (; i < argv.length - 1; i++) {  
  15.     try {  
  16.       copy(argv[i], dest, conf); // 将文件argv[i]拷贝到dest目录中   
  17.     } catch (RemoteException e) {  
  18.       // 捕获命令执行发生的异常信息   
  19.       exitCode = -1;  
  20.       try {  
  21.         String[] content;  
  22.         content = e.getLocalizedMessage().split("/n");  
  23.         System.err.println(cmd.substring(1) + ": " +  
  24.                            content[0]);  
  25.       } catch (Exception ex) {  
  26.         System.err.println(cmd.substring(1) + ": " +  
  27.                            ex.getLocalizedMessage());  
  28.       }  
  29.     } catch (IOException e) {  
  30.       // 捕获IO异常信息   
  31.       exitCode = -1;  
  32.       System.err.println(cmd.substring(1) + ": " + e.getLocalizedMessage());  
  33.     }  
  34.   }  
  35.   return exitCode;  
  36. }  

该命令的实现与mv命令类似,这里调用了一个重载的copy命令,实现对一个文件执行拷贝操作。该重载的拷贝方法如下所示:

  1. void copy(String srcf, String dstf, Configuration conf) throws IOException {  
  2.   Path srcPath = new Path(srcf); // 构造Path   
  3.   FileSystem srcFs = srcPath.getFileSystem(getConf()); // 获取到srcPath所在的文件系统srcFs   
  4.   Path dstPath = new Path(dstf);  
  5.   FileSystem dstFs = dstPath.getFileSystem(getConf()); // 获取到dstPath所在的文件系统dstFs   
  6.   Path [] srcs = FileUtil.stat2Paths(srcFs.globStatus(srcPath), srcPath); // 获取到srcFs中满足srcPath模式的FileStatus[]并转换成为Path[]   
  7.   if (srcs.length > 1 && !dstFs.isDirectory(dstPath)) {   
  8.     throw new IOException("When copying multiple files, " + "destination should be a directory.");  
  9.   }  
  10.   for(int i=0; i<srcs.length; i++) { // 循环拷贝操作   
  11.     FileUtil.copy(srcFs, srcs[i], dstFs, dstPath, false, conf); // 调用FileUtil类的拷贝方法copy完成文件在srcFs与dstFs文件系统之间拷贝文件的操作   
  12.   }  
  13. }  

现在,我们开始追踪 org.apache.hadoop.fs.FileUtil类的copy方法,看一看拷贝到底是如何实现的。FileUtil类中定义了多个重载的拷贝方法copy,我们只从FsShell类中调用的copy方法开始追踪其实现。上面调用的FileUtil类的copy实现如下所示:

  1. public static boolean copy(FileSystem srcFS, Path src,   
  2.                            FileSystem dstFS, Path dst,   
  3.                            boolean deleteSource,  
  4.                            Configuration conf) throws IOException {   
  5.   return copy(srcFS, src, dstFS, dst, deleteSource, true, conf); // 调用了一个重载的copy方法实现文件在srcFS与dstFS之间进行复制   
  6. }  

看重载的copy方法的实现,如下所示:

  1. public static boolean copy(FileSystem srcFS, Path src,   
  2.                            FileSystem dstFS, Path dst,   
  3.                            boolean deleteSource,  
  4.                            boolean overwrite,  
  5.                            Configuration conf) throws IOException {  
  6.   dst = checkDest(src.getName(), dstFS, dst, overwrite); // 检查目的文件系统dstFS中dst目录是否合法(参照src)   
  7.   if (srcFS.getFileStatus(src).isDir()) { // 若源文件系统srcFS中src是目录   
  8.     checkDependencies(srcFS, src, dstFS, dst); // 检查文件依赖性:如果srcFS=dstFS,并且dst不是src的子目录,检查通过;如果srcFS与dstFS不是同一个文件系统,依赖性检查通过   
  9.     if (!dstFS.mkdirs(dst)) { // 在dstFS中创建dst目录,准备向其中拷贝数据   
  10.       return false;  
  11.     }  
  12.     FileStatus contents[] = srcFS.listStatus(src); // 获取srcFS中src目录下的文件列表   
  13.     for (int i = 0; i < contents.length; i++) { // 分治思想:分治后执行递归拷贝文件操作   
  14.       copy(srcFS, contents[i].getPath(), dstFS,   
  15.            new Path(dst, contents[i].getPath().getName()),  
  16.            deleteSource, overwrite, conf); // 递归调用   
  17.     }  
  18.   } else if (srcFS.isFile(src)) { // 递归出口(如果src是一个普通文件)   
  19.     InputStream in=null;  
  20.     OutputStream out = null;  
  21.     try {  
  22.       in = srcFS.open(src); // 打开srcFS中的src文件,并返回输入流对象   
  23.       out = dstFS.create(dst, overwrite); // 在目的文件系统dstFS中创建dst文件,并返回输出流,等待写入   
  24.       IOUtils.copyBytes(in, out, conf, true); // 调用:通过调用IOUtils类的copyBytes方法实现流式拷贝   
  25.     } catch (IOException e) {  
  26.       IOUtils.closeStream(out); // 关闭out   
  27.       IOUtils.closeStream(in); // 关闭in   
  28.       throw e;  
  29.     }  
  30.   } else {  
  31.     throw new IOException(src.toString() + ": No such file or directory");  
  32.   }  
  33.   if (deleteSource) { // 如果设置了拷贝完成后删除源文件选项   
  34.     return srcFS.delete(src, true); // 删除源文件系统srcFS的源文件src   
  35.   } else {  
  36.     return true;  
  37.   }  
  38.   
  39. }  

IOUtils类中实现了Hadoop文件系统中文件的 流式拷贝操作,我们追踪该工具类的copyBytes方法,分析实现的过程。该方法如下所示:

  1. /** 
  2.  * 从一个流拷贝到另一个流中 
  3.  */  
  4. public static void copyBytes(InputStream in, OutputStream out, Configuration conf, boolean close)  
  5.   throws IOException {  
  6.   copyBytes(in, out, conf.getInt("io.file.buffer.size"4096),  close); // 调用:重载的copyBytes方法实现流式拷贝   
  7. }  

我们看重载流式拷贝实现方法copyBytes,如下所示:

  1. public static void copyBytes(InputStream in, OutputStream out, int buffSize, boolean close) throws IOException {  
  2.   PrintStream ps = out instanceof PrintStream ? (PrintStream)out : null// 使用PrintStream为out流增加便捷功能   
  3.   byte buf[] = new byte[buffSize]; // 字节缓冲区   
  4.   try {  
  5.     int bytesRead = in.read(buf); // 从输入流in读取bytesRead个字节到buf缓冲区中   
  6.     while (bytesRead >= 0) { // 确实读取到了字节   
  7.       out.write(buf, 0, bytesRead); // 将从in中读取到的字节,通过buf缓冲区写入到输出流out中   
  8.       if ((ps != null) && ps.checkError()) { // 如果ps=(PrintStream)out,测试内部标志,并自动刷新   
  9.         throw new IOException("Unable to write to output stream.");  
  10.       }  
  11.       bytesRead = in.read(buf); // 继续从in读取字节   
  12.     }  
  13.   } finally {  
  14.     if(close) {  
  15.       out.close(); // 关闭out   
  16.       in.close(); // 关闭in   
  17.     }  
  18.   }  
  19. }  

上面在从InputStream in拷贝到OutputStream out中的过程中,使用了更加高效的PrintStream流类,它能够为OutputStream增加方便打印各种数据值的表示形式,而且,它不会抛出IO异常,而是将流式拷贝过程中发生的异常,设置为通过调用checkError方法来检测内部的标志。另外,它还可以实现自动刷新,在向输出流中写入字节(通过字节缓冲区)之后,自动刷新。

cp命令的具体实现都在上面进行分析了,应该能够理解在Hadoop中如何在不同文件系统之间执行流式拷贝文件的过程。

  • copyFromLocal命令

该命令实现了从本地文件系统拷贝文件的操作。实现方法为,如下所示:

  1. /** 
  2.  * 从本地文件系统(srcs在本地文件系统中)拷贝srcs到目的文件系统(对应Path为dstf) 
  3.  */  
  4. void copyFromLocal(Path[] srcs, String dstf) throws IOException {  
  5.   Path dstPath = new Path(dstf);  
  6.   FileSystem dstFs = dstPath.getFileSystem(getConf()); // 获取到目的文件系统dstFs   
  7.   if (srcs.length == 1 && srcs[0].toString().equals("-")) // 如果只指定了一个参数“-”   
  8.     copyFromStdin(dstPath, dstFs); // 调用:从标准输入流中进行流式拷贝操作   
  9.   else // 否则   
  10.     dstFs.copyFromLocalFile(falsefalse, srcs, dstPath); // 调用目的文件系统dstFs的copyFromLocalFile方法执行拷贝操作   
  11. }  
  

我们关注一下copyFromStdin方法拷贝的实现,如下所示:

  1. private void copyFromStdin(Path dst, FileSystem dstFs) throws IOException {  
  2.   if (dstFs.isDirectory(dst)) { // 如果目的文件是目录,不支持源为标准输入流的情况   
  3.     throw new IOException("When source is stdin, destination must be a file.");  
  4.   }  
  5.   if (dstFs.exists(dst)) { // 如果目的文件系统dstFs中存在文件dst,出错   
  6.     throw new IOException("Target " + dst.toString() + " already exists.");  
  7.   }  
  8.   FSDataOutputStream out = dstFs.create(dst); // 满足拷贝要求,执行流式拷贝操作   
  9.   try {  
  10.     IOUtils.copyBytes(System.in, out, getConf(), false); // 调用IOUtils类的copyBytes方法实现,前面已经分析过拷贝过程   
  11.   }   
  12.   finally {  
  13.     out.close(); // 拷贝完成,关闭输出流out   
  14.   }  
  15. }  

再看一下,如果指定的是待拷贝的文件源不是标准输入流的情况,文件系统FileSystem是如何实现拷贝操作的。实现的方法copyFromLocalFile如下所示:

  1. /** 
  2.  * 将本地的srcs,拷贝到目的文件系统中的dst 
  3.  * delSrc指示了拷贝文件完成之后,是否删除源文件srcs 
  4.  */  
  5. public void copyFromLocalFile(boolean delSrc, boolean overwrite, Path[] srcs, Path dst)  
  6.   throws IOException {  
  7.   Configuration conf = getConf();  
  8.   FileUtil.copy(getLocal(conf), srcs, this, dst, delSrc, overwrite, conf); // 调用FileUtil工具类实现拷贝操作   
  9. }  

关于FileUtil的copy方法,前面已经详细分析过,不再累述。

像moveFromLocal、moveFromLocal、copyToLocal、moveToLocal、copyMergeToLocal等命令的实现都非常类似,也不做过多的解释了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值