Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2490853
  • 博文数量: 709
  • 博客积分: 12251
  • 博客等级: 上将
  • 技术积分: 7905
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-17 00:00
个人简介

实现有价值的IT服务

文章存档

2012年(7)

2011年(147)

2009年(3)

2008年(5)

2007年(74)

2006年(431)

2005年(42)

分类: IT职场

2007-04-23 15:16:23

Hadoop入门
单节点的安装与测试
1 下载hadoop的相应版本
2 设置conf目录下的hadoop-env.sh文件,最低要求是要配置JAVA_HOME这个环境变量
3 设置conf目录下的hadoop-site.xml配置文件,


fs.default.name
localhost:9000


mapred.job.tracker
localhost:9001


dfs.replication
1


4 配置本机的公私密钥对
ssh-keygen -t dsa -P '' -f ~/.ssh/id_dsa
cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
5 格式化namenode
bin/hadoop namenode -format
6 启动hadoop
bin/start-all.sh
7 导入数据到DFS
============================================================================
Hadoop学习笔记二 安装部署
本文主要是以安装和使用hadoop-0.12.0为例,指出在部署Hadoop的时候容易遇到的问题以及如何解决。

硬件环境
共有3台机器,均使用的FC5系统,Java使用的是jdk1.6.0。IP配置如下:
dbrg-1:202.197.18.72
dbrg-2:202.197.18.73
dbrg-3:202.197.18.74
这里有一点需要强调的就是,务必要确保每台机器的主机名和IP地址之间能正确解析。
一个很简单的测试办法就是ping一下主机名,比如在dbrg-1上ping dbrg-2,如果能ping通就OK!若不能正确解析,可以修改/etc/hosts文件,如果该台机器作Namenode用,则需要在hosts文件中加上集群中所有机器的IP地址及其对应的主机名;如果该台机器作Datanode用,则只需要在hosts文件中加上本机IP地址和Namenode机器的IP地址。
以本文为例,dbrg-1中的/etc/hosts文件看起来就应该是这样的:
127.0.0.0         localhost     localhost
202.197.18.72     dbrg-1        dbrg-1
202.197.18.73     dbrg-2        dbrg-2
202.197.18.74     dbrg-3        dbrg-3
dbrg-2中的/etc/hosts文件看起来就应该是这样的:
127.0.0.0         localhost    localhost
202.197.18.72     dbrg-1       dbrg-1
202.197.18.73     dbrg-2       dbrg-2
在上一篇学习笔记中提到过,对于Hadoop来说,在HDFS看来,节点分为Namenode和Datanode,其中Namenode只有一个,Datanode可以是很多;在MapReduce看来,节点又分为Jobtracker和Tasktracker,其中Jobtracker只有一个,Tasktracker可以是很多。
我是将namenode和jobtracker部署在dbrg-1上,dbrg-2,dbrg-3作为datanode和tasktracker。当然你也可以将namenode,datanode,jobtracker,tasktracker全部部署在一台机器上

目录结构
由于Hadoop要求所有机器上hadoop的部署目录结构要相同,并且都有一个相同的用户名的帐户。
我的三台机器上是这样的:都有一个dbrg的帐户,主目录是/home/dbrg
Hadoop部署目录结构如下:/home/dbrg/HadoopInstall,所有的hadoop版本放在这个目录中。
将hadoop0.12.0压缩包解压至HadoopInstall中,为了方便以后升级,建议建立一个链接指向要使用的hadoop版本,不妨设为hadoop
[dbrg@dbrg-1:HadoopInstall]$ln  -s  hadoop0.12.0   hadoop
这样一来,所有的配置文件都在/hadoop/conf/目录中,所有执行程序都在/hadoop/bin目录中。
但是由于上述目录中hadoop的配置文件和hadoop的安装目录是放在一起的,这样一旦日后升级hadoop版本的时候所有的配置文件都会被覆盖,因此建议将配置文件与安装目录分离,一种比较好的方法就是建立一个存放配置文件的目录,/home/dbrg/HadoopInstall/hadoop-config/,然后将/hadoop/conf/目录中的hadoop_site.xml,slaves,hadoop_env.sh三个文件拷贝到hadoop-config/目录中(这个问题很奇怪,在官网上的Getting Started With Hadoop中说是只需要拷贝这个三个文件到自己创建的目录就可以了,但我在实际配置的时候发现还必须把masters这个文件也拷贝到hadoop-conf/目录中才行,不然启动Hadoop的时候就会报错说找不到masters这个文件),并指定环境变量$HADOOP_CONF_DIR指向该目录。环境变量在/home/dbrg/.bashrc和/etc/profile中设定。
综上所述,为了方便以后升级版本,我们需要做到配置文件与安装目录分离,并通过设定一个指向我们要使用的版本的hadoop的链接,这样可以减少我们对配置文件的维护。在下面的部分,你就会体会到这样分离以及链接的好处了。

