Java 开发分析大数据.docx

上传人:b****8 文档编号:28365941 上传时间:2023-07-10 格式:DOCX 页数:15 大小:25.89KB
下载 相关 举报
Java 开发分析大数据.docx_第1页
第1页 / 共15页
Java 开发分析大数据.docx_第2页
第2页 / 共15页
Java 开发分析大数据.docx_第3页
第3页 / 共15页
Java 开发分析大数据.docx_第4页
第4页 / 共15页
Java 开发分析大数据.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

Java 开发分析大数据.docx

《Java 开发分析大数据.docx》由会员分享,可在线阅读,更多相关《Java 开发分析大数据.docx(15页珍藏版)》请在冰豆网上搜索。

Java 开发分析大数据.docx

Java开发分析大数据

Java开发2.0:

用HadoopMapReduce进行大数据分析

成堆的数据如何变成信息金矿

ApacheHadoop是目前分析分布式数据的首选工具,和大多数Java™2.0技术一样,是可扩展的。

从Hadoop的MapReduce编程建模开始,学习如何用它来分析数据,满足大大小小的商业信息需求。

内容

Google在2001年发布图像搜索功能时,只有2.5亿索引图像,不到10年,这个巨大的搜索功能已经可以检索超过100亿个图像了,每分钟有35小时的内容上传到YouTube。

据称,Twitter每天平均处理5500万tweet。

今年早些时候,搜索功能每天记录6亿条查询记录。

这 就是我们讨论大数据的意义所在。

关于本系列

从Java技术首次亮相以来,Java开发的格局已经发生了巨大的变化。

得益于成熟的开源框架和可靠的租用式部署基础设施,现在已经可以迅速经济地汇编、测试、运行和维护Java应用程序了。

在 本系列 中,AndrewGlover将探索使这种全新开发范例成为可能的各种技术和工具。

如此大规模的数据一度仅限于大企业、学校和政府机构—这些机构有能力购买昂贵的超级计算机、能够雇用员工保障其运行。

今天,由于存储成本的降低和处理能力的商品化,一些小公司,甚至个人都可以存储和挖掘同样的数据,推动新一轮的应用程序创新。

大数据革命技术之一是MapReduce,一个编程模式,是Google针对大规模、分布式数据而开发的。

在本文中,我将介绍Apache的开源MapReduce实现,Hadoop,也有人将其称之为云计算的杀手应用程序。

关于Hadoop

Apache的Hadoop框架本质上是一个用于分析大数据集的机制,不一定位于数据存储中。

Hadoop提取出了MapReduce的大规模数据分析引擎,更易于开发人员理解。

Hadoop可以扩展到无数个节点,可以处理所有活动和相关数据存储的协调。

Hadoop的众多特性和配置使其成为一个十分有用且功能强大的框架,其用途和功能令人惊讶。

Yahoo!

以及其他许多组织已经找到了一个高效机制来分析成堆的字节数。

在单个节点上运行Hadoop也很容易;您所需要的只是一些需要分析的数据,以及熟悉一般的Java代码。

Hadoop也可和Ruby、Python以及C++一起使用。

了解更多MapReduce

如果您是本系列的读者,您可能已经见过MapReduce一两次了。

在“通过CouchDB和Groovy的RESTClient实现REST”中,我介绍了CouchDB如何利用MapReduce进行查看,接着在“MongoDB:

拥有RDBMS特性的NoSQL数据存储”中我再次提到MapReduce,处理MongoDB文档的机制。

作为处理大数据集的概念框架,MapReduce对于使用许多计算机来解决分布式问题而言是高度优化的。

顾名思义,这个框架由两个函数构成。

map 函数专用于获取大数据输入,并将其分成小片段,然后交由其他进程进行操作。

reduce 函数整理 map 收集的各个回应,然后显示最后的输出。

在Hadoop中,您可以通过扩展Hadoop自身的基类来定义 map 和 reduce 实现。

实现和输入输出格式被一个指定它们的配置联系在一起。

Hadoop非常适合处理包含结构数据的大型文件。

Hadoop可以对输入文件进行原始解析,这一点特别有用,这样您就可以每次处理一行。

定义一个 map 函数实际上只是一个关于确定您从即将输入的文本行中捕获什么内容的问题。

回页首

数据,无处不在的数据!

美国政府产生大量数据,只有一部分是普通民众所感兴趣的。

各种政府机构免费发布关于US经济健康状况和更改社会人口统计资料的数据。

U.S.GeologicalSurvey(USGS)发布国内外地震数据。

世界各地每天都有很多个小型地震发生。

其中大多数发生在地壳深处,没有人能感觉到,尽管如此,但是监听站仍然会进行记录。

