设为首页 收藏本站
查看: 3808|回复: 0

[经验分享] Hadoop专业解决方案-第5章 开发可靠的MapReduce应用

[复制链接]

尚未签到

发表于 2015-7-11 09:41:52 | 显示全部楼层 |阅读模式
  本章主要内容:
  1、利用MRUnit创建MapReduce的单元测试。
  2、MapReduce应用的本地实例。
  3、理解MapReduce的调试。
  4、利用MapReduce防御式程序设计。
  在WOX.COM下载本章源代码
  本章在wox.com网站的源码可以在www.wiley.com/go/prohadoopsolutions的源码下载标签找到。第五章的源码根据本章的内容各自分别命名放在了第五章下载目录中。
  到目前为止,你应该对MapReduce体系结构,应用程序设计,和定制MapReduce扩展程序很熟悉了。本章讨论如何对测试程序通过有影响力(leveraging)的单元测试和基于Hadoop的设备创建可靠的MapReduce代码。你也可以从中学到不同的防御式编程技术来处理部分被破坏的(corrupted)数据的方法。
  MapReduce应用的单元测试
  缺陷(Bugs)总在代码中存在,这是一个不争的事实——你写的代码越多,你也会遇到越多的缺陷(Bugs)。即使是最伟大的程序员也极少写没有缺陷(bug)的代码。这就是为什么测试成为了代码开发中完整中的(integral)一部分,因此许多开发人员越来越倾向于测试性驱动的开发(test-driven development,TDD)。
  注:这里所讨论的MRUnit和它在MapReduce工作的单元测试中的使用是由诺基亚的同事Michael Spicuzza富有创造性地实现的。
