distcp流程分析

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

背景

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是一个ToolToolRunner应用(如果不熟悉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的准备工作。

  1. 构造器
  2. 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作用,下文分析。

  1. 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(
<think>Hadoop DistCp 报错 `xxx doesn't exist` 的解决方案 ==================================== DistCp 报错 `xxx doesn't exist` 表明在源路径或目标路径的某个环节存在路径不存在或访问权限问题。以下结合报错场景和引用资料,提供逐步排查方案: --- ### 一、核心原因分析 该报错通常由以下四类问题引发: 1. **路径拼写错误** HDFS 路径严格区分大小写和特殊字符(如 `//` 重复可能导致问题)。 2. **权限不足** 执行 DistCp 的用户缺乏源/目标路径的读/写权限(跨集群时更常见)。 3. **通配符`*`匹配问题** 使用通配符 `file*` 时,实际目录未匹配到(空匹配)。 4. **集群间配置问题** 跨集群复制时未正确配置代理用户或安全策略(如 HBase Thrift 代理相关配置[^4][^5])。 --- ### 二、具体排查步骤 #### 步骤 1:验证路径是否存在 - **手动检查源/目标路径**: ```bash hadoop fs -ls <完整路径> # 替换为实际的源或目标路径 ``` 若路径包含通配符(如 `source/file*`),需测试通配符是否有效: ```bash hadoop fs -ls source/file* # 验证实际匹配的目录列表 ``` **注意**:DistCp 通配符匹配只在直接路径层级有效(不会递归深层),若 `source/file*` 无匹配结果会报 `does not exist`。 #### 步骤 2:权限检查 - **检查用户权限**: 确保执行用户对源有读权限,对目标有写权限。跨集群时需配置 Kerberos 或代理用户: - **为 HBase Thrift 启用代理**(参考引用[4][^4]): ```xml <!-- hbase-site.xml 添加 --> <property> <name>hbase.thrift.support.proxyuser</name> <value>true</value> </property> ``` - 在非安全集群也可临时关闭权限检查(生产环境勿用): ```bash hadoop distcp -Ddfs.permissions.enabled=false <source> <target> ``` #### 步骤 3:处理通配符匹配空集问题 若通配符未匹配到任何路径: - 使用 `hadoop fs -ls` 确认通配符的实际匹配结果(如 `file*` → `file1, file2`)。 - 确保通配符作用于**目录层级**而非文件名(DistCp 通配符无法匹配文件内容)。 #### 步骤 4:跨集群的特殊配置 - **集群间网络互通**:确保 NameNode RPC 端口可访问(默认 8020)且防火墙开放。 - **安全认证**:若启用 Kerberos,需提前 `kinit` 获取 ticket。 - **代理用户配置** (如 HUE 访问 HBase 场景[^5]): 在目标集群的 `core-site.xml` 添加 HTTP 代理支持: ```xml <property> <name>hadoop.proxyuser.$USER.hosts</name> <!-- $USER 替换为实际用户 --> <value>*</value> </property> ``` #### 示例调试命令: ```bash # 测试通配符匹配(模拟 DistCp 行为) hadoop fs -ls hdfs://src-cluster/source/file* # 最小化复制测试(排除路径问题) hadoop distcp hdfs://src-cluster/source/file01 hdfs://tgt-cluster/target/ ``` --- ### 三、补充技巧与避坑指南 1. **使用 `-dryrun` 预演**: ```bash hadoop distcp -dryrun source/file* target/ # 输出匹配路径,检查是否存在空匹配 ``` 2. **路径规范化**: 避免使用相对路径,完整 HDFS URL 更可靠(如 `hdfs://namenode:8020/path`)。 3. **校验与跳过(引用[2][^2])**: 若涉及 Check sum 报错可追加 `-skipcrccheck -update`,但不应用于初次复制: ```bash hadoop distcp -skipcrccheck -update source/file* target/ ``` > **关键提示**:路径问题 90% 是拼写或权限导致,优先通过命令行直接验证路径。 --- ### 四、典型场景案例 - **案例 1**:通配符匹配了 0 个目录 ```log ERROR: Path 'hdfs://src/file*' does not exist. ``` 解决:修正通配符或手动创建所需目录。 - **案例 2**:跨集群未配置代理用户(引用[4][^5]) ```log AccessControlException: User hadoop not allowed for proxy user ``` 解决:配置目标集群 `core-site.xml` 的代理白名单。 - **案例 3**:路径包含保留字符 如路径含 `#` 或空格需转义处理(或使用引用符 `'path'`)。 --- ### 五、总结流程图 ```mermaid graph TD A[报错:xxx does not exist] --> B{路径存在且可访问?} B -->|是| C[进入下一步排查] B -->|否| D[修复路径或权限] C --> E{是否使用通配符?} E -->|是| F[检查通配符匹配结果数] E -->|否| G[验证目标父目录存在] F -->|匹配数≥1| H[检查权限] F -->|匹配数=0| I[修正通配符或源路径] H --> J{跨集群场景?} J -->|是| K[检查代理配置/Kerberos] J -->|否| L[集群本地调试] ``` §§ 相关问题 §§ 1. 如何用 DistCp 高效复制千万级小文件? 2. DistCp 出现 `Check sum mismatch` 错误如何解决? 3. 如何诊断 DistCp 作业的网络瓶颈? 4. HDFS 通配符匹配规则有哪些限制? </think> 针对 Hadoop DistCp 报错 `xxx doesn't exist` 的问题,这是由路径不存在或访问权限问题导致的常见错误。以下是系统化的排查和解决方案,结合核心引用内容和实践经验: --- ### 一、根本原因分析 1. **路径不存在**(最常见原因) - 源路径或目标路径拼写错误(如大小写敏感、多余空格) - HDFS 目录实际不存在(特别使用通配符 `file*` 时未匹配到任何目录) - 跨集群复制时集群名称(NameService)配置错误 2. **权限不足** - 执行 DistCp 的用户无源路径的读权限(`r`)或目标路径的写权限(`wx`) - 跨集群复制时未配置 Kerberos 认证或代理用户(参考引用[4][^4]) 3. **通配符扩展问题** - 通配符 `file*` 未匹配到任何目录(如目录前缀不符) - 通配符用法错误(如路径格式不完整) --- ### 二、解决方案步骤 #### 步骤 1:验证路径是否存在 通过命令行检查路径的真实性: ```bash # 检查源路径(替换成实际路径) hadoop fs -ls /source/file* # 查看通配符的匹配结果 # 检查目标路径 hadoop fs -ls /target ``` - ✅ 正常情况:显示目录列表 - ❌ 报错 `No such file or directory`:确认路径是否存在拼写错误 #### 步骤 2:检查路径权限 确保执行用户拥有权限(参考[^1]中的权限概念): ```bash # 检查源路径权限 hadoop fs -getfacl /source/file01 # 替换为实际路径 # 检查目标路径权限 hadoop fs -getfacl /target ``` - 🔑 **修复权限**(需管理员权限): ```bash hadoop fs -chmod -R 755 /source # 开放读权限 hadoop fs -chown myuser:supergroup /target # 修改所有者 ``` #### 步骤 3:处理通配符问题 当使用 `file*` 等通配符时: 1. **检查通配符有效性**: ```bash # 模拟通配符扩展结果 hadoop fs -ls /source/file* | wc -l # 返回 0 表示未匹配到任何目录 ``` 2. **修改为显式路径测试**(临时绕过通配符问题): ```bash hadoop distcp /source/file01 /target # 单独复制一个目录测试 ``` #### 步骤 4:跨集群特殊处理 跨集群复制需额外配置(引用[4][^4][^5]): 1. **核心配置**(在 `hbase-site.xml` 或 `core-site.xml` 中添加): ```xml <!-- 启用代理用户支持 --> <property> <name>hbase.thrift.support.proxyuser</name> <value>true</value> </property> <!-- 允许HTTP访问 --> <property> <name>hbase.regionserver.thrift.http</name> <value>true</value> </property> ``` 2. **Kerberos 认证**: ```bash kinit -kt /etc/security/keytabs/myuser.keytab myuser@REALM # 获取票据 ``` #### 步骤 5:完整修复命令示例 ```bash # 1. 开放权限(需HDFS管理员) hadoop fs -chmod -R 755 /source/file* # 2. 跨集群复制(带Kerberos) kinit -kt /path/to/keytab username hadoop distcp -D ipc.client.fallback-to-simple-auth-allowed=true \ hdfs://cluster1/source/file* \ hdfs://cluster2/target/ ``` --- ### 三、避坑指南 1. **路径格式规范**: - 使用完整 HDFS 协议路径:`hdfs://namenode:8020/path` - 避免特殊字符(如 `#`, ` `空格),必要时用引号包裹路径:`"/path with space"` 2. **通配符限制**: - 通配符 `*` 仅在路径末级有效(如 `/data/*` 正确;`/data*/file` 无效) - 嵌套通配需改用 `**`(部分 Hadoop 版本支持) 3. **验证命令**(Dry Run): ```bash hadoop distcp -dryrun /source/file* /target # 预览操作而不真实执行 ``` --- ### 四、典型错误场景修复 | **报错信息** | **原因** | **修复方案** | |---------------------------------------|----------------------|--------------------------------------| | `Path /source/file* does not exist` | 通配符未匹配到目录 | 检查 `/source` 下是否存在 `file`为前缀的目录 | | `Permission denied` | 用户无写权限 | 用 `hdfs` 用户执行或修改目录权限 | | `No common protection layer between ...` | 跨集群安全协议冲突 | 添加 `-D` 参数降级认证协议 | > 📌 **原则**:优先通过 `hadoop fs -ls` 直接验证路径权限和存在性,再执行 distcp
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值