USGS以CSV(或逗号分隔值)文件的格式发布每周地震数据。

每周文件平均不是很大—只有大约100KB左右。

但是,它可以作为学习Hadoop的基础。

记住,Hadoop有能力处理更 大的数据集。

跟踪震动

我近期从USGS网站下载的CSV文件有大约920多行。

如清单1所示:

清单1.清单1.一个USGS地震数据文件的行数统计

$>wc-leqs7day-M1.txt

920eqs7day-M1.txt

CVS文件内容如清单2所示(这是前两行):

清单2.清单2.CVS文件的前两行

$>head-n2eqs7day-M1.txt

Src,Eqid,Version,Datetime,Lat,Lon,Magnitude,Depth,NST,Region

ci,14896484,2,"Sunday,December12,201023:

23:

20UTC",33.3040,-116.4130,1.0,11.70,22,

"SouthernCalifornia"

这就是我称之为信息丰富 的文件,尤其是当您想到它总共有920行记录时。

然而我只想知道在该文件报告的这一周内每一天有多少次地震发生。

我想知道在这7天内哪个区域是地震频发区。

我第一个想到的就是使用简单的 grep 命令来搜索每天的地震数。

看看这个文件,我发现数据记录是从12月12开始的。

因此我对该字符串执行了一次 grep-c,其结果如清单3所示:

清单3.清单3.12月12有多少次地震发生?

$>grep-c'December12'eqs7day-M1.txt

98

安装Hadoop

如果您之前没有安装Hadoop,那么现在就装。

第一步,下载最新版二进制文件,解压,然后在您的路径上设置Hadoop的 bin 目录。

完成这些您就可以直接执行hadoop 命令了。

使用Hadoop要求您执行它的 hadoop命令,而不是像您所见到的那样调用 java 命令。

您可以向 hadoop 命令传选项,诸如在哪里可以找到您的Java二进制文件(例如,表示您的 map 和 reduce 实现)。

在我的示例中,我创建了一个jar文件,告诉Hadoop我想在我的jar文件内运行哪个任务。

我也向Hadoop类路径添加了一些运行我的应用程序所需的附加二进制文件。

现在,我知道在12月12日有98条记录,也就是说有98次地震。

我只能沿着这条记录向下,对12月10日的记录执行一次 grep,接着是11号,等等。

这听起来有点乏味。

更糟糕的是,我还需要知道在该文件中的是哪几天。

我确实不关心这些,甚至有时候我可能无法获取该信息。

事实上,我只想知道在七天这样一个时间段内任何一天的地震次数,使用Hadoop我就可以很容易的获取这一信息。

Hadoop只需要几条信息就可以回答我的第一个和第二个问题:

即,要处理哪条输入以及如何处理 map 和 reduce。

我也必须提供了一个可以将每件事都联系起来的作业。

在我开始处理这些代码之前,我需要花点时间确定我的CSV数据整齐有序。

回页首

使用opencsv进行数据解析

除了地震CSV文件的第一行之外,第一行是文件头,每一行都是一系列逗号分隔数据值。

我只对数据的3个部分感兴趣:

日期、地点和震级。

为了获取这些资料,我将使用一个很棒的开源库 opencsv,它将会帮助我分析CSV文件。

作为一个测试优先的工具,我首先编写一个快捷JUnit测试,确认我可以从CSV文件的一个样例行获取的我所需要的信息,如清单4所示:

清单4.清单4.解析一个CSV行