SSH设置
在Hadoop启动以后,Namenode是通过SSH(Secure Shell)来启动和停止各个节点上的各种守护进程的,这就需要在节点之间执行指令的时候是不需要输入密码的方式,故我们需要配置SSH使用无密码公钥认证的方式。
首先要保证每台机器上都装了SSH服务器,且都正常启动。实际中我们用的都是OpenSSH,这是SSH协议的一个免费开源实现。FC5中默认安装的OpenSSH版本是OpenSSH4.3P2。
以本文中的三台机器为例,现在dbrg-1是主节点,它需要主动发起SSH连接到dbrg-2和dbrg-3,对于SSH服务来说,dbrg-1就是SSH客户端,而dbrg-2、dbrg-3则是SSH服务端,因此在dbrg-2,dbrg-3上需要确定sshd服务已经启动。简单的说,在dbrg-1上需要生成一个密钥对,即一个私钥,一个公钥。将公钥拷贝到dbrg-2,dbrg-3上,这样,比如当dbrg-1向dbrg-2发起ssh连接的时候,dbrg-2上就会生成一个随机数并用dbrg-1的公钥对这个随机数进行加密,并发送给dbrg-1;dbrg-1收到这个加密的数以后用私钥进行解密,并将解密后的数发送回dbrg-2,dbrg-2确认解密的数无误后就允许dbrg-1进行连接了。这就完成了一次公钥认证过程。
对于本文中的三台机器,首先在dbrg-1上生成密钥对:
[dbrg@dbrg-1:~]$ssh-keygen  -t  rsa
这个命令将为dbrg-1上的用户dbrg生成其密钥对,询问其保存路径时直接回车采用默认路径,当提示要为生成的密钥输入passphrase的时候,直接回车,也就是将其设定为空密码。生成的密钥对id_rsa,id_rsa.pub,默认存储在/home/dbrg/.ssh目录下。然后将id_rsa.pub的内容复制到每个机器(也包括本机)的/home/dbrg/.ssh/authorized_keys文件中,如果机器上已经有authorized_keys这个文件了,就在文件末尾加上id_rsa.pub中的内容,如果没有authorized_keys这个文件,直接cp或者scp就好了,下面的操作假设各个机器上都没有authorized_keys文件。
对于dbrg-1
[dbrg@dbrg-1:.ssh]$cp id_rsa.pub authorized_keys
对于dbrg-2(dbrg-3同dbrg-2的方法)
[dbrg@dbrg-2:~]$mkdir .ssh
[dbrg@dbrg-1:.ssh]$scp authorized_keys dbrg-2:/home/dbrg/.ssh/
此处的scp就是通过ssh进行远程copy,此处需要输入远程主机的密码,即dbrg-2机器上dbrg帐户的密码,当然,你也可以用其他方法将authorized_keys文件拷贝到其他机器上
[dbrg@dbrg-2:.ssh]$chmod 644 authorized_keys
这一步非常关键,必须保证authorized_keys只对其所有者有读写权限,其他人不允许有写的权限,否则SSH是不会工作的。我就曾经在配置SSH的时候郁闷了好久。
[dbrg@dbrg-2:.ssh]ls -la
drwx------ 2 dbrg dbrg .
drwx------ 3 dbrg dbrg ..
 -rw-r--r-- 1 dbrg dbrg authorized_keys
