hiveserver2 清理hdfs的临时目录

客户端在连接hiveserver2时,会在hdfs上创建${hive.exec.scratchdir}/<username> (开启doAs为登录用户,否则为启动用户)目录,用于存放作业执行过程中产生的临时文件,在执行某些作业时会产生大量的临时文件,如遇客户端异常或jvm异常退出,造成数据无法清理。

hive提供如下方案解决清理临时文件问题:

1、 hive.start.cleanup.scratchdir : true  默认 :false

hiveserver2在启动的时候回清理 ${hive.exec.scratchdir}/<username> 下目录,若为多hiveserver2 服务且目录配置一致时,会造成删除正在执行作业的临时目录

2、hive.scratchdir.lock :true 默认: false

在scratchdir中保存锁文件,以防止被cleardanglingscratchdir删除

3、hive.server2.clear.dangling.scratchdir :true 默认 :false

开启一个线程池自动清理异常情况未被清理的临时目录,和hive.scratchdir.loc共同管理临时目录的清理,如果客户端处于活动状态,则锁定文件保持打开状态,并且 cleardanglingscratchdir 将检测到它并且不将其视为悬空暂存目录。只有当 Hive 进程意外死亡并留下临时目录时,它才会成为 cleardanglingscratchdir 删除的目标。

如上所述,有如下方案处理临时目录:

1、hive.start.cleanup.scratchdir 设置true;在hive启动时自动清理临时目录,对于多hiveserver2服务,修改hive.exec.scratchdir 配置,以服务命名。

2、hive.scratchdir.lock 设置true,hive.server2.clear.dangling.scratchdir设置true;在hiveserver2服务端口开启临时目录清理线程池,自动清理未被占用的临时目录。

建议采用方案1,只是在启动时清理临时目录,hiveserver2 也有自己的session管理机制,超时异常的session会被清理,临时目录也会被清理,所以异常生成的临时目录不会很多,方案2虽然会自动处理,但会在每个临时目录下生产锁文件,对于服务任务负载高时也会对hdfs产生影响,并且需要清理的临时目录也不会很多,故建议方案1;hiveserver2的正确下线方式为先删除注册到zookeeper上的临时目录,这样客户端就不会通过连接串登录此服务, 当该服务上的作业运行完成session退出时,hiveserver2服务也会停止。

如下为hiveserver2自动清理临时目录的逻辑,利用hdfs的文件锁判断是否被占用,借鉴

public void run() {
  try {
    //获取session临时目录即 hive.exec.scratchdir目录
    Path rootHDFSDirPath = new Path(rootHDFSDir);
    FileSystem fs = FileSystem.get(rootHDFSDirPath.toUri(), conf);
    FileStatus[] userHDFSDirList = fs.listStatus(rootHDFSDirPath);
 
    List<Path> scratchDirToRemove = new ArrayList<Path>();
    for (FileStatus userHDFSDir : userHDFSDirList) {
      FileStatus[] scratchDirList = fs.listStatus(userHDFSDir.getPath());
      for (FileStatus scratchDir : scratchDirList) {
        Path lockFilePath = new Path(scratchDir.getPath(), SessionState.LOCK_FILE_NAME);
        //判断是否有锁文件,没有掉过,不做清理
        if (!fs.exists(lockFilePath)) {
          String message = "Skipping " + scratchDir.getPath() + " since it does not contain " +
              SessionState.LOCK_FILE_NAME;
          if (verbose) {
            consoleMessage(message);
          }
          continue;
        }
        boolean removable = false;
        boolean inuse = false;
        try {
          //往此文件追加内容
          IOUtils.closeStream(fs.append(lockFilePath));
          removable = true;
        } catch (RemoteException eAppend) {
          // RemoteException with AlreadyBeingCreatedException will be thrown
          // if the file is currently held by a writer
          //如果文件当前由编写器持有,将引发带有AlreadyBeingCreateDexException的RemoteException
          if(AlreadyBeingCreatedException.class.getName().equals(eAppend.getClassName())){
            inuse = true;
          } else if (UnsupportedOperationException.class.getName().equals(eAppend.getClassName())) {
            // Append is not supported in the cluster, try to use create
            //群集中不支持追加,请尝试使用创建
            try {
              IOUtils.closeStream(fs.create(lockFilePath, false));
            } catch (RemoteException eCreate) {
              if (AlreadyBeingCreatedException.class.getName().equals(eCreate.getClassName())){
                // If the file is held by a writer, will throw AlreadyBeingCreatedException
                //如果文件由编写器保存,将抛出AlreadyBeingCreatedException
                inuse = true;
              }  else {
                consoleMessage("Unexpected error:" + eCreate.getMessage());
              }
            } catch (FileAlreadyExistsException eCreateNormal) {
                // Otherwise, throw FileAlreadyExistsException, which means the file owner is
                // dead
              //否则,抛出FileAlreadyExistsException,这意味着文件所有者已死亡,后续可以清理此临时目录
                removable = true;
            }
          } else {
            consoleMessage("Unexpected error:" + eAppend.getMessage());
          }
        }
        if (inuse) {
          // Cannot open the lock file for writing, must be held by a live process
          String message = scratchDir.getPath() + " is being used by live process";
          if (verbose) {
            consoleMessage(message);
          }
        }
        if (removable) {
          scratchDirToRemove.add(scratchDir.getPath());
        }
      }
    }
 
    if (scratchDirToRemove.size()==0) {
      consoleMessage("Cannot find any scratch directory to clear");
      return;
    }
    consoleMessage("Removing " + scratchDirToRemove.size() + " scratch directories");
    for (Path scratchDir : scratchDirToRemove) {
      if (dryRun) {
        System.out.println(scratchDir);
      } else {
        boolean succ = fs.delete(scratchDir, true);
        if (!succ) {
          consoleMessage("Cannot remove " + scratchDir);
        } else {
          String message = scratchDir + " removed";
          if (verbose) {
            consoleMessage(message);
          }
        }
      }
    }
  } catch (IOException e) {
    consoleMessage("Unexpected exception " + e.getMessage());
  }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值