Archive for 四月, 2010

Hadoop的分布式文件系统HDFS为java提供了原生的接口,可以像访问本地文件一样的,对HDFS中的文件进行增删改查等操作。

对于其他非java语言的支持,hadoop使用了Thrift

对于该方式,hadoop里针对thrift的hadoop-0.20.2/src/contrib/thriftfs/readme是这样说的:

Thrift is a software framework for scalable cross-language services
development. It combines a powerful software stack with a code generation
engine to build services that work efficiently and seamlessly
between C++, Java, Python, PHP, and Ruby.
This project exposes HDFS APIs using the Thrift software stack. This
allows applciations written in a myriad of languages to access
HDFS elegantly.
The Application Programming Interface (API)
===========================================
The HDFS API that is exposed through Thrift can be found in if/hadoopfs.thrift.
Compilation
===========
The compilation process creates a server org.apache.hadoop.thriftfs.HadooopThriftServer
that implements the Thrift interface defined in if/hadoopfs.thrift.
Th thrift compiler is used to generate API stubs in python, php, ruby,
cocoa, etc. The generated code is checked into the directories gen-*.
The generated java API is checked into lib/hadoopthriftapi.jar.
There is a sample python script hdfs.py in the scripts directory. This python
script, when invoked, creates a HadoopThriftServer in the background, and then
communicates wth HDFS using the API. This script is for demonstration purposes
only.

由于它说的过于简单,而我又对java世界了解甚少,导致走了不少弯路,现记录如下:

1、下载thrift源码,安装(./bootstrap.sh ;./configure –prefix=/usr/local/thrift; make;sudo make install)

2、将一些必须的文件cp到thrift安装目录下:

cp /path/to/thrift-0.2.0/lib/php/ /usr/local/thrift/lib/ -r

mkdir /usr/local/thrift/lib/php/src/packages/

cp /path/to/hadoop-0.20.2/src/contrib/thriftfs/gen-php/ /usr/local/thrift/lib/php/src/packages/hadoopfs/ -r

3、安装thrift的php扩展(针对php而言)

cd /path/to/thrift-0.2.0/lib/php/src/ext/thrift_protocol;phpize; ./configure;make ;make install;

修改php.ini,添加extension=thrift_protocol.so

4、编译hadoop

cd /path/to/hadoop-0.20.2; ant compile (ant -projecthelp可以查看项目信息;compile 是编译core和contrib目录)

5、启动hadoop的thrift代理

cd /path/to/hadoop-0.20.2/src/contrib/thriftfs/scripts/; ./start_thrift_server.sh [your-port] (如果不输入port,则随机一个port)

6、执行php测试代码

<?php

error_reporting(E_ALL);

ini_set(‘display_errors’, ‘on’);

$GLOBALS[‘THRIFT_ROOT’] = ‘/usr/local/thrift/lib/php/src‘;

define(‘ETCC_THRIFT_ROOT’, $GLOBALS[‘THRIFT_ROOT’]);

require_once(ETCC_THRIFT_ROOT.’/Thrift.php’ ); require_once(ETCC_THRIFT_ROOT.’/transport/TSocket.php’ ); require_once(ETCC_THRIFT_  ROOT.’/transport/TBufferedTransport.php’ ); require_once(ETCC_THRIFT_ROOT.’/protocol/TBinaryProtocol.php’ );

$socket = new TSocket(your-host, your-port);

$socket->setSendTimeout(10000);

$socket->setRecvTimeout(20000);

$transport = new TBufferedTransport($socket);

$protocol = new TBinaryProtocol($transport);

require_once(ETCC_THRIFT_ROOT.’/packages/hadoopfs/ThriftHadoopFileSystem.php’);

$client = new ThriftHadoopFileSystemClient($protocol);

$transport->open();

try

{

$pathname = new Pathname(array(‘pathname’ => ‘your-hdfs-file-name‘));

$fp = $client->open($pathname);

var_dump($client->stat($pathname));

var_dump($client->read($fp, 0, 1024));

exit;

} catch(Exception $e)

{

print_r($e);

}

$transport->close();

?>

可能出现的问题:

1、可以创建目录或者文件,但是读取不到文件内容

这时可以打开hadoop thrift的log4j配置(如果你用的是log4j记录日志的话),在/path/to/hadoop/conf/log4j.properties 里,修改:

hadoop.root.logger=ALL,console

这时,没执行一条访问HDFS的操作,都会把debug信息打印出来。

我这里看到的是file的id很奇怪,怀疑是在32位机上溢出了,尝试修改未果。之后迁移到64位机,运行正常!

问题代码在:/usr/local/thrift/lib/php/src/protocol/TBinaryProtocol.php (由于我代码里用的TBinaryProtocol类)的readI64函数里。

2、启动start_thrift_server.s失败

Exception in thread “main” java.lang.NoClassDefFoundError: org/apache/hadoop/thriftfs/HadoopThriftServer

Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.thriftfs.HadoopThriftServer

at java.net.URLClassLoader$1.run(URLClassLoader.java:217)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(URLClassLoader.java:205)

at java.lang.ClassLoader.loadClass(ClassLoader.java:323)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)

at java.lang.ClassLoader.loadClass(ClassLoader.java:268)

at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:336)

Could not find the main class: org.apache.hadoop.thriftfs.HadoopThriftServer. Program will exit.

可以查看classpath,是否正确。我是添加了以下语句才正常的:

CLASSPATH=$CLASSPATH:$TOP/build/contrib/thriftfs/classes/:$TOP/build/classes/:$TOP/conf/

3、安装thrift出现问题

运行./bootstrap.sh出错: 查看boost是否安装,或者版本过旧

make出错:需要jdk 1.6以上

make出错:ImportError: No module named java_config_2 可能因为python升级,导致java-config不可用,重新安装java-config即可

line 832: X–tag=CXX: command not found : 把thrif/libtool文件内所有的“$echo”改为”$ECHO”即可(应该是libtool版本问题)

如果过于频繁的出现问题,且现象说明是大部分软件版本过旧,那可以考虑emerge –sync更新全部软件。

比较惨的时候,emerge总是被masked,那就手动安装一些依赖库吧,比如boost:

首先,下载boost包 http://www.boost.org/users/download/ ;并解压到/usr/local下(想安装的地方)

然后,建软连接到/usr/include下:ln -s /usr/local/boost-version/boost /usr/include/boost,这样就安装完了不需要编译的部分

如果还要继续安装需要编译的部分,那么进入到boost目录,运行bootstrap.sh脚本,生成bjam,运行./bjam install即可

4、ant安装报找不到jdk

但是我已经在/etc/profile,每个用户的.bash_profile里都把JAVA_HOME指向jdk目录了,而且echo $JAVA_HOME的结果也是jdk目录。

查看ant脚本,在第一行echo $JAVA_HOME,发现为空。无奈,只能手工把javahome添加到ant脚本里。

5、hadoop版本统一以后,可以正常启动datanode了,但是tasktracker还是起不来,在log里找到:

2010-04-30 18:07:31,975 ERROR org.apache.hadoop.mapred.TaskTracker: Shutting down. Incompatible buildVersion.

JobTracker’s: 0.20.3-dev from  by ms on Thu Apr 29 17:44:22 CST 2010

TaskTracker’s: 0.20.3-dev from  by root on Fri Apr 30 17:48:14 CST 2010

解决方法:

把master的hadoop目录,整个覆盖过去!

(尝试过用ms账号重新ant hadoop,无效;即版本一致了,但是安装时间不一致也不行。。。。)

#!/usr/local/php/bin/php
<?php
#######################################################
##
#######################################################

if (1){
$url = “http://list.mp3.baidu.com/top/top100.html”;
$regex_str = ‘<td \s+>\s*<.*?>\s*(.*?)</a></td>\s*<td \s+>\s*<.*?>\s*(.*?)</a></td>';
$modifiers = ‘imx';
var_dump(ms_regex_url_content($url, $regex_str, $modifiers));

$url = ‘http://list.mp3.baidu.com/top/top500.html';
$regex_str = ‘<td \s+>\s*<.*?>\s*(.*?)</a></td>\s*<td \s+>\s*<.*?>\s*(.*?)</a></td>';
$modifiers = ‘imx';
var_dump(ms_regex_url_content($url, $regex_str, $modifiers));

$url = ‘http://list.mp3.baidu.com/top/bangping.html';
$regex_str = ‘<td \s+><.*?>(.*?)</a></td>\s*<td \s+><.*?>(.*?)</a></td>';
$modifiers = ‘imx';
var_dump(ms_regex_url_content($url, $regex_str, $modifiers));
}