注意每个机器上的.ssh目录的ls -la都应该和上面是一样的
接着,在三台机器上都需要对sshd服务进行配置(其实是可以不用配置的,完成了上面的那些操作了以后SSH就已经可以工作了),在三台机器上修改文件/etc/ssh/sshd_config
#去除密码认证
PasswordAuthentication  no
AuthorizedKeyFile   .ssh/authorized_keys
至此各个机器上的SSH配置已经完成,可以测试一下了,比如dbrg-1向dbrg-2发起ssh连接
[dbrg@dbrg-1:~]$ssh  dbrg-2
如果ssh配置好了,就会出现以下提示信息
The authenticity of host [dbrg-2] can't be established.
Key fingerprint is 1024 5f:a0:0b:65:d3:82:df:ab:44:62:6d:98:9c:fe:e9:52.
Are you sure you want to continue connecting (yes/no)?
OpenSSH告诉你它不知道这台主机,但是你不用担心这个问题,因为你是第一次登录这台主机。键入“yes”。这将把这台主机的“识别标记”加到“~/.ssh/know_hosts”文件中。第二次访问这台主机的时候就不会再显示这条提示信息了。
然后你会发现不需要输入密码就可以建立ssh连接了,恭喜你,配置成功了
不过,别忘了测试本机ssh  dbrg-1

Hadoop环境变量
在/home/dbrg/HadoopInstall/hadoop-conf目录下的hadoop_env.sh中设置Hadoop需要的环境变量,其中JAVA_HOME是必须设定的变量。HADOOP_HOME变量可以设定也可以不设定,如果不设定,HADOOP_HOME默认的是bin目录的父目录,即本文中的/home/dbrg/HadoopInstall/hadoop。我的是这样设置的
export  HADOOP_HOME=/home/dbrg/HadoopInstall/hadoop
export  JAVA_HOME=/usr/java/jdk1.6.0
从这个地方就可以看出前面所述的创建hadoop0.12.0的链接hadoop的优点了,当以后更新hadoop的版本的时候,就不需要在改配置文件,只需要更改链接就可以了。

Hadoop配置文件
如前所述,在hadoop-conf/目录下,打开slaves文件,该文件用来指定所有的从节点,一行指定一个主机名。即本文中的dbrg-2,dbrg-3,因此slaves文件看起来应该是这样的
dbrg-2
dbrg-3
在conf/目录中的hadoop-default.xml中包含了Hadoop的所有配置项,但是不允许直接修改!可以在hadoop-conf/目录下的hadoop-site.xml里面定义我们需要的项,其值会覆盖hadoop-default.xml中的默认值。可以根据自己的实际需要来进行定制。以下是我的配置档:





  fs.default.name
  dbrg-1:9000
  The name of the default file system. Either the literal string "local" or a host:port for DFS.


  mapred.job.tracker
  dbrg-1:9001
  The host and port that the MapReduce job tracker runs at. If "local", then jobs are run in-process as a single map and reduce task.


  hadoop.tmp.dir
  /home/dbrg/HadoopInstall/tmp
  A base for other temporary directories.


  dfs.name.dir
  /home/dbrg/HadoopInstall/filesystem/name
  Determines where on the local filesystem the DFS name node should store the name table. If this is a comma-delimited list of directories then the name table is replicated in all of the directories, for redundancy.


  dfs.data.dir
  /home/dbrg/HadoopInstall/filesystem/data
  Determines where on the local filesystem an DFS data node should store its blocks. If this is a comma-delimited list of directories, then data will be stored in all named directories, typically on different devices. Directories that do not exist are ignored.


  dfs.replication
  1
  Default block replication. The actual number of replications can be specified when the file is created. The default is used if replication is not specified in create time.

     
