2013年3月2日星期六

那些天,我给hadoop跪过的坑

大背景:

使用hadoop自带的datajoing软件包进行多个文件的数据连接(约等于实现一个sql的select多表连接功能)

坑1-编译期:

其实这个不是hadoop的坑,应该是shell或者javac的坑。
编辑完一个java文件后,当我用javac编译时,引用了多于一个的jar文件,起初我是这么写的:

javac -classpath hadoop/hadoop-*-core.jar:hadoop/contrib/datajoin/hadoop-*-datajoin.jar -d arvin ReduceDataJoin.java

提示:windows下多个jar包用分号;隔开,linux用冒号:

然后报错提示找不到一些基础的hadoop类,而从前只用一个jar包的时光一切都是ok的:
javac -classpath hadoop/hadoop-*-core.jar -d arvin ReduceDataJoin.java

于是加上-verbose选项查看编译过程,发现:

嗯,问题出在通配符*没有被展开,然后引起各种基础类找不到。修改成指定版本,问题解决。通配符未展开的原因未知,而如果只指定一个jar包,通配符可以顺利展开的,推测是:让shell变节了。后来拉light求证了一下,确实是shell展开通配符的规则所限,当shell碰到正则表达式需要展开匹配时,会首先对非通配符的字符进行匹配,匹配之后才会展开通配符进一步进行匹配,否则会放弃匹配(简而言之就是a*会匹配所有a打头的文件,而b*一开始就会忽略a打头的文件,因为扫描到a打头的文件名时,跟b匹配不上,所以后面的*不再进一步匹配),上面的:和其后的部分在shell里被当成了和前面字符串连在一起的整体的一个文件名,shell自然找不到目标文件,于是放弃展开直接传递给javac了,javac收到的就是一个包含通配符的文件名参数了,这个文件自然不存在。(shell通配符展开的问题已经被王垠在文章《unix设计缺陷》中深度吐槽过)

坑2-jar包生成期:

没经验的同学第一次引用第三方jar包基本都会碰到这个问题,NoClassDefFound Exception,奇葩的是别人基本都是忘记把datajoin包分发到datanode上了,而我没有忘记做分发,但是编译时却临时起意把生成的字节码文件放到了一个单独的子目录arvin中,然后生成jar包时却又把jar文件生成在了当前目录。结果运行时提示找不到入口类,unzip jar包后发现,主类名前多了个arvin的包,改成在当前目录生成解决。此属自作虐不可活,自己刨坑埋自己的半吊子行为。

附分发jar包的官方用法
hadoop jar ReduceDataJoin.jar ReduceDataJoin -libjars hadoop/contrib/datajoin/hadoop-0.20.2-datajoin.jar inpath outpath


坑3-运行期:

运行中报异常:NoSuchMethodException
答案在这里。这真真的必须是个坑,不过说起来也不是hadoop的坑,而是《hadoop in action》没说明白,给的代码中没说要默认无参构造函数。人家自带的例子中是给出了用法示例的有木有,例子可在这里看到:
hadoop-0.20.2/src/contrib/data_join/src/examples/org/apache/hadoop/contrib/utils/join/SampleTaggedMapOutput.java

搜索上述坑的解决方案时,还碰到了一个比较大众的,不过我没踩到,应该是新版本做了改进。

最后,这些坑或多或少都有自己出力刨土的地方,自己给自己呵呵一个。

没有评论:

发表评论