function ms_regex_url_content($url, $regex_str, $modifiers=”, $user_agent=”){
if (empty($url) || empty($regex_str)){
return false;
}

$regex = ms_inner_regex_to_pattern($regex_str, $modifiers);
if (empty($regex)){
return false;
}

$content = ms_inner_get_web_content($url, $user_agent);
if (empty($content)){
return false;
}

$matches = ms_inner_regex_get_match($content, $regex);

return $matches;
}

function ms_inner_regex_get_match(&$content, $regex){/*{{{*/
if (empty($content) || empty($regex) || ms_inner_error_pattern($regex)){
return false;
}

if (preg_match_all($regex, $content, $matches, PREG_SET_ORDER) <= 0){
return false;
}

return $matches;
}/*}}}*/

function ms_inner_get_web_content($url, $user_agent=”){/*{{{*/
if (empty($url)){
return false;
}

if (empty($user_agent)){
$user_agent = ‘Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1059 Safari/532.5′;
}

// create a new cURL resource
$ch = curl_init();

// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // return content

// grab URL and pass it to the browser
$content = curl_exec($ch);
if (empty($content)){
return false;
}

$charset = ms_inner_get_charset(curl_getinfo($ch, CURLINFO_CONTENT_TYPE), $content);
if (!empty($charset) && stripos($charset, ‘utf’) === false){
$content = iconv($charset, “utf-8//IGNORE”, $content);
if (empty($content)){ // 编码转换出错
return false;
}
}

// close cURL resource, and free up system resources
curl_close($ch);

return $content;
}/*}}}*/

function ms_inner_get_charset($content_type, &$content){/*{{{*/
if (empty($content_type) && empty($content)){
return false;
}

if (preg_match(‘/\bcharset\s*=\s*(\S+)\b/i’, $content_type, $matches) > 0){
return $matches[1];
}

$content_type = $content; // 在content里查找charset
if (preg_match(‘/\bcharset\s*=\s*(\S+)\b/i’, $content_type, $matches) > 0){
return $matches[1];
}

return false;
}/*}}}*/

function ms_inner_regex_to_pattern($raw_regex, $modifiers=”){/*{{{*/
if (!preg_match(‘{\\\\(|:/|$)}’, $raw_regex)){
$cooked = preg_replace(‘!/!’, ‘\/’, $raw_regex);
} else {
$pattern = ‘{ [^\\\\/]+ | \\\\. | (/ | \\\\$ ) }sx';

$f = create_function(‘$matches’, ‘
if (empty($matches[1])){
return $matches[0];
} else {
return “\\\\” . $matches[1];
}
‘);

$cooked = preg_replace_callback($pattern, $f, $raw_regex);
}
return “/$cooked/$modifiers”;
}/*}}}*/

/* 检查正则是否错误: false — 代表可用,否则返回出错提示 */
function ms_inner_error_pattern($pattern){/*{{{*/
if ($old_track = ini_get(‘track_errors’)){
$old_message = isset($php_errormsg)? $php_errormsg: false;
} else {
ini_set(‘track_errors’, 1);
}

unset($php_errormsg);
@preg_match($pattern, “”);
$return_value = isset($php_errormsg)? $php_errormsg: false;

if ($old_track){
$php_errormsg = isset($old_message)? $old_message: false;
} else {
ini_set(‘track_errors’, 0);
}

return $return_value;
}/*}}}*/
?>

1、场景

map task的数目同split的数目相关(一般是相等),split的数目由map input文件的大小与dfs.block.size共同确定;

mapper、reducer消耗的内存、执行的效率也同其输入文件的大小紧密相关,而输入文件大小的上限是由dfs.block.size确定的;

dfs.block.size还同文件存储效率、容错、网络带宽消耗等相关(只是看文档提及过,没有深入学习呢)。

所以,有多种场景,是需要修改dfs.block.size的。我目前遇到的是场景2.

2、问题重现

hadoop fs -put local-file-path hadoop-file-path  # 这时,dfs.block.size 分配为512k

执行mapreduce程序,发现由于split过小,map task 数目很多,每个执行时间都比较短,影响到效率

修改hadoop/conf/hdfs-site.xml(也可以放置在其他路径,通过-conf指定),设置dfs.block.size为64M

再次执行mapreduce程序,查看task的webUI界面,发现map input的大小仍然是512k左右(split不保证严格精确,趋近于block size);再查看当前job的webUI中的xml配置文件,发现dfs.block.size已经被修改为64M了。

3、问题分析

为什么 配置已经生效,但是hdfs中文件的分片貌似不变呢?使用下面的命令查看具体文件的分片效果:

hadoop fsck /user/ms/hadoop-file-path -files -blocks -racks

发现其文件的分片的len不变,同修改配置之前一样。

查阅《OReilly.Hadoop.The.Definitive.Guide》,发现input的存放时候的分片实际上是在hadoop fs -put的时候执行的!

也就是说,修改dfs.block.size会影响到reducer的输入,但是map的输入,是不会被影响到的(如果没有重新put的话)。所以map的task num也不会变。

以上尝试的是把dfs.block.size从小改为大,那么如果是从大改为小呢?结论也是一样:没有影响到map 输入的分片大小。

所以,猜测,map的输入,是不计算block size,不尝试再分片的。直接从-input路径下读取分片好的blocks。

4、结论

如果修改dfs.block.size的目的是要影响map的input size,那么就需要重新put文件到input中去!

从网上下载了OReilly.Hadoop.The.Definitive.Guide,记录一些学习的心得。

因为现在虽然能在几台机器上运行hadoop的程序了,但是总是遇到各种各样的问题,没有一个整体的思路。

配置文件

推荐把conf文件移出hadoop的安装目录,并且准备3个不同的版本,例如hadoop-local.xml, hadoop-localhost.xml, and hadoop-cluster.xml。这样可以在执行hadoop程序的时候,指定不同的conf文件,在单机单线程、单机多线程、cluster模式间切换。当需要升级hadoop的时候,也不会受到影响。

ps:不要被hadoop-home/conf下的一堆配置文件吓到,其实把所有的配置放在一个配置文件里,就可以!

比如,我把hadoop目录下的conf整个cp到/my/dir/conf下,重写了它的core-size.xml。

那么启动hadoop的命令如下:

$HADOOP_HOME/bin/start-all.sh –config /my/dir/conf/ (注意,这里的conf要用绝对路径;基本上所有的hadoop参数都用绝对路径)

执行的时候命令如下:

$HADOOP_HOME/bin/hadoop fs -conf /my/dir/conf/core-site.xml -ls

善用combiner

那么combiner到底是什么?

如果mapper的输出为:

第一组:

(1925, 0)

(1925, 20)

(1925, 25)

第二组:

(1925, 25)

(1925, 15)

假设reducer会找出针对每个key的的最大value,那么reducer的输出应该是25:

(1950, [0, 20, 10, 25, 15]) =》 (1925, 25)

而如果引入了combiner:

(1950, [20, 25]) =》 (1925, 25)

也就是说,combiner会先针对每个分组计算,如果把输出作为reducer的输入。

我现在一般执行的hadoop命令如下:

hadoop jar contrib/streaming/hadoop-0.20.2-streaming.jar -mapper mapper -reducer reducer -input input -output output

这样很容易就造成内存不足,报255错误。如果引入combiner应该可以大幅降低内存的使用率。


Hadoop 0.21.0开始,就支持非java语言的combiner了。即使不支持,也可以通过管道来实现:

hadoop jar $HADOOP_INSTALL/contrib/streaming/hadoop-*-streaming.jar \

-input input/ncdc/all \

-output output \

-mapper “ch02/ruby/max_temperature_map.rb | sort | ch02/ruby/max_temperature_reduce.rb” \

-reducer src/main/ch02/ruby/max_temperature_reduce.rb \

-file src/main/ch02/ruby/max_temperature_map.rb \

-file src/main/ch02/ruby/max_temperature_reduce.rb

上面红色部分,mapper使用了管道,首先对于mapper的输出sort,然后combiner

File可以在cluster之间同步代码

内存相关的参数

mapred.child.java.opts 设置了JVM端限制的mapper、reducer程序的内存上限。

reducer的输入

mapper或者combiner等的输出,会作为reducer的输入。由于数据与数据间,会有某些联系,hadoop确保了具有同样key的数据,一定划分在同一个reducer输入中。更进一步说:MapReduce makes the guarantee that the input to every reducer is sorted by key. 这个过程,就叫做shuffle。

shuffle 过程

通过上图,可以看出,随着mapper的输出,shuffle会对它进行partition、sort,并且缓存起来,定期写入磁盘;而针对多个mapper的sort后的输出,会将同一key的merge到一起,作为reducer的输入。

在这个过程中,有很多配置参数可以调整,以提高效率:io.sort.*

如果提供了combiner的话,并且the number of spills is at least three (the value of the min.num.spills.for.combine property),那么在写入磁盘前,会调用combiner。这意味着combiner可能会被多次调用。

另外,启用压缩也会提高效率:

setting mapred.compress.map.output to true. The compression library to use is specified by mapred.map.output.compression.codec;

reducer的三个阶段

查看jobdetails.jsp,会发现一个reducer进程总是分为3个阶段:copy、sort、reduce。

copy阶段,在mapper还未结束的时候,就会开始;它不断的复制mapper的输出到磁盘,并把小分片合并为大分片。

sort阶段:在本reducer的所有输入都获取到了之后开始;

This is done in rounds. For example, if there were 50 map outputs, and the merge factor was 10 (the default, controlled by the io.sort.factor property, just like in the map’s merge), then there would be 5 rounds. Each round would merge 10 files into one, so at the end there would be five intermediate files.

所以,也可以通过调整 io.sort.factor 来调整它的效率。

reduce阶段:执行reducer程序。

避免行过长

If you are using TextInputFormat,then you can set a maximum expected line length to safeguard against corrupted files. Corruption in a file can manifest itself as a very long line, which can cause out of memory errors and then task failure. By setting mapred.linerecordreader.maxlength to a value in bytes that fits in memory (and is comfortably greater than the length of lines in your input data), the record reader will skip the (long) corrupt lines without the task failing.

可以通过设置mapred.linerecordreader.maxlength的值,来避免由于过长的行而引起的错误(比如内存错误等)。

疑问

profile task能够用在非java语言上吗?

spill、counter 怎么用?

compress怎么用?

split何时生效?

我对于dfs.block.size的配置有时总是无效:job的xml配置文件里,确实是我写入的size值,但是map的输入split大小却是原先的值。《Guide》中有如下的话:

For most jobs, a good split size tends to be the size of a HDFS block, 64 MB by default, although this can be changed for the cluster (for all newly created files), or specified when each file is created.

什么意思?难道对于clusterdfs.block.size仅在input首次创建的时候起作用吗??当执行Hadoop fs –put的时候,就会根据dfs.block.size进行分片的操作了吗??

使用下面的语句,看看结果:

% hadoop fsck /user/tom/part-00007 -files -blocks -racks

/user/ms/MsWebLog/Ex20100418_00 198 725 632 bytes, 3 block(s):  OK

0. blk_-7924980155109394173_1028 len=67 108 864 repl=1 [/default-rack/10.60.1.138:50010]

1. blk_6998001014416791788_1028 len=67108864 repl=1 [/default-rack/10.60.1.138:50010]

2. blk_4528578964978907509_1028 len=64507904 repl=1 [/default-rack/10.60.1.138:50010]

/user/ms/MsWebLog/Ex20100420_00 5738496 bytes, 2 block(s):  OK

0. blk_-7043144658142303184_1313 len=5120000 repl=1 [/default-rack/10.60.1.138:50010]

1. blk_-2037885665168033090_1313 len=618496 repl=1 [/default-rack/10.60.1.138:50010]

/user/ms/MsWebLog/Ex20100420_01 411 275 352 bytes, 81 block(s):  OK

0. blk_2587308140591116543_1314 len=5120000 repl=1 [/default-rack/10.60.1.138:50010]

1. blk_-5020593632857483951_1314 len=5120000 repl=1 [/default-rack/10.60.1.138:50010]

2. blk_-4761591232288872634_1314 len=5120000 repl=1 [/default-rack/10.60.1.138:50010]

……


reducer的输入总是sorted的

The MapReduce framework ensures that the keys are ordered, so we know

that if a key is different from the previous one, we have moved into a new key group.

这是一个很好很好的特性!!在代码中,只要寻找key group的边界,进行相应的处理,就可以获得性能和资源方面极好的提升。

HDFS 概念Block

通过dfs.block.size指定大小,默认是64MBHDFS的文件被划分为彼此独立的blocks。不同于传统意义上的block,如果一个文件的小于HDFS block,它不会占用到整个HDFS block大小的存储空间。

% hadoop fsck -files –blocks

压缩

Gzip不支持split

Bzip2支持split

貌似选择怎样的压缩方式,是否选择压缩,还有很大的讲究。待学习。

通过一个简单的例子来分析hadoop的管理页面。

1.例子描述

bin/hadoop fs -put conf/* input

[ms@chengyi hadoop-0.20.2]$ bin/hadoop fs -ls input
Found 13 items
-rw-r–r–   1 ms supergroup       3936 2010-04-19 10:53 /user/ms/input/capacity-scheduler.xml
-rw-r–r–   1 ms supergroup        535 2010-04-19 10:53 /user/ms/input/configuration.xsl
-rw-r–r–   1 ms supergroup        368 2010-04-19 10:53 /user/ms/input/core-site.xml
-rw-r–r–   1 ms supergroup       2320 2010-04-19 10:53 /user/ms/input/hadoop-env.sh
-rw-r–r–   1 ms supergroup       1245 2010-04-19 10:53 /user/ms/input/hadoop-metrics.properties
-rw-r–r–   1 ms supergroup       4190 2010-04-19 10:53 /user/ms/input/hadoop-policy.xml
-rw-r–r–   1 ms supergroup        257 2010-04-19 10:53 /user/ms/input/hdfs-site.xml
-rw-r–r–   1 ms supergroup       2815 2010-04-19 10:53 /user/ms/input/log4j.properties
-rw-r–r–   1 ms supergroup        272 2010-04-19 10:53 /user/ms/input/mapred-site.xml
-rw-r–r–   1 ms supergroup         11 2010-04-19 10:53 /user/ms/input/masters
-rw-r–r–   1 ms supergroup         11 2010-04-19 10:53 /user/ms/input/slaves
-rw-r–r–   1 ms supergroup       1243 2010-04-19 10:53 /user/ms/input/ssl-client.xml.example
-rw-r–r–   1 ms supergroup       1195 2010-04-19 10:53 /user/ms/input/ssl-server.xml.example

[ms@chengyi conf]$ wc -l *
98 capacity-scheduler.xml
24 configuration.xsl
15 core-site.xml
57 hadoop-env.sh
40 hadoop-metrics.properties
97 hadoop-policy.xml
15 hdfs-site.xml
94 log4j.properties
11 mapred-site.xml
2 masters
2 slaves
57 ssl-client.xml.example
55 ssl-server.xml.example

2.配置

master / slave = localhost

dfs.block.size=512

3.hadoop中查看一个task的页面如下:

FileSystemCounters
HDFS_BYTES_READ 4,190
HDFS_BYTES_WRITTEN 4,287
Map-Reduce Framework
Map input records 97
Spilled Records 0
Map input bytes 4,190
Map output records 97

其中,map input records指的是输入的<key-value>对的个数,map input bytes是字节数,map output records是输出的<key-value>个数。

4. 再次cp一个较大的文件到input中:

bin/hadoop fs -put ~/configure input/

[ms@chengyi hadoop-0.20.2]$ ls -l ~/configure
-rw-rw-r–. 1 ms ms 727233  4月 20 16:18 /home/ms/configure
[ms@chengyi hadoop-0.20.2]$ wc -l ~/configure
22621 /home/ms/configure

并且修改dfs.block.size= 512000.(保险起见,重启hadoop的服务)

最终分成为两个blocks

[ms@chengyi hadoop-0.20.2]$ bin/hadoop fs -ls output
Found 3 items
drwxr-xr-x   – ms supergroup          0 2010-04-20 17:36 /user/ms/output/_logs
-rw-r–r–   1 ms supergroup     373621 2010-04-20 17:36 /user/ms/output/part-00000
-rw-r–r–   1 ms supergroup     374032 2010-04-20 17:36 /user/ms/output/part-00001

可以看到,调整了dfs.block.size之后,小文件被合并为大文件了,而超出dfs.block.size的大文件则被split了。

FileSystemCounters
HDFS_BYTES_READ 4,190
HDFS_BYTES_WRITTEN 4,287
Map-Reduce Framework
Map input records 97
Spilled Records 0
Map input bytes 4,190
Map output records 97

1、经常遇到的exception是:PipeMapRed.waitOutputThreads(): subprocess failed with code 2

这里的error code会随着错误类型的不同而变化。查看源码src/contrib/streaming/src/java/org/apache/hadoop/streaming/PipeMapRed.java:

int exitVal = sim.waitFor();
if (exitVal != 0) {
if (nonZeroExitIsFailure_) {
throw new RuntimeException(“PipeMapRed.waitOutputThreads(): subprocess failed with code “
+ exitVal);
} else {
logprintln(“PipeMapRed.waitOutputThreads(): subprocess exited with code ” + exitVal
+ ” in ” + PipeMapRed.class.getName());
}
}
可以看到,这里的error code实际上是waitFor方法的返回值。
查看javahelp:http://java.sun.com/javase/7/docs/api/ 找到waitFor的返回值
Returns:
the exit value of the subprocess represented by this Process object. By convention, the value 0 indicates normal termination.

也就是说,它返回的实际上应该是子进程的返回值。我的应用场景里应该是linux系统返回值,查看http://blog.chinaunix.net/u2/61322/showart_1791012.htmlerror code 2: No such file or directory。即找不到文件或者目录。
考虑到脚本中有include、require之类的操作,查看之后,果然是绝对、相对路径的问题!

2、如果出现莫名其妙的错误,考虑脚本本身是否有问题。

比如input分片是否过大,导致后来的内容不足:

基于文件的InputFormat实现(通常是 FileInputFormat的子类) 默认行为是按照输入文件的字节大小,把输入数据切分成逻辑分块(logical InputSplit )。 其中输入文件所在的FileSystem的数据块尺寸是分块大小的上限。下限可以设置mapred.min.split.size 的值。
如果想更改inputformat的上限,可以通过设置dfs.block.size或者ulimit的file size来实现。
error code 255 == 内存不足??
java.lang.RuntimeException: PipeMapRed.waitOutputThreads(): subprocess failed with code 255

3、怎样判断是mapper还是reducer出了问题呢?


看这里的重试过程,如果reduce的百分比多次重复,则是reducer出了问题,重试的次数由

10/04/19 17:48:48 INFO streaming.StreamJob:  map 0%  reduce 0%
10/04/19 17:49:01 INFO streaming.StreamJob:  map 45%  reduce 0%
10/04/19 17:49:03 INFO streaming.StreamJob:  map 50%  reduce 0%
如果猜测是mapper出了问题,可以将reducer设为NONE,查看结果:
bin/hadoop jar contrib/streaming/hadoop-0.20.2-streaming.jar -mapper map.pl -reducer NONE -input MsTmpDir -output MsUserView

4、脚本未在cluster之间同步的问题:

java.lang.RuntimeException: Error in configuring object 

Cannot run program "cal_url_relation_reducer.php": java.io.IOException: error=2, No such file or directory

不明白为啥会报第一个错,但是肯定的是,它是由于php脚本未在cluster之间同步导致的。现象是:
在未同步该脚本的机器上,执行失败,但是在同步了该脚本的机器上(我现在是master上,就执行成功)

5、编译hadoop之后,无法启动全部节点

FATAL org.apache.hadoop.hdfs.server.datanode.DataNode: Incompatible build versions: namenode BV = ; datano  de BV = 911707

查看各个节点的版本:

master节点:

$ hadoop version

Hadoop 0.20.3-dev

Subversion  -r

Compiled by ms on Thu Apr 29 17:44:22 CST 2010

master的版本是0.20.3,自己编译过,而无法启动的slave节点的版本是0.20.2,没有编译成功。所以需要统一master和slaves的版本。

sudo /sbin/modprobe b43

查看man手册: modprobe – program to add and remove modules from the Linux Kernel

也就是说,吧b43这个模块加载到里kernel中,使其支持无线网络。

至于b43的名称,网上搜吧。。。。

https://svn01.cnmscorp.myspace.cn/svn/search/cyrecommend/v1

给Fedora 12添加宋体

用openoffice打开doc文档,我的默认字体是宋体,因为fedora没有宋体,在同一个页面上不同格式的宋体会有一部分显示为方框。以下为搜索来的,记在这里。

windows的字体存放在c:\windows\fonts目录下:simfang.ttf 仿宋体 simhei.ttf 黑体 simkai.ttf 楷体  simsun.ttc改后缀simsun.ttf 宋体和新宋体 tahoma.ttf tahoma字体 tahomabd.ttf tahoma字体的粗体形式 verdana.ttf verdana字体 verdanab.ttf verdana字体的粗体形式 verdanai.ttf verdana字体的斜体形式 verdanaz.ttf verdana字体的粗体+斜体形式

拷贝过来的字体文件放在了/usr/share/fonts/xpfonts目录下。

二、将字体加入到linux的可使用字体中

  1. cd /usr/share/fonts/xpfonts
  2. mkfontscale
  3. mkfontdir
  4. fc-cache
在openoffice中其中仿宋体的字体名是FangSong_GB2312,黑体是SimHei,楷体是KaiTi_GB2312,宋体是SimSun
zz from:http://www.colong.org/2010/01/%E7%BB%99fedora-12%E6%B7%BB%E5%8A%A0%E5%AE%8B%E4%BD%93/

比较了Pseudo-Distributed模式下的hadoop与直接运行脚本之间的效率差别。

环境:

操作系统与硬件:Linux tj1cantispam002 2.6.22-gentoo-r2-build-general-dl140 #5 SMP Wed Jan 23 12:11:40 CST 2008 x86_64 Intel(R) Xeon(R) CPU            5130  @ 2.00GHz GenuineIntel GNU/Linux

input文件大小:586M

hadoop配置:全默认配置,Pseudo-Distributed模式

时间计算方法:time命令

结果:

Pseudo-Distributed模式下的hadoop
real    0m55.044s
user    0m1.568s
sys     0m0.160s
直接运行脚本:time ./mapper.pl < Ex20100414_00 > tmp; ./reducer.pl < tmp > re.out.txt
real    0m29.449s
user    0m28.994s
sys     0m0.436s

分析:

在单机情况下,并不能发挥hadoop的优势,这只是一个学习的过程。

1、hadoop常见问题汇总

http://www.cs.brandeis.edu/~cs147a/lab/hadoop-troubleshooting/

借助这个网址,解决了下面的问题:

$bin/hadoop fs -copyFromLocal /ms/wwwroot/cyrecommand/v1/hadoopTest/urlRec/buildData/smallLog smalllog

Error: File /user/ms/smalllog could only be replicated to 0 nodes, instead of 1

对应于以下问题:

Your DataNode is started and you can create directories with bin/hadoop dfs -mkdir, but you get an error message when you try to put files into the HDFS (e.g., when you run a command like bin/hadoop dfs -put).

我在namenode的web界面:http://tj1cantispam002.cnmsprod.local:50070/dfshealth.jsp  查看了live node数目是正常的(>0);这时去hadoop-username下看了看,硬盘满了。

清理了磁盘,释放了空间,就可以了。

2、网址

http://hadoop.apache.org/common/docs/r0.18.2/cn/hdfs_user_guide.html 中文的帮助手册

http://hadoop.apache.org/common/docs/current/commands_manual.html 英文的帮助手册

http://hadoop.apache.org/common/docs/current/hadoop-default.html 默认配置文件

http://hadoop.apache.org/common/docs/current/ 当前版本的hadoop文档

http://wiki.apache.org/hadoop/首页 hadoop的中文wiki

http://hadoop-server:50070/dfshealth.jsp 查看namenode、datanode的状态(端口号根据配置变化)

http://hadoop-server:50030/jobtracker.jsp 查看jobtracker的状态(端口号根据配置变化)

3、自带资源

Hadoop configuration is driven by two types of important configuration files:

  1. Read-only default configuration – src/core/core-default.xmlsrc/hdfs/hdfs-default.xml and src/mapred/mapred-default.xml.
  2. Site-specific configuration – conf/core-site.xmlconf/hdfs-site.xml and conf/mapred-site.xml.

To learn more about how the Hadoop framework is controlled by these configuration files, look here.

Additionally, you can control the Hadoop scripts found in the bin/ directory of the distribution, by setting site-specific values via the conf/hadoop-env.sh.