部署Hadoop
前面讲的这么多Hadoop的环境变量和配置文件都是在dbrg-1这台机器上的,现在需要将hadoop部署到其他的机器上,保证目录结构一致。
[dbrg@dbrg-1:~]$scp  -r  /home/dbrg/HadoopInstall  dbrg-2:/home/dbrg/
[dbrg@dbrg-1:~]$scp  -r  /home/dbrg/HadoopInstall  dbrg-3:/home/dbrg/
至此,可以说,Hadoop已经在各个机器上部署完毕了下面就让我们开始启动Hadoop吧

启动Hadoop
启动之前,我们先要格式化namenode,先进入~/HadoopInstall/hadoop目录,执行下面的命令
[dbrg@dbrg-1:hadoop]$bin/hadoop  namenode  -format
不出意外,应该会提示格式化成功。如果不成功,就去hadoop/logs/目录下去查看日志文件
下面就该正式启动hadoop啦,在bin/下面有很多启动脚本,可以根据自己的需要来启动。
* start-all.sh 启动所有的Hadoop守护。包括namenode, datanode, jobtracker, tasktrack
* stop-all.sh 停止所有的Hadoop
* start-mapred.sh 启动Map/Reduce守护。包括Jobtracker和Tasktrack
* stop-mapred.sh 停止Map/Reduce守护
* start-dfs.sh 启动Hadoop DFS守护.Namenode和Datanode
* stop-dfs.sh 停止DFS守护
在这里,简单启动所有守护
[dbrg@dbrg-1:hadoop]$bin/start-all.sh
同样,如果要停止hadoop,则
[dbrg@dbrg-1:hadoop]$bin/stop-all.sh

HDFS操作
运行bin/目录的hadoop命令,可以查看Haoop所有支持的操作及其用法,这里以几个简单的操作为例。
建立目录
[dbrg@dbrg-1:hadoop]$bin/hadoop  dfs  -mkdir  testdir
在HDFS中建立一个名为testdir的目录
复制文件
[dbrg@dbrg-1:hadoop]$bin/hadoop  dfs  -put  /home/dbrg/large.zip  testfile.zip
把本地文件large.zip拷贝到HDFS的根目录/user/dbrg/下,文件名为testfile.zip
查看现有文件
[dbrg@dbrg-1:hadoop]$bin/hadoop  dfs  -ls
=================================================================================
 Hadoop-- 海量文件的分布式计算处理方案
  Hadoop 是Google MapReduce的一个Java实现。MapReduce是一种简化的分布式编程模式,让程序自动分布到一个由普通机器组成的超大集群上并发执行。就如同java程序员可以不考虑内存泄露一样, MapReduce的run-time系统会解决输入数据的分布细节,跨越机器集群的程序执行调度,处理机器的失效,并且管理机器之间的通讯请求。这样的模式允许程序员可以不需要有什么并发处理或者分布式系统的经验,就可以处理超大的分布式系统得资源。
一、概论
    作为Hadoop程序员,他要做的事情就是:
    1、定义Mapper,处理输入的Key-Value对,输出中间结果。
    2、定义Reducer,可选,对中间结果进行规约,输出最终结果。
    3、定义InputFormat 和OutputFormat,可选,InputFormat将每行输入文件的内容转换为Java类供Mapper函数使用,不定义时默认为String。
    4、定义main函数,在里面定义一个Job并运行它。
   
    然后的事情就交给系统了。
    1.基本概念:Hadoop的HDFS实现了google的GFS文件系统,NameNode作为文件系统的负责调度运行在master,DataNode运行在每个机器上。同时Hadoop实现了Google的MapReduce,JobTracker作为MapReduce的总调度运行在master,TaskTracker则运行在每个机器上执行Task。
    2.main()函数,创建JobConf,定义Mapper,Reducer,Input/OutputFormat 和输入输出文件目录,最后把Job提交給JobTracker,等待Job结束。
    3.JobTracker,创建一个InputFormat的实例,调用它的getSplits()方法,把输入目录的文件拆分成FileSplist作为Mapper task 的输入,生成Mapper task加入Queue。
    4.TaskTracker 向 JobTracker索求下一个Map/Reduce。
     
     Mapper Task先从InputFormat创建RecordReader,循环读入FileSplits的内容生成Key与Value,传给Mapper函数,处理完后中间结果写成SequenceFile.
     Reducer Task 从运行Mapper的TaskTracker的Jetty上使用http协议获取所需的中间内容(33%),Sort/Merge后(66%),执行Reducer函数,最后按照OutputFormat写入结果目录。
      TaskTracker 每10秒向JobTracker报告一次运行情况,每完成一个Task10秒后,就会向JobTracker索求下一个Task。
      Nutch项目的全部数据处理都构建在Hadoop之上,详见Scalable Computing with Hadoop。

    二、程序员编写的代码
    我们做一个简单的分布式的Grep,简单对输入文件进行逐行的正则匹配,如果符合就将该行打印到输出文件。因为是简单的全部输出,所以我们只要写Mapper函数,不用写Reducer函数,也不用定义Input/Output Format。
