一个递归引发的思考

发表于:2014-3-18 11:20

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:凡提    来源:51Testing软件测试网采编

  最近一段时间,在登月项目中接触到一个涉及数据对比的工具,需要对hdfs上的一些原始数据进行按行解析,并重新保存成可被hive识别的数据文件。作为一个复杂度不高的应用MR并行计算框架的工具,设计制作过程还是很顺利的,两三天的功夫编码完成,自测也通过了,然而上线使用后,却发生了一个意想不到的bug,在解决该bug的过程中,我有幸从中获得了一些新的技术启发,也许对大多数技术人员来说只是一个常规到不值一提的小技术点,然而对我却是一个不错的感悟,记录下来以供抛砖引玉。
  闲话少说,直切重点。
  事情是这样的,用户的需求是希望将某个路径作为参数传递给工具,然后工具可以遍历该目录下的所有子目录和文件,将所有数据文件进行解析转换。对于这样一个需求,最常规的思路是做一个递归函数,遇到文件时处理文件,遇到目录时则进行递归,这样很快就可以把某个路径下的所有子目录和文件都遍历一遍,程序也会显得简洁明了。代码一般如下:
private void recursion (Path path) {
FileStatus[] children = fs.listStatus (path);
for(FileStatus child : children){
if(child.isDir()){
recursion(child.getPath());
}
else{
…… //执行文件处理代码
}
}
}
  这样一段程序在我个人自测阶段也没有发现什么问题,但是放到云梯上实际使用的时候问题就来了——栈溢出了。工具抛出了StackOverflowError异常。
  当用户告知出现这个问题的时候,一刹那间我曾经通读过的DistCP的源码立即在我脑海中闪现了出来,曾经不理解为何要那么写的一段代码,此时此刻我终于恍然大悟。这个问题的根源在于hdfs文件系统的目录层次太深了,因此每一层递归累积起来终于将jvm的栈空间撑爆了。自测阶段之所以没有暴露出问题,完全是因为云梯线上的目录文件树在一个小集群中很难模拟。
  解决这个问题是非常简单的,只需要将递归算法替换成迭代算法就可以了。修改后的代码如下:
Stack<FileStatus> pathstack = new Stack<FileStatus>();
for(pathstack.push(fs.getFileStatus(path)); !pathstack.empty();){
FileStatus cur = pathstack.pop();
FileStatus[] children = fs.listStatus(cur.getPath());
for(int i = 0; i < children.length; i++) {
final FileStatus child = children[i];
if (child.isDir()) {
pathstack.push(child);
}
else {
…… //执行文件处理代码
}
}
}
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号