一个递归引发的思考
作者:网络转载 发布时间:[ 2014/3/19 11:47:41 ] 推荐标签:工具 数据 递归
近一段时间,在登月项目中接触到一个涉及数据对比的工具,需要对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 {
…… //执行文件处理代码
}
}
}
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11