上传文件夹着实让我蛋疼了阵。
以Hadoop的Streaming官方文档为例,研究一下Hadoop任务工作目录的目录结构。Hadoop官方例子如下 (最好自己看文档,血的教训):
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
-archives 'hdfs://hadoop-nn1.example.com/user/me/samples/cachefile/cachedir.jar' \
-D mapred.map.tasks=1 \
-D mapred.reduce.tasks=1 \
-D mapred.job.name="Experiment" \
-input "/user/me/samples/cachefile/input.txt" \
-output "/user/me/samples/cachefile/out" \
-mapper "xargs cat" \
-reducer "cat"
$ ls test_jar/
cache.txt cache2.txt
$ jar cvf cachedir.jar -C test_jar/ .
added manifest
adding: cache.txt(in = 30) (out= 29)(deflated 3%)
adding: cache2.txt(in = 37) (out= 35)(deflated 5%)
$ hadoop dfs -put cachedir.jar samples/cachefile
$ hadoop dfs -cat /user/me/samples/cachefile/input.txt
cachedir.jar/cache.txt
cachedir.jar/cache2.txt
$ cat test_jar/cache.txt
This is just the cache string
$ cat test_jar/cache2.txt
This is just the second cache string
$ hadoop dfs -ls /user/me/samples/cachefile/out
Found 1 items
/user/me/samples/cachefile/out/part-00000 69
$ hadoop dfs -cat /user/me/samples/cachefile/out/part-00000
This is just the cache string
This is just the second cache string
这个例子跑起来是没有问题的。
这里的内容稍稍有一些难理解。什么叫别名?类似于C语言中的引用。比如我有一个文件(or文件夹)叫做file,把他打包成file.jar后put到hadoop,之前在streaming时使用archives引用,这时候hadoop会在每个任务的当前工作目录创建一个名为file.jar的symlink指向“真正”的file.jar,然后在工作目录自动解决此file.jar,注意file.jar解压后并不会得到file这个东西(与本地不同),而是得到file.jar/file,即自动的添加了一级目录file.jar。可是目录file.jar可读性太差,于是我们希望给他一个别名,比如叫做data。那么在执行streaming时就要写为
-archives /*/file.jar#data
如果在工作目录解压后得到的文件就是data/file(注意data只是file.jar的一个别名,file.jar/file也是存在的)
比如上例中,在任务的工作目录cachedir.jar解压后的目录结构是cachedir.jar/cache.txt cache2.txt,这是因为我们使用jar压缩的时候加了-C选项,因而忽略了目录。
下面我们稍稍修改上面例子,使用下面的命令压缩:
jar cvf cachedir.jar test_jar/ .
此时本地解压cachedir.jar得到的是test_jar/cache.txt cache2.txt
此时上面的例子就无法执行了,得到如下的错误:
attempt_201403291230_171885_m_000001_0: cat: cachedir.jar/cache.txt: No such file or directory
attempt_201403291230_171885_m_000001_0: cat: cachedir.jar/cache2.txt: No such file or directory
reduce仍试图去cachedir.jar下找文件,实际上在任务的工作目录下目录结构已经变成了
cachedir.jar/test_jar/cache.txt cache2.txt
自然找不到了。
我们修改index.txt的内容为如下两行
cachedir.jar/test_jar/cache.txt
cachedir.jar/test_jar/cache2.txt
程序便又可以运行了
当然,看到cachedir.jar作为目录肯定不爽,于是我们可以给他设一个别名
-archives /*/cachedir.jar#data
然后目录就“变成”了data/test_jar/cache.txt cache2.txt
搞定!
我们可以用一个脚本验证:
run_mapred.sh
ls * >$2
"cat"
然后streaming命令改为:
hadoop jar $streaming_jar \
-archives 'hdfs:///user/tinfo/cuixiangfei/test2/cachedir.jar' \
-D mapred.job.name="Experiment" \
-files ./run_mapred.sh \
-numReduceTasks 1 \
-input "/user/tinfo/cuixiangfei/test2/index.txt" \
-output $output_path \
-mapper /bin/cat \
-reducer "sh run_mapred.sh"
这是一个更简单的任务,map和reduce都是cat。我们只是为了看run_mapred.sh中ls的输出,如下:
job.jar
run_mapred.sh
cachedir.jar:
cachedir.jar
META-INF
test_jar
tmp:
可以看到,除了job.jar外,还有-files的run_mapred.sh,cachedir.jar下面有cachedir.jar、META_INF和test_jar,test_jar下就是我们的数据cache.txt和cache2.txt。
由上说明,多个文件被压缩成XXXX.jar后,在任务的工作目录自动解压时是解压成XXXX.jar/(这里才是我们的数据),多了一层目录。比如a.txt, b.txt, c.txt打包成XXXX.jar,解压后就是XXXX.jar/a.txt....,并不会像我之前想的那样把a.txt, b.txt, c.txt解压到当前目录。