云计算期末报告无图 kmeans和最短路径算法hadoop实现详解

来源:互联网 发布:vm虚拟机没网络 编辑:程序博客网 时间:2024/05/22 22:36

《云计算应用开发实验》大作业报告

一.实验环境与实验工具

ubuntu 16.04真机 + hadoop2.6 + 本地伪分布
 

二.实验原理

以下内容为科普性内容,不过里面还是有一些关键的解释在配环境的时候用得上

Hadoop是一个能够让用户轻松架构和使用的分布式计算平台。

用户可以轻松地在Hadoop上开发和运行处理海量数据的应用程序。它主要有以下几个优点:

  1. 高可靠性。Hadoop按位存储和处理数据的能力值得人们信赖。
  2. 高扩展性。Hadoop是在可用的计算机集簇间分配数据并完成计算任务的,这些集簇可以方便地扩展到数以千计的节点中。
  3. 高效性。Hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快。
  4. 高容错性。Hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配
  5. 低成本。与一体机、商用数据仓库以及QlikView、Yonghong Z-Suite等数据集市相比,Hadoop是开源的,项目的软件成本因此会大大降低

Hadoop的核心就是HDFS和MapReduce

HDFS(Hadoop Distributed File System,Hadoop分布式文件系统),它是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,适合那些有着超大数据集(large data set)的应用程序。

HDFS的关键元素:

  • Block:将一个文件进行分块,通常是64M。
  • NameNode:保存整个文件系统的目录信息、文件信息及分块信息,这是由唯一一台主机专门保存,当然这台主机如果出错,NameNode就失效了。
  • SecondaryNameNode 它不是 namenode 的冗余守护进程,而是提供周期检查点和清理任务。
    • 出于对可扩展性和容错性等考虑,我们一般将SecondaryNameNode运行在一台非NameNode的机器上。
  • DataNode:它负责管理连接到节点的存储(一个集群中可以有多个节点)。每个存储数据的节点运行一个 datanode 守护进程。

上面关于namenode和datanode的定义还是比较重要的

图片名称

MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念”Map(映射)”和”Reduce(归约)”,是它的主要思想。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。

MapReduce物理实体

  1. 客户端(client):编写mapreduce程序,配置作业,提交作业,这就是程序员完成的工作;
  2. JobTracker:初始化作业,分配作业,与TaskTracker通信,协调整个作业的执行;
  3. TaskTracker:保持与JobTracker的通信,在分配的数据片段上执行Map或Reduce任务,TaskTracker和JobTracker的不同有个很重要的方面,就是在执行任务时候TaskTracker可以有n多个,JobTracker则只会有一个;
  4. Hdfs:保存作业的数据、配置信息等等,最后的结果也是保存在hdfs上面

MapReduce作业运行机制:

图片名称

  • ### 这一过程在实验中体现得比较明显,之后会有代码的详细分析

1.在客户端启动一个作业。

2.向JobTracker请求一个Job ID。

3.将运行作业所需要的资源文件复制到HDFS上,包括MapReduce程序打包的JAR文件、配置文件和客户端计算所得的输入划分信息。这些文件都存放在JobTracker专门为该作业创建的文件夹中。文件夹名为该作业的Job ID。JAR文件默认会有10个副本(mapred.submit.replication属性控制);输入划分信息告诉了JobTracker应该为这个作业启动多少个map任务等信息。

4.JobTracker接收到作业后,将其放在一个作业队列里,等待作业调度器对其进行调度,当作业调度器根据自己的调度算法调度到该作业时,会根据输入划分信息为每个划分创建一个map任务,并将map任务分配给TaskTracker执行。对于map和reduce任务,TaskTracker根据主机核的数量和内存的大小有固定数量的map槽和reduce槽。

5.TaskTracker每隔一段时间会给JobTracker发送一个心跳,告诉JobTracker它依然在运行,同时心跳中还携带着很多的信息,比如当前map任务完成的进度等信息。当JobTracker收到作业的最后一个任务完成信息时,便把该作业设置成“成功”。当JobClient查询状态时,它将得知任务已完成,便显示一条消息给用户。

用最简短的语言解释MapReduce:
We want to count all the books in the library. You count up shelf #1, I count up shelf #2. That’s map. The more people we get, the faster it goes.
Now we get together and add our individual counts. That’s reduce.

三.实验过程

以下内容为正题,从环境搭建开始:

重搭真机伪分布环境

之前的实验是在虚拟机上跑的,利用两台虚拟机一台做主机一台做从机,虚拟机性能差,两台虚拟机之前的连接速度极慢,map reduce的过程也非常的慢,亲测是没跑动上面的算法,所以我重新在ubuntu16.04的真机上搭建了伪分布式的hadoop环境和hdfs分布式文件系统,当然也顺便把单机模式的hadoop也跑了一下wordcount,伪分布和完全分布就只是不需要配置slave文件,即自己就是自己的从机,因为TA给了配置文档,但是是全分布的,而且有些也不需要,我就把关键的地方说一下
1. java安装环境变量啥的基本常识,注意一下要在~/.bashrc里面添加的环境变量才会一直生效,配置完后需要source一下才能立即生效
2. 配置ssh,ssh毕竟是ubuntu最常用的功能之一,方便以后ssh自己的笔记本,这里就不用改名为master了,好难看,还是保持的当前的名字,但是在/etc/hosts那里我还是改成了自己的pc名,ip就是本地ip:127.0.0.1,不用添加master和slave,然后配置ssh就是让自己的机器能直接通过账户密码ssh控制,添加密匙什么的按教程来就没问题
3. hadoop安装配置,并且和java一样设置hadoop的环境变量就是在.bashrc文件里添加hadoop的bin路径,然后关键是三个文件的配置,注意尝试过单机模式,就不用配置core-site文件,然后输入输出就是本地的文件,我跑了一下wordcount试了一下成功了就继续配置伪分布模式
* 修改core-site.xml

  <property>          <name>fs.defaultFS</name>          <value>hdfs://cx-ThinkPad-X230:9000</value>      </property>      <property>          <name>hadoop.tmp.dir</name>          <value>/usr/local/hadoop/hadoop/tmp</value>      </property> 

这里第一个fs.defaultFS就是配置默认的文件系统,主要要写上自己主机名称,9000为端口号,默认为这个如果不是那么需要在代码中设置,
第二个是hadoop.tmp.dir,下面的路径名就是你第一次执行hdfs namenode -format这在这个文件夹下生成hdfs系统,这是一个基目录,所以如果你的hdfs文件系统出了问题或者想重新配置,将这个文件夹删掉再重新执行hdfs namenode -format即可(亲测经验之谈。。。)

  • 修改hdfs-site.xml
  <property>        <name>dfs.replication</name>        <value>1</value>    </property>    <property>        <name>dfs.namenode.name.dir</name>        <value>/usr/local/hadoop/hadoop/tmp/dfs/name</value>    </property>    <property>        <name>dfs.datanode.data.dir</name>        <value>/usr/local/hadoop/hadoop/tmp/dfs/data</value>    </property>

dfs.replication是副本数,即block的备份数
后面两个就是节点的路径,需要在第一个配置文件的子目录下面,这点十分重要,非常之坑,亲测我这里的路径和上面的路径名不一致,导致我后面只要一跑mapreduce导致系统直接注销,迷了我很久,后来从伪分布又换成单机,又换成伪分布,配了几次才发现问题
NameNode 和Datanode在前面已经介绍得很清楚,就不过多介绍

这里在hdfs文件系统format之后我尝试在/usr/local/hadoop/hadoop/tmp即我指定的目录中查看我put上去的东西,结果并不能找到文件,而是一些奇怪的文件,看来hdfs文件系统将传上去的文件处理过,要访问只能通过hdfs方式查看

然后配置文件还要配置(其实伪分布模式可以不用搞这个的)
YARN 是从 MapReduce 中分离出来的,负责资源管理与任务调度。YARN 运行于 MapReduce 之上,提供了高可用性、高扩展性,如果配了用sbin/start-yarn.sh启动即可,会看到多的nodemanager和recoursemanager,配置和教程一样就不多说了

上面的配置过程还是折腾了许久,因为没有按照TA的教材自己研究,网上教程版本太多,参差不齐,不过有问题才能学到东西,配置过程也加深了对hadoop和hdfs的理解,这个东西还是很强大的,也激起了继续折腾的兴趣

算法实现

1.Kmeans

这里先介绍一下Kmeans算法
K-means算法是硬聚类算法,是典型的基于原型的目标函数聚类方法的代表,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
图片名称

