之前有文章介绍过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. 使用Scribefire在博客中插入语法高亮

    效果如下, 文字1 int cool void main() { cout<<"hello world!"<<endl } 文字2 经过一番折腾,终于搞定了 ...

  2. Python爬虫学习之爬美女图片

    最近看机器学习挺火的,然后,想要借助业余时间,来学习Python,希望能为来年找一份比较好的工作. 首先,学习得要有动力,动力,从哪里来呢?肯定是从日常需求之中来.我学Python看网上介绍.能通过P ...

  3. Spring MVC 项目搭建 -4- spring security-添加自定义登录页面

    Spring MVC 项目搭建 -4- spring security-添加自定义登录页面 修改配置文件 <!--spring-sample-security.xml--> <!-- ...

  4. C# Socket编程笔记(自己看,转载)

    看到这个题目,是不是很眼熟?在博客园里搜下,保证会发现关于这个东东的文章实在是太多了~~~真得是没有写得必要,而且我也有点懒得去琢磨字句.(看到这,肯定得来个转折的了,不然就看不到下文了,不是吗)但是 ...

  5. React 实践项目 (一)

    React在Github上已经有接近70000的 star 数了,是目前最热门的前端框架.而我学习React也有一段时间了,现在就开始用 React+Redux 进行实战! 项目代码地址:https: ...

  6. Mac之OS系统下搭建JavaEE环境 <二> 之Tomcat 的安装配置

    二.Tomcat的安装与配置 1.下载Tomcat 找到Tomcat的官网 百度搜索Tomcat 点击下载即可 下载网址:http://tomcat.apache.org/download-80.cg ...

  7. c++ 命名空间 以及 作用域 函数参数 面向对象实验报告

    面向对象的分析与设计  实验报告一 一.变量的储存类别 auto static register extern auto变量   函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存 ...

  8. 只有第三方控件pas代码生成控件

    如果他人的VCl控件只有pas代码,如何生成控件? 步骤:1.new Pakage(bpl ) 2.在Contains中Add VCl***.pas 3.再Complie ,Install 即可 XE ...

  9. mac重开电脑后显示重装提示解决办法

    情况描述: mac昨天电脑关闭后 第二天打开电脑就显示语言选择安装语言 解决办法: 1  出现语言安装提示界面  我们选择简体中文 2  出现苹果密码登陆    我们选择下面的按钮点击退出  这样就可 ...

  10. 在Linux安装配置Tomcat 并部署web应用 ( 三种方式 )

    系统版本:centos6.5版本 java版本:1.7 一.准备工作 1.java -version 检查是否有java环境,没有则需要去安装并配置到环境变量中. 2.下载tomcat包,下载地址:h ...