package  demo.hadoop
public   class  HadoopGrep {
  public   static   class  RegMapper  extends  MapReduceBase  implements  Mapper {
   private  Pattern pattern;
   public   void  configure(JobConf job) {
   pattern  =  Pattern.compile(job.get( " mapred.mapper.regex " ));
  }
   public   void  map(WritableComparable key, Writable value, OutputCollector output, Reporter reporter)
     throws  IOException {
   String text  =  ((Text) value).toString();
   Matcher matcher  =  pattern.matcher(text);
    if  (matcher.find()) {
    output.collect(key, value);
   }
  }
 }
  private  HadoopGrep () {
 }  //  singleton
  public   static   void  main(String[] args)  throws  Exception {
 
  JobConf grepJob  =   new  JobConf(HadoopGrep. class );
  grepJob.setJobName( " grep-search " );
  grepJob.set( " mapred.mapper.regex " , args[ 2 ]);
  grepJob.setInputPath( new  Path(args[ 0 ]));
  grepJob.setOutputPath( new  Path(args[ 1 ]));
  grepJob.setMapperClass(RegMapper. class );
  grepJob.setReducerClass(IdentityReducer. class );
     
  JobClient.runJob(grepJob);
 }
}
        RegMapper类的configure()函数接受由main函数传入的查找字符串,map() 函数进行正则匹配,key是行数,value是文件行的内容,符合的文件行放入中间结果。
        main()函数定义由命令行参数传入的输入输出目录和匹配字符串,Mapper函数为RegMapper类,Reduce函数是什么都不做,直接把中间结果输出到最终结果的的IdentityReducer类,运行Job。

        整个代码非常简单,丝毫没有分布式编程的任何细节。

       三.运行Hadoop程序
        Hadoop这方面的文档写得不全面,综合参考GettingStartedWithHadoop 与Nutch Hadoop Tutorial 两篇后,再碰了很多钉子才终于完整的跑起来了,记录如下:     
3.1 local运行模式
       完全不进行任何分布式计算,不动用任何namenode,datanode的做法,适合一开始做调试代码。
       解压hadoop,其中conf目录是配置目录,hadoop的配置文件在hadoop-default.xml,如果要修改配置,不是直接修改该文件,而是修改hadoop-site.xml,将该属性在hadoop-site.xml里重新赋值。
       hadoop-default.xml的默认配置已经是local运行,不用任何修改,配置目录里唯一必须修改的是hadoop-env.sh 里JAVA_HOME的位置。

       将编译好的HadoopGrep与RegMapper.class 放入hadoop/build/classes/demo/hadoop/目录 找一个比较大的log文件放入一个目录,然后运行

       hadoop / bin / hadoop demo.hadoop.HadoopGrep log文件所在目录 任意的输出目录 grep的字符串
     查看输出目录的结果,查看hadoop/logs/里的运行日志。 
     在重新运行前,先删掉输出目录。
 