算法过程如下:

  • 从N个文档随机选取K个文档作为质心
  • 对剩余的每个文档测量其到每个质心的距离,并把它归到最近的质心的类
  • 重新计算已经得到的各个类的质心
  • 迭代2~3步直至新的质心与原质心相等或小于指定阈值,算法结束

图片名称 图片名称 图片名称 图片名称 图片名称

从 K-means 算法框架可以看出,该算法需要不断地进行样本分类调整,不断地计算调整后的新的聚类中心,因此当数据量非常大时,算法的时间开销是非常大的。而Hadoop的分布式计算平台恰好可以弥补这一缺点,所以K-means算法也十分适合hadoop

其实这个算法在hadoop实现非常简单粗暴,感觉也是hadoop和机器学习结合最通俗易懂的例子,个人觉得就像机器学习和hadoop结合的”wordcount”,这里主要是对代码的详细分析,包括了对mapreduce的理解

先介绍map和reduce过程
* map过程:
首先从HDFS上读取中心点,然后让当前文档与这k个中心点进行距离运算,找出距离最近的的一个中心点,以中心点为key值,将记录原样输出。

  public static class Map extends Mapper<LongWritable, Text, IntWritable, Text>{        //中心集合        ArrayList<ArrayList<Double>> centers = null;              int k = 0;           //读取中心        protected void setup(Context context) throws IOException,                InterruptedException {            //从HDFS上读取数据            centers = Utils.getCentersFromHDFS(context.getConfiguration().get("centersPath"),false);            k = centers.size();//获得中心点的数目         }        /**         * 1.每次读取一条要分类的条记录与中心做对比,归类到对应的中心         * 2.以中心ID为key,中心包含的记录为value输出(例如: 1 0.2 。  1为聚类中心的ID,0.2为靠近聚类中心的某个值)         */        protected void map(LongWritable key, Text value, Context context)                throws IOException, InterruptedException {            //读取一行数据            ArrayList<Double> fileds = Utils.textToArray(value);            int sizeOfFileds = fileds.size();//维度             double mindistance = 1999999999;            int centerIndex = 0;                       //依次取出k个中心点与当前读取的记录做计算            for(int i=0;i<k;i++){                double currentdistance = 0;                for(int j=1;j<sizeOfFileds;j++){                     double centerPoint = Math.abs(centers.get(i).get(j));                    double filed = Math.abs(fileds.get(j));                    currentdistance += Math.pow((centerPoint - filed), 2);                }                //循环找出距离该记录最接近的中心点的ID                if(currentdistance<mindistance){                    mindistance = currentdistance;                    centerIndex = i;//记录下是第几个中心点                 }            }            //以中心点为Key 将记录原样输出            context.write(new IntWritable(centerIndex+1), value);        }          }

从HDFS读取文件函数getCentersFromHDFS():根据路径读取文件,转换为ArrayList

  public static ArrayList<ArrayList<Double>> getCentersFromHDFS(String centersPath,boolean isDirectory)     throws IOException{        ArrayList<ArrayList<Double>> result = new ArrayList<ArrayList<Double>>();        Path path = new Path(centersPath);        Configuration conf = new Configuration();        FileSystem fileSystem = path.getFileSystem(conf);        if(isDirectory){   //判断是否是目录             FileStatus[] listFile = fileSystem.listStatus(path);            for (int i = 0; i < listFile.length; i++) {                result.addAll(getCentersFromHDFS(listFile[i].getPath().toString(),false));            }            return result;        }        FSDataInputStream fsis = fileSystem.open(path);        LineReader lineReader = new LineReader(fsis, conf);        Text line = new Text();        while(lineReader.readLine(line) > 0){//读取中心点文件,将Text转换为ArrayList<ArrayList<Double>>返回            ArrayList<Double> tempList = textToArray(line);            result.add(tempList);        }        lineReader.close();        return result;    }

 
* reduce过程
以key值分簇,计算出该簇的下一轮的中心点。之后又回到main函数中,调用比较函数compareCenters()判断是否已经收敛。

    //利用reduce的归并功能以中心为Key将记录归并到一起  public static class Reduce extends Reducer<IntWritable, Text, Text, Text>{        /**         * 1.Key为聚类中心的ID value为以该中心点的边缘点的集合         * 2.计数所有记录元素的平均值,求出新的中心         */        protected void reduce(IntWritable key, Iterable<Text> value,Context context)                throws IOException, InterruptedException {            ArrayList<ArrayList<Double>> filedsList = new ArrayList<ArrayList<Double>>();                        //依次读取记录集,每行为一个ArrayList<Double>            for(Iterator<Text> it =value.iterator();it.hasNext();){                ArrayList<Double> tempList = Utils.textToArray(it.next());                filedsList.add(tempList);            }            //计算新的中心            //每行的元素个数            int filedSize = filedsList.get(0).size();            double[] avg = new double[filedSize];            for(int i=1;i<filedSize;i++){                //求每列的平均值                double sum = 0;                int size = filedsList.size();                for(int j=0;j<size;j++){                    sum += filedsList.get(j).get(i);                }                avg[i] = sum / size;            }            context.write(new Text("") , new Text(Arrays.toString(avg).replace("[", "").replace("]", "")));        }         }

 
* 主函数
读入输入的4个参数,执行选取初始中心点的函数getinitcenter(),初始化完成后进入第一次运算。

    public static void main(String[] args)         throws ClassNotFoundException, IOException, InterruptedException {                Configuration conf = new Configuration();        if (args.length != 4) {            System.err.println("Usage: InvertedIndex <centerPath> <dataPath> <newCenterPath>");            System.exit(2);        }        args = new GenericOptionsParser(conf, args).getRemainingArgs();//获取运行命令时输入的参数         String centerPath = args[0];        String dataPath = args[1];        String newCenterPath = args[2];        int NumOfCenter=Integer.parseInt(args[3]);        Utils.getinitcenter(centerPath,dataPath,NumOfCenter);//随机选取初始点         int count = 0;        while(true){            run(centerPath,dataPath,newCenterPath,true);            System.out.println(" 第 " + ++count + " 次计算 ");            if(Utils.compareCenters(centerPath,newCenterPath )){//判断是否收敛,如果收敛则不用再进行运算                 //run(centerPath,dataPath,newCenterPath,false);                break;            }        }    }

还有其他的一些通用函数这里就不介绍了,在附件的代码里都有详细的注释

Kmeans算法运行结果
图片名称

图片名称

上图是运行两次之后成功分簇,然后查看结果和原始的数据点,可以看到二维点集

(0,2),(0,0),(1.5,0),(5,0),(5,2)

已经分成了0,1两簇,第一簇包括(0,2),(0,0),(1.5,0),第二簇包括(5,0),(5,2)
 
 

2.shortestPath 最短路径算法

没错,我们虽然小组只有两个人但是就是折腾了两个出来,好不容易在真机上搭出来环境,跑得贼快,一个算法不过瘾,还有原因是因为上面的例子其实基本就是博客写的,我们自己就加了个随机生成簇首点,估计写的同学都很多(估计TA也发现了大片代码雷同),于是想重新搞一个难一点的,然后自己写,于是就选了这个算法

这个算法的思路也就是map和reduce的流程是参照博客上面的,但是实现都是自己敲的,算是比较深刻的实践了mapreduce编程,自我感觉要比kmeans难那么一丢丢,虽然做完了发现也很简单= =
首先说一下map和reduce的过程

文件的输入格式是

A   B,12 E,5 D,1B   C,2 D,3C   E,1D   B,3 C,1 E,5E   A,1K   a,n1, b,n2 ... //表示节点到K 到 后面邻居节点的距离是n

然后在实验中发现下面的规则
注意节点与邻居表之间的分隔符是'\t',非常重要,因为在map的过程中会自动把一行文本按y遇到的第一个’\t’分隔成map的key和value,如果没有’\t’,那么会全部存为value,而key没有值(打印没有值,但是实际上我觉得应该是按行数来作为一个key value的)


  • map过程

第一次将A B,12 E,5 D,1分解成为
Node <当前与源点距离> <邻接表>
A 0 B,12 E,5 D,1(源节点)
或者
B inf C,2 D,3(中间节点)
压入的时候就Node作为key,<当前距离> <邻接表>一同作为value
之后就是
如果当前与源点距离不为inf就把节点的的所有邻接表距离算出来(包含源点),并按照
Node作为key,当前距离+下一节点距离作为value
那么在reduce阶段,同一个key也就是每个node节点获得的values数据存的就是
Nodei    <当前与源点距离> <邻接表>Nodei    dis1Nodei    dis2Nodei    dis3

reduce会自动按key值排序,所以我们获得到的nodei是按A,B,C..顺序得到的,所以也就类似BFS了


  • reduce过程
    key会排序,但是注意values数组并不会排序,而是按压入的顺序存储,

reduce过程任务是更新到当前节点的最短路径
从上面的Nodei dis中选一个最小的值与下面的距离比较,如果更小则更新
Nodei <当前与源点距离> <邻接表>
然后一直运行这个过程,直到一轮mapreduce之后所有节点都没有更新,则停止,最后输出源节点到所有节点的最短路径

代码+注释:
* map代码

    public static class ShortestPathMapper extends Mapper<Text, Text, Text, Text> {        protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {                  String name=key.toString();            //configuration 里可以存一下控制变量,可在mapreduce过程中的获取            int conuter = context.getConfiguration().getInt("Mapcount", 1);            String adj = "";            //第一次就加上去初始的距离,源点为0其他为不可达            if (conuter == 1) {                if (name.equals("A")) {                    adj = "0"+ " " +value.toString();                } else {                    adj = "inf"+ " " +value.toString();                }            }             else {                adj = value.toString();            }            Node node = new Node();            node.getFormatString(adj);            if (!node.getDistance().equals("inf")){                //当前节点可达,就计算其所有邻接点距离                for (int i = 0; i < node.getNodeNum(); i++) {                    String k = node.getName(i);                    double new_dis=Double.parseDouble(node.getValue(i)) + Double.parseDouble(node.getDistance());                    //System.out.println(k+" "+new_dis+"\n");                    context.write(new Text(k), new Text(new_dis+""));                }            }            context.write(key, new Text(adj));        }    }

 
* reduce代码

    public static class ShortestPathReducer extends Reducer<Text, Text, Text, Text> {        protected void reduce(Text key, Iterable<Text> values, Context context)             throws IOException, InterruptedException {            Node node = new Node();            boolean f=false; //标记该节点是否有更新最短距离            double min=9999999;//存到这个节点最短的距离            int i = 0;            //遍历每个节点的values,其中一个包含其邻接点信息,要单独取出来            for (Text t : values) {                String[] strs = StringUtils.split(t.toString(), ' ');                System.out.println(t.toString());                if (strs.length>1 ) {                    //包含邻接点的信息的那条信息单独处理                    node.getFormatString(t.toString());                }                else {                    //否则取出距离                    double dis_new=Double.parseDouble(strs[0]);                    //和最短的比较,                    if (dis_new<min){                        min=dis_new;                    }                }                i++;            }            //获取这一次reduce的最短距离和存储的最短距离            String dis_new=min+"";            String dis_old=node.getDistance();            //如果没有访问过又有新的距离,那么直接更新            if(dis_old.equals("inf")&&min!=9999999){                node.setDistance(dis_new);                f=true;            }             //否则需要判断距离是不是比当前的小,距离更近才更新            else if (min < Double.parseDouble(dis_old)){                node.setDistance(dis_new);                f=true;            }            ////如果有更新最短距离,那么更改停止标记            if (f==true){                context.getCounter(Stop_flag.Flag).increment(1L);                //context.getConfiguration().setBoolean("stop", true);            }            //获得stop标志,用于判断是否是最后一次输出            boolean stop=context.getConfiguration().getBoolean("stop", false);            if (stop==true)                context.write(key, new Text(node.getDistance()));            else                context.write(key, new Text(node.toString()));        }    }

 
* 主函数

    static enum Stop_flag {Flag}    public static void main(String[] args) throws ClassNotFoundException, IOException, InterruptedException{        Configuration conf = new Configuration();        if (args.length != 2) {            System.err.println("Usage: ShortestPath <dataPath> <outPath>");            System.exit(2);        }        args = new GenericOptionsParser(conf, args).getRemainingArgs();        String dataPath = args[0];        String outPath = args[1];        FileSystem fs = FileSystem.get(conf);        int count = 1;        while (true) {            //System.out.println(conf.getInt("Mapcount", 0)+"\n");            conf.setInt("Mapcount", count);            conf.setBoolean("stop",false);            Job job = Job.getInstance(conf);            //jar包名            job.setJarByClass(ShortestPath.class);            //设置对应的class,在打包的jar里面            job.setMapperClass(ShortestPathMapper.class);            job.setReducerClass(ShortestPathReducer.class);            job.setMapOutputKeyClass(Text.class);            job.setMapOutputValueClass(Text.class);            job.setInputFormatClass(KeyValueTextInputFormat.class);            //每次的输出文件要为新的,这样可以看到更新的过程            Path outPath1 = new Path(outPath+"tmp" + count);            if (fs.exists(outPath1)) {                fs.delete(outPath1, true);            }            FileOutputFormat.setOutputPath(job, outPath1);            //输入文件第一次为输入的路径,后面就是上一次reduce生成的文件            if (count == 1)                FileInputFormat.addInputPath(job, new Path(dataPath));            else                FileInputFormat.addInputPath(job, new Path(outPath+"tmp" + (count - 1)));            if (job.waitForCompletion(true)) {                System.out.println("run time: "+count);                                //if (conf.getBoolean("stop", false)) break;                long num = job.getCounters().findCounter(Stop_flag.Flag).getValue();                //如果所有节点都没有更新距离,那么算法结束                if (num==0) {                    //结束的mapreduce输出只输出最短路径                    conf.setInt("Mapcount", count);                    //设置stop标记为true                    conf.setBoolean("stop",true);                    Job job1 = Job.getInstance(conf);                    job1.setJarByClass(ShortestPath.class);                    job1.setMapperClass(ShortestPathMapper.class);                    job1.setReducerClass(ShortestPathReducer.class);                    job1.setMapOutputKeyClass(Text.class);                    job1.setMapOutputValueClass(Text.class);                    job1.setInputFormatClass(KeyValueTextInputFormat.class);                    //输入文件为最后的节点,输出文件为final文件                    FileInputFormat.addInputPath(job1, new Path(outPath+"tmp" + count));                    FileOutputFormat.setOutputPath(job1,new Path(outPath+"final"));                    job1.waitForCompletion(true);                    System.out.println("end ");                    break;                }            }            count++;        }    }

ShortestPath算法运行结果
测试数据集
图片名称

A   B,3 D,1B   A,3 E,7 F,2C   D,2 E,5 F,3D   A,1 C,2 E,5E   B,7 C,5 D,5 F   B,2 C,3

运行结果
图片名称

上图红框为运行完成后查看生产的所有中间文件tmp1-3和最终的运行结果final

A   0B   3.0C   3.0D   1.0E   6.0F   5.0

表示从A点到各个节点的最短距离,结合上面的给出的数据图很容易验证是正确的,
然后黄色框的是把中间运行结果打印出来了,可以看到运行了三次,然后各点到A点的距离也在不断的减少,下面再贴一些mapreduce运行的中间过程截图

图片名称 图片名称
这打印的是reduce阶段接收到的每个节点的现在计算出的距离和邻接表,从上往下依次是A,B,C…原因就是因为reduce会按照key值排序,可以看出values列表是无序的,这就是之前也说过的问题。
左图为运行之后可以看到有些节点还是不可达的,距离为10^7或者9999999,(因为java的double精度丢失,很奇怪,竟然10^7就丢失精度了。。),右图为已经完成最短路径计算的。
实验部分至此结束

四.实验总结

这次大作业肯定是收获很大的,以后还是可以吹嘘自己是学过hadoop并且亲自写过mapreduce的,并且通过自己的实验从搭环境到wordcount程序跑起来,然后是学习别人的代码,从简单的例子,比如统计,排序,学习了mapreduce的框架和一些特性,这里总结一下框架,也就是每个hadoop程序都要走的

//配置文件,可以存储一些配置Configuration conf = new Configuration();//获取一个jobJob job = Job.getInstance(conf);job.setJarByClass(ShortestPath.class);//设置map和reduce的类,就自己写的map和reduce过程job.setMapperClass(ShortestPathMapper.class);job.setReducerClass(ShortestPathReducer.class);//设置输入输出个格式,这里用到textjob.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);job.setInputFormatClass(KeyValueTextInputFormat.class);//输入输出文件,在hdfs文件系统上FileInputFormat.addInputPath(job, new Path());FileOutputFormat.setOutputPath(job, new Path());

然后还可以提前设置一些在mapreduce过程中用到的变量

//设置conf.setInt("Mapcount", count);conf.setBoolean("stop",true);//在mapreduce过程中获取boolean stop=context.getConfiguration().getBoolean("stop", false);

注意这些配置变量是不能在map和reduce过程中修改的,我在实验中尝试修改结果失败了
总之,实验过程虽然艰辛但是还是比较好玩的,hadoop这个平台以后有需要还会来继续研究研究,争取弄点更有意思的东西。

0 0
原创粉丝点击