测试性驱动的程序开发(TEST-DRIVEN DEVELOPMENT

  测试性驱动程序开发是一种基于开发实际代码的同时编写自动化测试代码的编程技术。这保证了你即时的测试你的代码,并保证你快速而容易地重复测试你的代码,因为这个过程是自动的。
  测试性驱动程序开发涉及到了如下简短、重复的开发流程:
  1、在写任何代码之前,你都要首先写它的自动化测试代码。当你写自动化测试代码的时候,你需要考虑到他所有可能的输入,错误,以及输出。这样,你需要在实际编写代码前设计你的代码习惯
  2、第一次运行你的测试代码,这个测试应该是失败的——表明代码还没有准备好。
  3、然后,你应该开始编程。因为这里已经有测试代码了,只要这个代码仍旧失败,那么就意味着还没有准备好。这个代码可以一直修复直到它通过所有的断言为止。
  4、一旦这个代码通过了这个测试,然后通过重构,你可以将它清除干净。只要这个代码仍旧能通过测试,它意味着它仍旧能工作。你不必当心改变会引起新的缺陷(bugs)。
  5、在其他的方法和程序中重新开始整个事情。
  测试性驱动编程的一个基石是单元测试。尽管通过利用足够多的模拟类,利用JUnit可以从技术上测试大量的MapReduce应用程序,这里仍有一个可以选择的方法能够提供可以增加的覆盖水平。MRUnit是专门为Hadoop服务的测试框架。它是从包含对Hadoop的Cloudera的分布式开源实现开始的,它现在是Apache的一个子项目。MRUnit是基于Junit的,并允许单元测试成为映射式(mappers),简化式(reducers),和其他mapper-reducer交叉的交叉性测试,伴随着合并,客户计数,和拆分。
  如第三章的解释,Eclipse提供了MapReduce的一个非常好的开发工具。在这里的讨论中,你能学会如何创建包含了MapReduce程序依赖的所有需求的pom.xml文件。Eclipse也提供了基于MRUnit的单元测试平台。
  为了利用MRUnit,你应该继承在第三章中增加的MRUnit依赖的标准MapReudce Maven中的pom文件,如清单5-1所示:
  注:MRUnit的jar文件,和,所以,Maven的依赖项,有如下两个版本:
  mrunit-0.9.0-incubating-hadoop1.jar 是Hadoop的MapReduce的第一个版本,而mrunit-0.9.0-incubating-hadoop2.jar是工作在Hadoop的MapReudce新的版本下的。这个新版本是指从Cloudere的CDH 4而来的hadoop-2.0版本。
       有了这个工具,你可以实现包含MapReduce应用的主要元素的单元测试。第三章的单词计数的实例在这里用作测试的实例。这意味着这个实例中的mapper和reducer会作为参数传递给测试程序。

测试Mappers

使用MRUnit测试mappers非常直接,代码清单5-2非常明显的展示了其特性。

注:MRUnit支持老的(从mapred包而来)和新的(从mapreduce包而来)MapReduce APIs。当测试你的代码时,注意保证应用合适的MapDriver对象的实例。相同的ReduceDriverMapReduceDriver对象,在以后的章节中描述。

写一个基于MRUnit的测试单元非常简单。这种简单的标志性的加强是基于流利的API风格的。为了写你的测试程序,你需要做如下的事情:

1.在测试map程序中安装MapDriver类作为map确切的参数。

2.通过使用withMapper调用来增加一个你正在测试的实例。第三章单词计数的程序的mapper程序应用在了这里。

3. 你可以使用一个可选的withConfiguration方法,将所需的配置传递给mapper。

4. 该withInput调用,你可以通过所需的键和值 - 在这种情况下,用一个任意值,并且包含一个长文本对象如“猫,猫,狗“

5. 使用withOutput调用中得到预计输出。在这个例子中,是“猫”,“猫”和“狗”三个文本对象与其出现的相应i的intWritable值 – 所有的值都为1。

6. 如果一个mapper递增计数器可选。 withCounter (组名称, expectedValue ) (清单5-2中没有显示),使你能够指定计数器的期望值。

7.最后一次调用,runtest,反馈进入mapper中指定的输入值,比较实际的输出和通过withoutput方法得到的期望输出值。

对于MapDriver类的缺陷是对于每个侧是你最后只能有单个的输入和输出。如果你想,你可以调用withInput和withOutput多次,因此,你在任何时间只是测试一组输入/输出。为了指定多个输入,你必须使用MapReducerDriver对象(将在这章稍后介绍)。

Reducers 测试

       测试Reducer和测试Mapper是一样的。可以参看清单5-3:

清单5-3

@Test

public void testReducer() throws Exception {

List values = new ArrayList();

values.add(new IntWritable(1));

values.add(new IntWritable(1));

new ReduceDriver()

.withReducer(new WordCount.Reduce())

.withConfiguration(new Configuration())

.withInput(new Text("cat"), values)

.withOutput(new Text("cat"), new IntWritable(2))

.runTest();

}

以下是这段代码详细的解释:


  • reducer被创建的时候一个IntWritable对象列表作为输入对象。
  • 一个ReducerDriver需要被实例化。如同MapprtDriver一样,这也真是作为参数在reducer中测试。
  • 你想要的一个reducer的实例是通过withReducer调用的。这个在第三章的word count实例中使用的实例将在这里使用。
  • 一个可选的withconfiguration方法
  • WithInput调用允许你传递输入之值给reducer。这里,你传递“cat”健和在一开始通过intwritable创建的列表。
  • 你可以通过withOutput指定希望的输出结果。这里,你指定相同的健“cat”和intwritable代替单词“cat”的个数。
  • 如果一个reducer是一个递增的计数器,一个可选的计数组合(组,名,期待值)(在5-3清单中未列出)可以让你指定希望得到的计数值。
  • 最后,你调用runtest,其中反馈了reducer的指定输出,并和期望输出作对比。
Reducerdricver和mapperDriver存在相同的限制,不能接受超过一个的输入/输出对。

到目前为止,这一章节已经向你展示了如何分开测试mapper和reducer的方法,但是,也可能需要一起对它们进行交叉测试。你可以利用MapReduceDriver类来实现。Mapreducedriver类也被用来测试联合使用的问题。

交叉测试

MRUnit提供了Mapreducerdriver类来让你测试mapper和reducer共同使用的情况。MapReducerDriver类不同于MapperDriver和ReducerDriver类被参数化。首先,你参数化mapper类的输入和输出类型,然后是Reducer的输入和输出类型。因为mapper的输出类型通常是和reducer的输入类型相互匹配的,你最终得到三对参数对。补充一下,你可以提供多组的输入和指定多组的期望输出。清单5-4列出了一些实例代码:

清单5-4 一起测试mapper和reducer

@Test

public void testMapReduce() throws Exception {

new MapReduceDriver()

.withMapper(new WordCount.Map())

.withReducer(new WordCount.Reduce())

.withConfiguration(new Configuration())

.withInput(new LongWritable(1), new Text("dog cat dog"))

.withInput(new LongWritable(2), new Text("cat mouse"))

.withOutput(new Text("cat"), new IntWritable(2))

.withOutput(new Text("dog"), new IntWritable(2))

.withOutput(new Text("mouse"), new IntWritable(1))

.runTest();

}

正如你在上面的代码中所看到的,这个安装程序和MapDriver/ReduceDriver用到的类是很相似的。你传递实例实例到mapper和reducer中。(第三章涉及到的单词计数的实例在这里也用到了。)可选的是,你可以利用withConfiguration和withCombiner来测试配置和需要的合并。

MapReduceDrive类让你能够传递多个不同的键值。这里,你传递两个记录——第一个含有一个LonWritable的随意值和以恶文本对象包含一行“dog cat dog”,第二个LongWritable对象包含一个任意值和一个文本对象包含一行“cat mouse”。

你也可以利用withoutput方法指定一个期望的输出。这里,你指定三个关键值——“cat”,“dog”,”mouse”——伴随着一致的计数2,2,和1.最后,如果mapper/reducer 是一个递增的计数器,一个可选的是,withCounter(group,name,experctedValue)(在清单5-4中没有列出来)可以让你指定这个计数器期望的值。

如果一个测试失败了,MRUnit会产生一个和清当5-5相类似的指定输出,告诉你出现了什么错误。

清单5-5:MRUnit不成功时的输出结果

13/01/05 09:56:30 ERROR mrunit.TestDriver: Missing expected output (mouse, 2)

at position 2.

13/01/05 09:56:30 ERROR mrunit.TestDriver: Received unexpected output (mouse,

1)   at position 2.

如果测试结果是成功的,你会活得一个小小的自信,mapper和reducer协同工作是成功的。

尽管MRUnit使mapper和reducer代码的单元测试变得简单了,在这里涉及的mapper和reducer实例是比较简单的。如果你的map和/或者reduce代码开始变得很复杂,从Hadoop框架获得支持分开处理,并单独测试业务逻辑是一个好的设计方法(也就是说,需要应用程序定制)。就像在交叉测试中使用MapReduceDriver一样,在你不再测试你的代码的时候也是很容易得到一个点的,而不是已经做了这件事的Hadoop框架本身。

这里所设计的单元测试是一种典型的在实现发现bugs的方法,但是这些测试不会测试基于Hadoop的已经完成的MapReduce任务。本地任务运行,在下一节中描述,能让你在本地运行Hadoop程序,在一个java虚拟机中,使如果MapReduce任务失败了更加容易调试。

用Eclipse进行本地程序测试

利用Eclipse进行Hadoop开发提供了运行完整的MapReduce本地应用程序的能力——在一个单例模式下。Hadoop分布式(Hadoop-core)伴随着本地任务远行,能让你在本地计算机上运行Hadoop,在单个的JVM中。在这种情况下,你能在map或者reduce方法内部设置断点,利用eclipse调试器,和单步执行代码来检验程序的错误。

在本地的eclipse中运行MapReduce程序不许要一些特别的配置或者设置。如图5-1,只需要右键类,选择Run As(或者Debug as)再选择Java Application就行了。


图5-1:在本地Eclipse中运行MapReduce程序

注意:尽管一个本地的任务执行可以运行完整的程序,但是它也很多限制。例如,它不能运行超过一个的Reducer。(它不支持0reducer的情况。)通常,这不是问题,因为许多程序能够只用一个reducer执行。需要注意的问题是,即使你设定了多个reducer,本地任务也会忽略这些设置,并用单个的reducer。也要注意所有的本地mapper是顺序执行的。

一个基于Eclipse的本地可执行任务可以在Linux和Windows中执行。(如果在Windows中使用mapreduce程序,你需要安装Cygwin。)默认情况下,一个本地运行的任务会使用本地的文件系统进行读和写数据。

注意这个,默认情况,本地Hadoop执行程序使用本地文件系统。(如同在第二章中描述的,HDFS实现了对本地文件系统提供支持。)这意味着,所有用于测试的数据都需要拷贝到本地,产生的结果也是本地的。

如果这个是不可选的,你可以配置本地运行程序去操作集群数据(包括HBasse)。为了通过本地执行程序进入集群,你必须要使用一个配置文件,如清单5-6中所示:

清单5-6:Hadoop访问集群数据的配置文件





hbase.zookeeper.quorum

Comma separated list of zookeeper nodes



hbase.zookeeper.property.clientPort

zookeeper port




fs.default.name

hdfs:///




hadoop.job.ugi

hadoop, hadoop



这个配置文件定义了三个主要的组件——HBase的配置(定义了指向Zookeeper的连接数),HDFS的配置(定义了HBase的URL),和安全模拟(这需要如果你开发的机器和Hadoop集群属于不同的安全域,或者在你本机和Hadoop集群中你使用不同的登陆名)。将这个配置文件加到可执行的应用中使相当简单的,如同清单5-7中所示。

清单5-7:加载集群信息的配置文件

Configuration.addDefaultResource("Hadoop properiies");

Configuration conf = new Configuration();

尽管利用本地执行任务进行测试相对于单元测试来说会比较彻底,一个你必须记住的就是测试hadoop程序必须关注计算规模,不论你在本地运行多少次,直到你使用真实的数据测试代码的时候,你都不会确定它是正确工作的。

实际上,许多测验都不能被验证,包括下面几项:

1、 在程序运行的时候有多少的mapper被创建,数据在它们之间是如何被拆分的?

2、 多少真实的重洗和排序?是否必要去实现一个联合器?是否一个驻内存的联合器是可选的?

3、 是什么样的硬件/软件/网络环境?是否需要调整应用程序/集群的参数?

这意味着为了保证应用程序正常工作,测试本地任务必须在Hadoop集群上用真实的数据测试。

MapReduce可执行程序的高并发性和它依赖于大量的数据,使得测试MapReduce代码变得比较具有挑战性。在下一节,你会学到如何利用Hadoop的日志来加强Hadoop执行程序的调试。

利用日志文件测试Hadoop

日志文件被软件工程广泛的使用了,并包含如下几个重要的目标:

1、 创建一个可执行的测试应用,例如,为了执行分析,或者得到潜在的提升。

2、 收集执行的各项指标,能够用来进行实时的和事后的分析,并且能自动测试,错误校验,等等。

MapReduce本身已经记录了程序执行过程中的各项日志。本地的这些文件是受Hadoop的配置文件控制的。默认情况下,它们存放在Hadoop版本文件夹下的logs子目录下。对于单个程序最重要的日志文件是TaskTracker的日志。MapReduce任务抛出的任何异常信息都会在这些日志文件中记载。

这个log文件目录下还有一个userlogs的子目录,它包含了每个任务的日志。每个任务都会记录它的stdout和stderr信息到在这个目录下的这两个文件中。每一个应用指定的日志信息包括用户的代码也存放在这些文件里。在一个多节点的Hadoop集群上,这些日志文件没有集中汇总,你必须查看每个节点下的logs/userlosgs目录。

一个访问一个任务的所有日志的方便的方法是通过JobTracker的网页。如图5-2所示。它能让你看到这个任务的mappers和reducers的所有日志。



图5-2 任务网页

所有任务的日志信息可以通过TaskTracker的任务网页进入(任务的设置和清除日志,也包括mapper和reducer的日志一致性网页)。从这些页面,你可以导航到任务的配置页面,如图5-3所示.


图5-3 任务配置页面

这个任务配置文件包含了配置对象的文本。如果你使用了大量的自己写的配置文件(例如,当你从你的驱动器传递参数到mappers和reducers的时候)。这些页面允许你配置他们期望的值。

另外,任务的安装和清理日志,包括mapper和reducer的页面,被链接到统一的日志文件页面。如图5-4所示,日志文件包括stdout,stderr和syslog三个日志文件。


图5-4 map的日志文件

任何应用程序指定的日志都应该在这个页面上显示。因为这个页面可以实时的刷新。它可以有效的查看单个执行任务的进程(假设你的日志文件记录了合适的信息)。

通过使用MapReduce框架来运行用户提供的调试脚本会使日志运行的更加有效。这些用户指定的日志信息是对于问题信息更加重要的记载。这些脚本允许从任务的输出文件(stdout和stderr),系统日文件,和任务配置文件中挖掘数据。这些从脚本的标准输出文件的到的文件可以利用任务提供的接口来使用。

你可以对失败的任务提供map和reduce分开的脚本。你可以通过对mapred.map.task.debug.script(为了调试map任务)和mapred.reduce.task.debug.script(为了调试reduce任务)属性设置合适的值来提交调试脚本。你可以通过API来设置这些属性。对于这些脚本的参数就是任务的stdout,stderr,syslog和jobconf文件。

当决定什么样的东西需要在你的日志文件中出现,使你的决定在一个有目的的情况下进行。对于调试日志文件有如下几点建议:

1. 异常或者错误代码信息应该一直输出异常信息。

2. 任何不期望的变量的值(例如,空值)应该在执行的过程中记录日志。

3. 不可预料的执行路径应该记录日志。

4. 如果异常是发生在被包含的里面的,那么在主函数块中应该记录相关日志。

5. 太多的日志文件反而使日志无效。尽量使相关的信息放在同一个日志文件中。

尽管利用JobTracker可以很方便的查看指定任务的日志文件,但它不事后自动记录日志和挖掘数据。下一节将描述适合自动记录日志的方法。

进行中的程序日志

你可以使用广泛的方法来解决日志文件的问题,利用指定的软件(例如,适合HadoopOps的Splunk)和一个定制的日志处理程序。为了实现定制的日子处理程序,所有map和reduce产生的日志文件都应该集中到一个文件总来。你可利用清单5-8(代码文件:HadoopJobLogScraper类)所展示的那样来处理,它允许你将所有相关的任务的日志集中起来,并将它们存放到单个的文件中去。

注意:这个解决方案是一个叫Dmitry Mikhelson的诺基亚同事提供的。

LISTING 5-8: Simple log screen scraper

public class HadoopJobLogScraper{

private String _trackerURL = null;

public static void main(String[] args) throws IOException{

if (args.length != 2){

System.err.println("usage: , ");

}

String jobId = args[1];

String trackerURL = args[0];

HadoopJobLogScraper scraper = new HadoopJobLogScraper(trackerURL);

scraper.scrape(jobId, JobType.MAP);

scraper.scrape(jobId, JobType.REDUCE);

System.out.println("done");

}

public enum JobType{

MAP("map"), REDUCE("reduce");

private String urlName;

private JobType(String urlName){

this.urlName = urlName;

}

public String getUrlName(){

return urlName;

}

}

private Pattern taskDetailsUrlPattern = Pattern.compile("(.*?)");

private Pattern logUrlPattern = Pattern.compile("

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-85412-1-1.html 上篇帖子: 初学hadoop——安装启动及遇到的问题 下篇帖子: Hadoop安装配置手册
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表