3.2 单机集群运行模式
       现在来搞一下只有单机的集群.假设以完成3.1中的设置,本机名为hadoopserver
       第1步.    然后修改hadoop-site.xml ,加入如下内容:
< property >
   < name > fs.default.name
   < value > hadoopserver:9000

< property >
   < name > mapred.job.tracker
   < value > hadoopserver:9001

< property >
   < name > dfs.replication
   < value > 1
    从此就将运行从local文件系统转向了hadoop的hdfs系统,mapreduce的jobtracker也从local的进程内操作变成了分布式的任务系统,9000,9001两个端口号是随便选择的两个空余端口号。
 
  另外,如果你的/tmp目录不够大,可能还要修改hadoop.tmp.dir属性。

  第2步. 增加ssh不输入密码即可登陆。
    因为Hadoop需要不用输入密码的ssh来进行调度,在不su的状态下,在自己的home目录运行ssh-keygen -t rsa ,然后一路回车生成密钥,再进入.ssh目录,cp id_rsa.pub authorized_keys
    详细可以man 一下ssh, 此时执行ssh hadoopserver,不需要输入任何密码就能进入了。
  3.格式化namenode,执行
  bin/hadoop namenode -format
  4.启动Hadoop
     执行hadoop/bin/start-all.sh, 在本机启动namenode,datanode,jobtracker,tasktracker
 
  5.现在将待查找的log文件放入hdfs,。
     执行hadoop/bin/hadoop dfs 可以看到它所支持的文件操作指令。
     执行hadoop/bin/hadoop dfs put log文件所在目录 in ,则log文件目录已放入hdfs的/user/user-name/in 目录中
  6.现在来执行Grep操作
      hadoop/bin/hadoop demo.hadoop.HadoopGrep in out
      查看hadoop/logs/里的运行日志,重新执行前。运行hadoop/bin/hadoop dfs rmr out 删除out目录。
  7.运行hadoop/bin/stop-all.sh 结束
  3.3 集群运行模式
  假设已执行完3.2的配置,假设第2台机器名是hadoopserver2
  1.创建与hadoopserver同样的执行用户,将hadoop解压到相同的目录。
  2.同样的修改haoop-env.sh中的JAVA_HOME 及修改与3.2同样的hadoop-site.xml
  3. 将hadoopserver中的/home/username/.ssh/authorized_keys 复制到hadoopserver2,保证hadoopserver可以无需密码登陆hadoopserver2
     scp /home/username/.ssh/authorized_keys 
 
  4.修改hadoop-server的hadoop/conf/slaves文件, 增加集群的节点,将localhost改为
    hadoop-server
    hadoop-server2
  5.在hadoop-server执行hadoop/bin/start-all.sh
   将会在hadoop-server启动namenode,datanode,jobtracker,tasktracker
   在hadoop-server2启动datanode 和tasktracker
 
  6.现在来执行Grep操作
     hadoop/bin/hadoop demo.hadoop.HadoopGrep in out
    重新执行前,运行hadoop/bin/hadoop dfs rmr out 删除out目录
  7.运行hadoop/bin/stop-all.sh 结束。
   
四、效率
    经测试,Hadoop并不是万用灵丹,很取决于文件的大小和数量,处理的复杂度以及群集机器的数量,相连的带宽,当以上四者并不大时,hadoop优势并不明显。
    比如,不用hadoop用java写的简单grep函数处理100M的log文件只要4秒,用了hadoop local的方式运行是14秒,用了hadoop单机集群的方式是30秒,用双机集群10M网口的话更慢,慢到不好意思说出来的地步。
=======================================================================
 转:hadoop 试用
Statistic.java
1. 初始化配置文件,临时文件存放目录,还有具体的Job。
        Configuration defaults = new Configuration();
        File tempDir = new File("tmp/stat-temp-"+Integer.toString(
                new Random().nextInt(Integer.MAX_VALUE)));
        JobConf statJob = new JobConf(defaults, Statistic.class);