publicclassCSVProcessingTest{

privatefinalStringLINE="ci,14897012,2,\"Monday,December13,2010"+

"14:

10:

32UTC\",33.0290,-115."+

"5388,1.9,15.70,41,\"SouthernCalifornia\"";

@Test

publicvoidtestReadingOneLine()throwsException{

String[]lines=newCSVParser().parseLine(LINE);

assertEquals("shouldbeMonday,December13,201014:

10:

32UTC",

"Monday,December13,201014:

10:

32UTC",lines[3]);

assertEquals("shouldbeSouthernCalifornia",

"SouthernCalifornia",lines[9]);

assertEquals("shouldbe1.9","1.9",lines[6]);

}

}

正如您在 清单4 中所看到的,opencsv 处理逗号分隔值非常容易。

该解析器仅返回一组 String,所以有可能获取位置信息(别忘了,在Java语言中数组和集合的访问是从零开始的)。

转换日期格式

当使用MapReduce进行处理时,map 函数的任务是选择一些要处理的值,以及一些键。

这就是说,map 主要处理和返回两个元素:

一个键和一个值。

回到我之前的需求,我首先想知道每天会发生多少次地震。

因此,当我在分析地震文件时,我将发布两个值:

键是日期,值是一个计数器。

reduce 函数将对计数器(只是一些值为1的整数)进行总计。

因此,提供给我的是在目标地震文件中某一个日期出现的次数。

由于我只对24小时时段内的信息感兴趣,我得剔除每个文件中的日期的时间部分。

在清单5中,我编写了一个快速测试,验证如何将一个传入文件中的特定日期信息转换成一个更一般的24小时日期:

清单5.清单5.日期格式转换

@Test

publicvoidtestParsingDate()throwsException{

Stringdatest="Monday,December13,201014:

10:

32UTC";

SimpleDateFormatformatter=newSimpleDateFormat("EEEEE,MMMMMdd,yyyyHH:

mm:

ssZ");

Datedt=formatter.parse(datest);

formatter.applyPattern("dd-MM-yyyy");

Stringdtstr=formatter.format(dt);

assertEquals("shouldbe13-12-2010","13-12-2010",dtstr);

}

在 清单5 中,我使用了 SimpleDateFormat Java对象,将CSV文件中格式为Monday,December13,201014:

10:

32UTC的日期 String 转换成了更一般的13-12-2010。

回页首

Hadoop的map和reduce

现在我已经找到了处理CSV文件以及其日期格式的解决方法。

我要开始在Hadoop中实施我的 map 和 reduce 函数了。

这个过程需要理解Java泛型,因为Hadoop选择使用显式类型,为了安全起见。

当我使用Hadoop定义一个映射实现时,我只扩展Hadoop的 Mapper 类。

然后我可以使用泛型来为传出键和值指定显式类。

类型子句也指定了传入键和值,这对于读取文件分别是字节数和文本行数。

EarthQuakesPerDateMapper 类扩展了Hadoop的 Mapper 对象。

它显式地将其输出键指定为一个 Text 对象,将其值指定为一个IntWritable,这是一个Hadoop特定类,实质上是一个整数。

还要注意,class子句的前两个类型是 LongWritable 和 Text,分别是字节数和文本行数。

由于类定义中的类型子句,我将传入 map 方法的参数类型设置为在 context.write 子句内带有该方法的输出。

如果我想指定其他内容,将会出现一个编译器问题,或Hadoop将输出一个错误消息,描述类型不匹配的消息。

清单6.清单6.一个映射实现

publicclassEarthQuakesPerDateMapperextendsMapper

Text,Text,IntWritable>{

@Override

protectedvoidmap(LongWritablekey,Textvalue,Contextcontext)throwsIOException,

InterruptedException{

if(key.get()>0){

try{

CSVParserparser=newCSVParser();

String[]lines=parser.parseLine(value.toString());

SimpleDateFormatformatter=

newSimpleDateFormat("EEEEE,MMMMMdd,yyyyHH:

mm:

ssZ");

Datedt=formatter.parse(lines[3]);

formatter.applyPattern("dd-MM-yyyy");

Stringdtstr=formatter.format(dt);

context.write(newText(dtstr),newIntWritable

(1));

}catch(ParseExceptione){}

}

}

}

清单6 中的 map 实现比较简单:

本质上是,Hadoop为在输入文件中找到的每一行文本调用这个类。

为了避免除了CSV头部,首先检查是否字节数(key 对象)为零。

然后执行清单4和5中的步骤:

捕获传入日期,进行转换,然后设置为传出键。

我也提供了一个数:

1。

就是说,我为每个日期编写一个计数器,当 reduce 实现被调用时,获取一个键和一系列值。

在本例中,键是日期及其值,如清单7所示:

清单7.清单7.一个map输出和reduce输入的逻辑视图

"13-12-2010":

[1,1,1,1,1,1,1,1]

"14-12-2010":

[1,1,1,1,1,1]

"15-12-2010":

[1,1,1,1,1,1,1,1,1]

注意,context.write(newText(dtstr),newIntWritable

(1))(在 清单6 中)构建了如 清单7 所示的逻辑集合。

正如您所了解的,context 是一个保存各种信息的Hadoop数据结构。

context 被传递到 reduce 实现,reduce 获取这些值为1的值然后总和起来。

因此,一个 reduce 实现逻辑上创建如清单8所示的数据结构:

清单8.清单8.一个reduce输出视图

"13-12-2010":

8

"14-12-2010":

6

"15-12-2010":

9

我的 reduce 实现如 清单9 所示。

与Hadoop的 Mapper 一样,Reducer 被参数化了:

前两个参数是传入的键类型(Text)和值类型(IntWritable),后两个参数是输出类型:

键和值,这在本例中是相同的。

清单9.清单9.reduce实现

publicclassEarthQuakesPerDateReducerextendsReducer

IntWritable>{

@Override

protectedvoidreduce(Textkey,Iterablevalues,Contextcontext)

throwsIOException,InterruptedException{

intcount=0;

for(IntWritablevalue:

values){

count++;

}

context.write(key,newIntWritable(count));

}

}

我的 reduce 实现非常简单。

正如我在 清单7 中所指出的,传入的是实际上是一个值的集合,在本例中是1的集合,我所做的就是将它们加起来,然后写出一个新键值对表示日期和次数。

我的 reduce 代码可以挑出您在 清单8 中所见到的这几行。

逻辑流程看起来像这样:

"13-12-2010":

[1,1,1,1,1,1,1,1]->"13-12-2010":

8

当然,这个清单的抽象形式是 map->reduce。

回页首

定义一个HadoopJob

现在我已经对我的 map 和 reduce 实现进行了编码,接下来所要做的是将所有这一切链接到一个Hadoop Job。

定义一个 Job 比较简单:

您需要提供输入和输出、map 和 reduce 实现(如 清单6 和 清单9 所示)以及输出类型。

在本例中我的输出类型和 reduce 实现所用的是同一个类型。

清单10.清单10.一个将map和redece绑在一起的Job

publicclassEarthQuakesPerDayJob{

publicstaticvoidmain(String[]args)throwsThrowable{

Jobjob=newJob();

job.setJarByClass(EarthQuakesPerDayJob.class);

FileInputFormat.addInputPath(job,newPath(args[0]));

FileOutputFormat.setOutputPath(job,newPath(args[1]));

job.setMapperClass(EarthQuakesPerDateMapper.class);

job.setReducerClass(EarthQuakesPerDateReducer.class);

job.setOutputKeyClass(Text.class);

job.setOutputValueClass(IntWritable.class);

System.exit(job.waitForCompletion(true)?

0:

1);

}

}

在 清单10 中,我使用一个 main 方法将所有这一切绑在一起,该方法有两个参数:

地震CSV文件的目录,以及生成报告的输出目录(Hadoop更喜欢创建该目录)。

为了执行这个小框架,我需要将这些类打包。

我还需要告知Hadoop在哪里可以找到 opencsv 二进制文件。

然后可以通过命令行执行Hadoop,如清单11所示:

清单11.清单11.执行Hadoop

$>exportHADOOP_CLASSPATH=lib/opencsv-2.2.jar

$>hadoopjartarget/quake.jarcom.b50.hadoop.quake.EarthQuakesPerDayJob

~/temp/mreduce/in/~/temp/mreduce/out

运行这些代码,Hadoop开始运行时您将可以看到一堆文本在屏幕上一闪而过。

我所用的CSV文件相比专门用于处理这种情况的Hadoop,那真是小巫见大巫!

hadoop应该可以在几秒钟内完成,具体取决于您的处理功能。

完成这些后,您可以使用任何编辑器查看输出文件内容。

还可以选择直接使用 hadoop 命令。

正如清单12所示:

清单12.清单12.读取Hadoop输出

$>hadoopdfs-catpart-r-00000

05-12-201043

06-12-2010143

07-12-2010112

08-12-2010136

09-12-2010178

10-12-2010114

11-12-2010114

12-12-201079

如果您像我一样,在 清单12 中首先会注意到的就是每天地震数—12月9日就有178次地震。

希望您也会注意到Hadoop实现了我所想要的:

整齐地列出我的研究范围内每天的地震次数。

回页首

编写另一个Mapper

接下来,我想找到地震发生在哪里,以及如何快速计算出在我的研究范围内记录地震次数最多的是哪个区域。

当然,您已经猜到了,Hadoop可以轻松地做到。

在这个案例中,键不再是日期而是区域。

因此,我编写了一个新的 Mapper 类。

清单13.清单13.一个新的map实现

publicclassEarthQuakeLocationMapperextendsMapper

IntWritable>{

@Override

protectedvoidmap(LongWritablekey,Textvalue,Contextcontext)throwsIOException,

InterruptedException{

if(key.get()>0){

String[]lines=newCSVParser().parseLine(value.toString());

context.write(newText(lines[9]),newIntWritable

(1));

}

}

}

和之前获取日期然后进行转换相比,在 清单13 中我所作的是获取位置,这是CSV阵列中的最后一个条目。

相比一个庞大的位置和数字列表,我将结果限制在那些7天内出现10次的区域。

清单14.清单14.哪里的地震较多?

publicclassEarthQuakeLocationReducerextends

Reducer{@Overrideprotectedvoid

reduce(Textkey,Iterablevalues,Contextcontext)throws

IOException,InterruptedException{intcount=0;for(IntWritablevalue:

values){count++;}if(count>=10){

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1