概论
    作为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程序
3.1实践前准备:
 3.1.1.下载hadoop程序  用户制作的程序
 3.1.2.两台机器:
           a.主控机器server 192.168.0.52

           b.客户机client  192.168.0.53

3.2.非集群方式:
      把hadoop程序 用户制作的程序 上传到linux机器上 使hadoop/bin目录下的脚本具有可执行权限
      完全不进行任何分布式计算,不动用任何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.3.单机集群:
  第1步.    然后修改hadoop-site.xml ,加入如下内容:

< property > 
   < name > fs.default.name </ name > 
   < value > server:9000 </ value >   //指定名字节点服务
</ property > 
< property > 
   < name > mapred.job.tracker </ name > 
   < value > server:9001 </ value > //指定合并节点服务
</ property > 
< property > 
   < name > dfs.replication </ name > 
   < value > 1 </ value > 
</ property >

    从此就将运行从local文件系统转向了hadoop的hdfs系统,mapreduce的jobtracker也从local的进程内操作变成了分布式的任务系统,9000,9001两个端口号是随便选择的两个空余端口号。 
  另外,如果你的/tmp目录不够大,可能还要修改hadoop.tmp.dir属性。
第2步. 增加ssh不输入密码即可登陆。
A为本地主机(即用于控制其他主机的机器) ;
B为远程主机(即被控制的机器Server), 假如ip为192.168.60.110;
A和B的系统都是Linux

在A上运行命令:

# ssh-keygen -t rsa (连续三次回车,即在本地生成了公钥和私钥,不设置密码)
# ssh root@192.168.60.110 "mkdir .ssh" (需要输入密码)
# scp ~/.ssh/id_rsa.pub root@192.168.60.110:.ssh/id_rsa.pub (需要输入密码)  //用这种方法实现把公匙复制被控制机器上

在B上的命令:

# touch /root/.ssh/authorized_keys (如果已经存在这个文件, 跳过这条)
# cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys (将id_rsa.pub的内容追加到authorized_keys 中)

回到A机器:
# ssh -i 私鈅路径 root@192.168.60.110 (不需要密码, 登录成功)     //-i 参数指定本地密钥,如果私匙在.ssh中,且名称为id_rsa,则不用i指定密匙位置
经典举例:,

首先 ssh-keygen -t rsa 命令生成了一个密钥和一个公钥, 而且密钥可以设置自己的密码

可以把密钥理解成一把钥匙, 公钥理解成这把钥匙对应的锁头,

把锁头(公钥)放到想要控制的server上, 锁住server, 只有拥有钥匙(密钥)的人, 才能打开锁头, 进入server并控制

而对于拥有这把钥匙的人, 必需得知道钥匙本身的密码,才能使用这把钥匙 (除非这把钥匙没设置密码), 这样就可以防止钥匙被了配了(私钥被人复制)   [比较经典]
   
上面是一个通用规则:

  本机运行只需要把id_rsa.pub追加到authorized_keys中即可
这样使用ssh root@server即可以不用输入密码登录

  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.把hdfs中的目的文件取到本地:
    执行hadoop/bin/hadoop dfs -get out 本地路径
  8.运行hadoop/bin/stop-all.sh 结束

3.4.多机集群:
 
  进行3.3的所有配置后,在执行以下步骤
  第一步:  把hadoop 和应用程序 以及配置文件 完全拷贝到要加入成为节点服务器的client机器  第二步: 修改server机器上的slaves(中文奴隶的意思)文件,这个文件中一行表示一个节点 把client  ip地址加入
          例如:
               localhost 或server或192.168.0.52
               client 或192.168.0.53
  第三步: 按照3.3中所讲的方法实现从server不用输入密码登录到client机器
   方法是:在server机器上运行命令

            scp /root/.ssh/id_rsa.pu root@client:.ssh            让输入登录到clint密码,输入即可
          然后登录到client机器运行命令:
           cp /root/.ssh/id_rsa.pub /root/.ssh/authorized_keys
     即可
 第四步:在hadoop-server执行hadoop/bin/start-all.sh
   将会在hadoop-server启动namenode,datanode,jobtracker,tasktracker
   在hadoop-server2启动datanode 和tasktracker
 
 第五步.现在来执行Grep操作
     hadoop/bin/hadoop demo.hadoop.HadoopGrep in out
    重新执行前,运行hadoop/bin/hadoop dfs rmr out 删除out目录

 第六步.运行hadoop/bin/stop-all.sh 结束
             
提示:新加入一个台节点服务器,只需要执行第1.2.3步即可