2. 设置Job的相关参数
        statJob.setJobName("StatTestJob");
        statJob.setInputDir(new File("tmp/input/"));
        statJob.setMapperClass(StatMapper.class);
        statJob.setReducerClass(StatReducer.class);
        statJob.setOutputDir(tempDir);
3. 运行Job,清理临时文件
        JobClient.runJob(statJob);
        new JobClient(defaults).getFs().delete(tempDir);
4. 运行这个Demo,会输出
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
Key: 0, Value: For the latest information about Hadoop, please visit our website at:
Key: 70, Value:
Key: 71, Value:   
Key: 107, Value:
Key: 108, Value: and our wiki, at:
Key: 126, Value:
Key: 127, Value:   
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing build\test\mapred\local\job_lck1iq.xml\localRunner
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151414 Running job: job_lck1iq
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing build\test\mapred\local\job_lck1iq.xml\localRunner
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151414 E:\workground\opensource\hadoop-nightly\tmp\input\README.txt:0+161
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151414 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151414 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151415 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151415 parsing build\test\mapred\local\job_lck1iq.xml\localRunner
060328 151415 parsing file:/E:/workground/opensource/hadoop-nightly/bin/mapred-default.xml
060328 151415 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
060328 151415 reduce > reduce
060328 151415  map 100%  reduce 100%
060328 151415 Job complete: job_lck1iq
060328 151415 parsing jar:file:/E:/workground/opensource/hadoop-nightly/hadoop-nightly.jar!/hadoop-default.xml
060328 151415 parsing file:/E:/workground/opensource/hadoop-nightly/bin/hadoop-site.xml
5. 分析一下输出。
刚开始hadoop加载了一大堆配置文件,这里先不管。接着程序对 tmp/input/ 下面的 readme.txt 进行了解析,调用我的 StatMapper.map(WritableComparable key, Writable value, OutputCollector output, Reporter reporter) ,程序输出了key和value的值。可见key是当前指针在文件中的位置,而value是当前行的内容。接着还看到了解析xml文件的log,估计是程序框架启动了多个线程来进行操作,提高效率。
因为我的 StatMapper 只输出key-value,没有做其它事情,reduce这步被略过了。
6. 想办法让Reduce执行,看来要在StatMapper那里动动手脚。
StatMapper.java:
    public void map(WritableComparable key, Writable value, OutputCollector output, Reporter reporter)
            throws IOException
    {
        String tokenLength = String.valueOf(value.toString().split(" ").length);
        output.collect(new UTF8(""), new LongWritable(1));
    }
每行的单词数作key,1作value提交给output.collect(),这样应该就能够统计文件里面每行的单词数频率了。
7. 接着还要修改Statistic.java:
        statJob.setOutputDir(tempDir);
        statJob.setOutputFormat(SequenceFileOutputFormat.class);
        statJob.setOutputKeyClass(UTF8.class);
        statJob.setOutputValueClass(LongWritable.class);
8. 以及StatReducer.java:
    public void reduce(WritableComparable key, Iterator values, OutputCollector output, Reporter reporter)
            throws IOException
    {
        long sum = 0;
        while (values.hasNext())
        {
            sum += ((LongWritable)values.next()).get();
        }
        System.out.println("Length: " + key + ", Count: " + sum);
        output.collect(key, new LongWritable(sum));
    }
