文章目录
背景
distcp可用于跨集群或集群内目录的复制,distcp参数不同复制的结果差别较大。本文结合官网及源码,对distcp流程进行分析,并结合测试给出验证。
使用
1. shell
目标目录父目录不存在时,可以自动建立多层目标目录。
1. 文件复制
文件复制时,相当于cp,因为hdfs无法并行写。将a.txt复制并重命令为a_bak.txt
$ hadoop distcp \
hdfs://cluster-host1:9000/v2-ec-source/2019/07/02/_SUCCESS \
hdfs://10.179.25.59:9000/v2-ec-source/2019/07/02/_SUCCESS_bak
2. 文件夹复制
distcp主要为复制文件夹服务。一个文件只能分配到一个map。因此,在文件夹中有多个文件时,可以发挥并行优势。
$ hadoop distcp \
hdfs://cluster-host1:9000/v2-ec-source/2019/07/02 \
hdfs://10.179.25.59:9000/v2-ec-source/2019/bak
将02下的所有文件,复制到bak目录下。
多文件夹复制
将多个文件夹复制到目标文件夹下,并且多个文件夹最后一个文件夹名保留。
$ hadoop distcp \
hdfs://cluster-host1:9000/v2-ec-source/2019/06 \
hdfs://cluster-host1:9000/v2-ec-source/2019/07 \
hdfs://10.179.25.59:9000/v2-ec-source/2019/mult
目标文件夹结果:
$ hadoop fs -ls /v2-ec-source/2019/mult
... 2019-10-23 20:44 /v2-ec-source/2019/mult/06
... 2019-10-23 20:44 /v2-ec-source/2019/mult/07
当从多个源拷贝时,如果两个源冲突,DistCp会停止拷贝并提示出错信息, 如果在目的位置发生冲突,会根据选项设置解决。 默认情况会跳过已经存在的目标文件(比如不用源文件做替换操作)。每次操作结束时都会报告跳过的文件数目,但是如果某些拷贝操作失败了,但在之后的尝试成功了。
值得注意的是,当另一个客户端同时在向源文件写入时,拷贝很有可能会失败。 尝试覆盖HDFS上正在被写入的文件的操作也会失败。 如果一个源文件在拷贝之前被移动或删除了,拷贝失败同时输出异常 FileNotFoundException。
2. distcp源码分析
1.shell入口:
hadoop-common-project/hadoop-common/src/main/bin下以hadoop文件为入口:
...
elif [ "$COMMAND" = "distcp" ] ; then
CLASS=org.apache.hadoop.tools.DistCp
CLASSPATH=${CLASSPATH}:${TOOL_PATH}
...
# Always respect HADOOP_OPTS and HADOOP_CLIENT_OPTS
HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
#make sure security appender is turned off
HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,NullAppender}"
export CLASSPATH=$CLASSPATH
exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@"
;;
进入 org.apache.hadoop.tools.DistCp:
我们看到Distcp是一个Tool、ToolRunner应用(如果不熟悉hadoop的ToolRunner模式,请参看本博客ToolRunner文章)。Tool应用要求实现run方法,如下:
public class DistCp extends Configured implements Tool {
...
// Tool应用必须的run方法
@Override
public int run(String[] argv) {
// 下文具体分析时给出源码
}
// main方法,也是shell的入口
public static void main(String argv[]) {
int exitCode;
try {
DistCp distCp = new DistCp();
Cleanup CLEANUP = new Cleanup(distCp);
ShutdownHookManager.get().addShutdownHook(CLEANUP,
SHUTDOWN_HOOK_PRIORITY);
exitCode = ToolRunner.run(getDefaultConf(), distCp, argv);
}
catch (Exception e) {
LOG.error("Couldn't complete DistCp operation: ", e);
exitCode = DistCpConstants.UNKNOWN_ERROR;
}
System.exit(exitCode);
}
}
准备工作
我们从main函数入手,先看main的准备工作。
- 构造器
- cleanup:
private static class Cleanup implements Runnable {
private final DistCp distCp;
Cleanup(DistCp distCp) {
this.distCp = distCp;
}
@Override
public void run() {
if (distCp.isSubmitted()) return;
distCp.cleanup();
}
}
...
// 清理方法
private synchronized void cleanup() {
try {
if (metaFolder == null) return;
jobFS.delete(metaFolder, true);
metaFolder = null;
} catch (IOException e) {
LOG.error("Unable to cleanup meta folder: " + metaFolder, e);
}
}
如果distcp实例未提交任务,则删除metaFolder,并另metaFolder = null。
至于metaFolder作用,下文分析。
ShutdownHookManager工具类(可参看其他文章)。
执行
disctp使用ToolRunner.run执行任务。
exitCode = ToolRunner.run(getDefaultConf(), distCp, argv);
我们回到run方法中。
@Override
public int run(String[] argv) {
if (argv.length < 1) {
OptionsParser.usage();
return DistCpConstants.INVALID_ARGUMENT;
}
try {
inputOptions = (OptionsParser.parse(argv));
setTargetPathExists();
LOG.info("Input Options: " + inputOptions);
} catch (Throwable e) {
LOG.error("Invalid arguments: ", e);
System.err.println("Invalid arguments: " + e.getMessage(

本文详细解析了DistCp在Hadoop集群间高效复制数据的原理与流程,包括shell使用、源码分析、元数据生成等关键环节。
最低0.47元/天 解锁文章
2958

被折叠的 条评论
为什么被折叠?



