之前有文章介绍过HDFS租约带来的问题,导致spark应用无法正常读取文件,只能将异常文件找出并且删除后,任务才能继续执行。

但是删除文件实在是下下策,而且文件本身其实并未损坏,只是因为已经close的客户端没有及时的释放租约导致。

按照Hadoop官网的说法,HDFS会启动一个单独的线程,专门处理未及时释放的租约,自动释放超过“硬超时”(默认1小时)仍未释放的租约,但是从问题的现象上来看,这个线程并没有正常的工作,甚至怀疑这个线程是否没有启动,我使用的是CDH集群,可能与相关的设置有关,这一点需要确认。

如果Hadoop没有自动清理租约,我们有办法手动的刷新租约吗?答案是肯定的。

在网上查看资料时,发现HDFS源码中的DistributedFileSystem类提供了一个叫做recoverLease的方法,可以主动的刷新租约。但是非常奇怪,既然已经为外界提供了这个接口,为什么不提供shell指令给用户使用呢?为什么只能通过代码的方式调用呢?我使用的是hadoop-2.6.0,也许后期的版本有所更新,这一点也需要求证。

下面看一下这个方法的源码:

/**
* Start the lease recovery of a file
*
* @param f a file
* @return true if the file is already closed
* @throws IOException if an error occurs
*/
public boolean recoverLease(final Path f) throws IOException {
Path absF = fixRelativePart(f);
return new FileSystemLinkResolver<Boolean>() {
@Override
public Boolean doCall(final Path p)
throws IOException, UnresolvedLinkException {
return dfs.recoverLease(getPathName(p));
}
@Override
public Boolean next(final FileSystem fs, final Path p)
throws IOException {
if (fs instanceof DistributedFileSystem) {
DistributedFileSystem myDfs = (DistributedFileSystem)fs;
return myDfs.recoverLease(p);
}
throw new UnsupportedOperationException("Cannot recoverLease through" +
" a symlink to a non-DistributedFileSystem: " + f + " -> " + p);
}
}.resolve(this, absF);
}

有兴趣的朋友可以下载hadoop源码来仔细推敲一下内部的实现原理,这里我们只说如何调用,解决我们的问题:

    public static void recoverLease(String path) throws IOException {
DistributedFileSystem fs = new DistributedFileSystem();
Configuration conf = new Configuration();
fs.initialize(URI.create(path), conf);
fs.recoverLease(new Path(path));
fs.close();
}

这是我编写的一个调用改接口的简单的封装方法,需要注意的是,此处传入的path,必须是包含文件系统以及namenode和端口号的全路径,比如:

hdfs://namenode1:9000/xxx/xxx.log

如果只需要恢复单个文件,调用上述方法即可,但是通常情况下,我们需要对一个目录进行递归的处理,即恢复指定目录下所有租约异常的文件。

这个时候,我们需要先找出指定目录下所有租约异常的文件,形成一个Set或者List,然后再遍历这个容器,对每个文件进行恢复。

寻找文件列表的方法如下:

public static Set<String> getOpenforwriteFileList(String dir) throws IOException {
/*拼接URL地址,发送给namenode监听的dfs.namenode.http-address端口,获取所需数据*/
StringBuilder url = new StringBuilder();
url.append("/fsck?ugi=").append("dev");
url.append("&openforwrite=1"); /*获得namenode的主机名以及dfs.namenode.http-address监听端口,例如:http://hadoopnode1:50070*/
Path dirpath;
URI namenodeAddress;
dirpath = HDFSUtil.getResolvedPath(dir);
namenodeAddress = HDFSUtil.getDFSHttpAddress(dirpath); url.insert(0, namenodeAddress);
try {
url.append("&path=").append(URLEncoder.encode(
Path.getPathWithoutSchemeAndAuthority(new Path(dir)).toString(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} Configuration conf = new Configuration();
URLConnectionFactory connectionFactory = URLConnectionFactory.newDefaultURLConnectionFactory(conf);
URL path = null;
try {
path = new URL(url.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} URLConnection connection;
BufferedReader input = null;
try {
connection = connectionFactory.openConnection(path, UserGroupInformation.isSecurityEnabled());
InputStream stream = connection.getInputStream();
input = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
} catch (IOException | AuthenticationException e) {
e.printStackTrace();
} if (input == null) {
System.err.println("Cannot get response from namenode, url = " + url);
return null;
} String line;
Set<String> resultSet = new HashSet<>();
try {
while ((line = input.readLine()) != null) {
if (line.contains("MISSING") || line.contains("OPENFORWRITE")) {
String regEx = "/[^ ]*";
Pattern pattern = Pattern.compile(regEx);
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
resultSet.add(matcher.group().replaceAll(":", ""));
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
input.close();
} return resultSet; }

其实获取租约异常列表的方法是我从HDFS源码的org.apache.hadoop.hdfs.tools.DFSck中仿照而来的,通过向NameNode的dfs.namenode.http-address端口通信,获取openforwrite状态的文件列表,然后通过正则匹配以及字符串切割,获取所需的内容。

顺便提一句,由于此代码是Java代码,并且返回的Set类型为java.util.Set,如果在Scala代码中调用,则需要将Set类型转化为scala.collection.immutable.Set,具体方法如下:

    /*获取需要被恢复租约的文件列表,返回类型为java.util.Set*/
val javaFilesSet = HDFSUtil.getOpenforwriteFileList(hdfsPrefix + recoverDirPath)
if (null == javaFilesSet || javaFilesSet.isEmpty) {
println("No files need to recover lease : " + hdfsPrefix + recoverDirPath)
return
} /*将java.util.Set转换成scala.collection.immutable.Set*/
import scala.collection.JavaConverters._
val filesSet = javaFilesSet.asScala.toSet

至此,利用以上两个方法,即可获取指定目录下的所有租约异常的文件列表,然后遍历调用租约恢复接口,即可实现批量恢复。

如何恢复未释放租约的HDFS文件的更多相关文章

  1. [bigdata] 使用Flume hdfs sink, hdfs文件未关闭的问题

    现象: 执行mapreduce任务时失败 通过hadoop fsck -openforwrite命令查看发现有文件没有关闭. [root@com ~]# hadoop fsck -openforwri ...

  2. linux删除文件未释放空间问题处理

    linux删除文件未释放空间问题处理 或者 /根分区满了 (我的根分区是/dev/sda1,/dev/sda1满了) http://blog.csdn.net/donghustone/article/ ...

  3. LINUX文件删除,但磁盘空间未释放

    最近在进行系统压测,由于服务器节点太多,便写了个简单的脚本,在执行过程中发现,日志文件删除后,磁盘空间只释放了一小部分,任有大部分磁盘空间未释放. 使用lsof | grep delete命令,发现已 ...

  4. 【Linux命令】删除大文件后磁盘空间未释放问题

    前言 工作中经常遇到Linux系统磁盘空间不足,但是删除后较大的日志文件后,发现磁盘空间仍没有被释放,有点摸不着头脑,今天博主带大家解决这个问题. 思路 1.工作发现磁盘空间不足: 2.找到占用磁盘空 ...

  5. Linux文件删除空间未释放

    当系统空间使用量过大需要清理空间或者清理某个文件时,有时会出现执行了删除命令之后磁盘空间并没有释放,很多人首次遇到该情况时会比较困惑,在考虑是不是像windows系统的回收站一样,删除只是逻辑删除到回 ...

  6. 如何有效恢复误删的HDFS文件

    HDFS是大数据领域比较知名的分布式存储系统,作为大数据相关从业人员,每天处理HDFS上的文件数据是常规操作.这就容易带来一个问题,实际操作中对重要数据文件的误删,那么如何恢复这些文件,就显得尤为重要 ...

  7. HDFS 文件读写过程

    HDFS 文件读写过程 HDFS 文件读取剖析 客户端通过调用FileSystem对象的open()来读取希望打开的文件.对于HDFS来说,这个对象是分布式文件系统的一个实例. Distributed ...

  8. mysql优化, 删除数据后物理空间未释放(转载)

    mysql优化, 删除数据后物理空间未释放(转载) OPTIMIZE TABLE 当您的库中删除了大量的数据后,您可能会发现数据文件尺寸并没有减小.这是因为删除操作后在数据文件中留下碎片所致.OPTI ...

  9. Delphi窗体创建释放过程及单元文件小结(转)

    Delphi窗体创建释放过程及单元文件小结 Delphi中的窗体,有模式窗体与非模式窗体两种.两种窗体的调用方式不同,模式窗体使用ShowModal显示,非模式窗体使用Show显示.当显示模式窗体的时 ...

随机推荐

  1. eclipse中Build Path 导入的包和复制到 lib 包的区别

    Java Build Path是我们编译需要的包,在比如在import ***.***.***时如果没用Java Build Path导入包的话类里面就有红叉,说不识别这个类,build path只是 ...

  2. Hbase FilterList使用总结

    作者:Syn良子 出处:http://www.cnblogs.com/cssdongl/p/7098138.html 转载请注明出处 我们知道Hbase的Scan经常需要用到filter来过滤表中的数 ...

  3. [leetcode-565-Array Nesting]

    A zero-indexed array A consisting of N different integers is given. The array contains all integers ...

  4. 详解Mysql自动备份与恢复

    通过 mysqldump命令,直接生成一个完整的 .sql 文件 Step 1: 创建一个批处理备份SQL c: cd C:Program Filesmysql5.6.24bin mysqldump ...

  5. Unity 游戏框架搭建 (五) 简易消息机制

    什么是消息机制? 23333333,让我先笑一会. 为什么用消息机制?   三个字,解!!!!耦!!!!合!!!!. 我的框架中的消息机制用例: 1.接收者 ``` using UnityEngine ...

  6. 【SqlServer系列】表连接

    1   概述 1.1  已发布[SqlServer系列]文章 [SqlServer系列]MYSQL安装教程 [SqlServer系列]数据库三大范式 [SqlServer系列]表单查询 1.2  本篇 ...

  7. Eclipse添加struts2

    参照:http://jingyan.baidu.com/article/915fc414fd94fb51394b208e.html 一.插件下载:http://struts.apache.org/do ...

  8. (转)基于 Token 的身份验证

    原文:https://ninghao.net/blog/2834 最近了解下基于 Token 的身份验证,跟大伙分享下.很多大型网站也都在用,比如 Facebook,Twitter,Google+,G ...

  9. 详解Android Activity---Activity的生命周期

    转载注明来自:  http://www.cnblogs.com/wujiancheng/ 一.正常情况下Activity的生命周期:  Activity的生命周期大概可以归为三部分 整个的生命周期:o ...

  10. [CF787D]遗产(Legacy)-线段树-优化Dijkstra(内含数据生成器)

    Problem 遗产 题目大意 给出一个带权有向图,有三种操作: 1.u->v添加一条权值为w的边 2.区间[l,r]->v添加权值为w的边 3.v->区间[l,r]添加权值为w的边 ...