9. 放一堆java文件到input目录下面,再次运行。
(省略无数的xml解析log)
Length: 0, Count: 359
Length: 1, Count: 3474
Length: 10, Count: 1113
Length: 11, Count: 1686
Length: 12, Count: 1070
Length: 13, Count: 1725
Length: 14, Count: 773
Length: 15, Count: 707
Length: 16, Count: 490
Length: 17, Count: 787
Length: 18, Count: 348
Length: 19, Count: 303
Length: 2, Count: 1543
Length: 20, Count: 227
Length: 21, Count: 421
Length: 22, Count: 155
Length: 23, Count: 143
Length: 24, Count: 109
Length: 25, Count: 219
Length: 26, Count: 83
Length: 27, Count: 70
Length: 28, Count: 55
Length: 29, Count: 107
Length: 3, Count: 681
Length: 30, Count: 53
Length: 31, Count: 43
Length: 32, Count: 38
Length: 33, Count: 66
Length: 34, Count: 36
Length: 35, Count: 26
Length: 36, Count: 42
Length: 37, Count: 52
Length: 38, Count: 32
Length: 39, Count: 33
Length: 4, Count: 236
Length: 40, Count: 17
Length: 41, Count: 40
Length: 42, Count: 15
Length: 43, Count: 23
Length: 44, Count: 14
Length: 45, Count: 27
Length: 46, Count: 15
Length: 47, Count: 30
Length: 48, Count: 2
Length: 49, Count: 18
Length: 5, Count: 1940
Length: 50, Count: 8
Length: 51, Count: 11
Length: 52, Count: 2
Length: 53, Count: 5
Length: 54, Count: 2
Length: 55, Count: 1
Length: 57, Count: 4
Length: 58, Count: 1
Length: 59, Count: 3
Length: 6, Count: 1192
Length: 60, Count: 1
Length: 61, Count: 4
Length: 62, Count: 1
Length: 63, Count: 3
Length: 66, Count: 1
Length: 7, Count: 1382
Length: 8, Count: 1088
Length: 9, Count: 2151
060328 154741 reduce > reduce
060328 154741  map 100%  reduce 100%
060328 154741 Job complete: job_q618hy
Cool,统计出来了,但是这些数据有什么意义............. 看来该搞一个(2)来捣鼓一些更有意义的事情了
 
之前做的Demo太无聊了,决心改造一下~~
1.  输入格式。
之前的程序,StatMapper莫名其妙被输入了一堆key,value,应该是一种默认的输入格式,找了一下,原来是这个: org.apache.hadoop.mapred.InputFormatBase,  继承了InputFormat接口。接口里面有一个
  FileSplit[] getSplits(FileSystem fs, JobConf job, int numSplits)
    throws IOException;
看来所有输入输出都必须以文件为单位存放了,就像Lucene一样。一般输入数据都是按照行来分隔的,看来一般用这个InputFormatBase就可以了。
2. 输入数据。
这东东本来就是用来高效处理海量数据的,于是我想到了那iHome的ActionLog....,加起来好几百个M的,符合要求吧。这里统计一下这几天,指令被调用的次数。
3. 修改程序。
StatMapper.java:
    public void map(WritableComparable key, Writable value, OutputCollector output, Reporter reporter)
            throws IOException
    {
        String[] token = value.toString().split(" ");
        String "id"= token[6];
        String act = token[7];
        output.collect(new UTF8(act), new LongWritable(1));
    }
StatReducer.java:
    public void reduce(WritableComparable key, Iterator values, OutputCollector output, Reporter reporter)
            throws IOException
    {
        long sum = 0;
        while (values.hasNext())
        {
            sum += ((LongWritable)values.next()).get();
        }
        System.out.println("Action: " + key + ", Count: " + sum);
        output.collect(key, new LongWritable(sum));
    }
4. 运行。
这回日志看清楚了:
...
060328 162626 E:\workground\opensource\hadoop-nightly\tmp\input\action_log.txt.2006-03-21:0+21357898
060328 162626  map 8%  reduce 0%
060328 162627 E:\workground\opensource\hadoop-nightly\tmp\input\action_log.txt.2006-03-21:0+21357898
060328 162627  map 22%  reduce 0%
060328 162628 E:\workground\opensource\hadoop-nightly\tmp\input\action_log.txt.2006-03-21:0+21357898
060328 162628  map 37%  reduce 0%
060328 162629 E:\workground\opensource\hadoop-nightly\tmp\input\action_log.txt.2006-03-21:0+21357898
060328 162629  map 52%  reduce 0%
060328 162630 E:\workground\opensource\hadoop-nightly\tmp\input\action_log.txt.2006-03-21:0+21357898
060328 162631 E:\workground\opensou
阅读(4270) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2008-04-29 15:10:11

gan ni mama