之前有文章介绍过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. python flask(多对多表查询)

    我们在flask的学习中,会难免遇到多对多表的查询,今天我也遇到了这个问题.那么我想了好久.也没有想到一个解决的办法,试了几种方法,可能是思路的限制我放弃了,后来,我就在网上百度,可是发现百度出来的结 ...

  2. selenium webDriver给隐藏域赋值 input hidden set value

    //直接这样无法给input hidden赋值// driver.findElement(By.id("image_default")).sendKeys("a1112. ...

  3. 14.什么是jsp动作

    JSP动作元素(action elements),动作元素为请求处理阶段提供信息.动作元素遵循XML元素的语法,有一个包含元素名的开始标签,可以有属性,可选的内容,与开始标签匹配的结束标签. 包含的类 ...

  4. HTML5 Web SQL 数据库操作

    Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs. 以下是规范中定义的三个核心方法: openDataba ...

  5. Spring Security Filter详解

    Spring Security Filter详解 汇总 Filter 作用 DelegatingFilterProxy Spring Security基于这个Filter建立拦截机制 Abstract ...

  6. Java学习笔记之字符串常用方法

    一.String关键字一些常用方法 1.构造方法: public String(); 空构造 public String(byte[]  bytes);将字节数组转成字符串 public String ...

  7. Echarts展示百分比的问题

    22.echarts 想要自定义tooltip 的百分比的时候,可以在formatter中console.log(params); 当鼠标移动到y轴的时候会触发输出;

  8. (转载)MQ基本操作

    摘自:http://blog.sina.com.cn/s/blog_4892cf780100erga.html 一.MQ基本操作 MQ中有几个很重要的组件:队列管理器(QueueManager).队列 ...

  9. 391.FANUC宏程序编程

    运算符 运算符由2个字母组成,用于两个值的比较,以决定它们是相等还是一个值小于或大于另一个值.注意,不能使用不等号 运算符 含义 EQ 等于(=) NE 不等于 GT 大于 GE 大于或等于 LT 小 ...

  10. 网站waf检测

    WAFW00F WAFW00F识别和指纹Web应用防火墙(WAF)产品. 其工作原理是首先通过发送一个正常http请求,然后观察其返回有没有一些特征字符,若没有在通过发送一个恶意的请求触发waf拦截来 ...