背景:
最近两个月在弄用户行为分析系统,我本来是做java ee, 一开始,在数据量还不是很大的情况下,用纯 java 实现数据分析。但是越来越痛苦,我的数据采用一级一级总结抽取的方法,第一级数据是用户上传的txt数据,系统转化一次一级数据的时间从无知觉-几分钟-十几分钟-几十分钟-一个多钟-到现在的接近两个钟点。而且,中途还得保证数据的完整性、程序的健壮性,举个例子,我要分析5000个用户文件,如果中途机器出现故障、程序报错、或者用户数据不规范,都会导致转化数据不完整,这个时候,我不知道哪些是已经被分析的数据,哪些数据是清白的,我得做容错处理,做读取标记,或者做事物回滚。而这些,如果涉及到关联,控制层关联缓存,缓存关联服务层,服务层关联dao层,dao层直接影响数据库,那就太麻烦了。
于是,打算用hadoop来做,hadoop 是用java 实现的,API看起来特别舒服!!! 断断续续看了一个星期,在此记录一下
步骤:
我完完全全把hadoop看成是一个java框架,类似于spring,学习步骤也是老套路:
先看看能做出什么样的效果
适用性和局限性
工作原理机制、模式
hello,world
看别人的代码快速解决当前实际需求
漫长的查看API、代码深入
上面的网络一大把资源,我这里总结一些学习资源:
hadoop 各版本下载地址:http://archive.apache.org/dist/hadoop/common/
hadoop 慕课网视频
hadoop API (更换版本即可) : http://hadoop.apache.org/docs/r2.6.0/api/
实战:
hadoop的3种运行模式中,我先用单机模式,如果在本地上安装hadoop很耗费时间,配置、跑wordCount的demo老是不成功的条件下,可以先不用本地安装。既然是个java框架,用maven导进来即可。
新建一个maven支持的java项目
pom.xml 文件引进hadoop,版本随意,注意下兼容jdk版本就行
<!-- hadoop -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
- log4j 的配置文件,打印log
log4j.rootLogger=INFO,Console,File
#控制台日志
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%p][%t][%d{yyyy-MM-dd HH\:mm\:ss}][%C] - %m%n
#普通文件日志
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=logs/ssm.log
log4j.appender.File.MaxFileSize=10MB
#输出日志,即debug日志
log4j.appender.File.Threshold=ALL
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=[%p][%t][%d{yyyy-MM-dd HH\:mm\:ss}][%C] - %m%n
- 编码
...
static class MyMapper extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(0);
private Text word = new Text();
@Override
protected void map(Object key, Text value, Mapper<Object, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
//分隔
String[] appSerial = value.toString().split("&");
//只要第一行第3个元素序列号
if(appSerial.length > 3) {
word.set(appSerial[2]);
context.write(word, one);
}
}
}
static class MyReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
private Text keyEx = new Text();
@Override
protected void reduce(Text key, Iterable<IntWritable> values,
Reducer<Text, IntWritable, Text, IntWritable>.Context context)
throws IOException, InterruptedException {
int sum = 0;
//叠加统计结果
for (IntWritable val : values) {
sum += val.get() + 1;
}
result.set(sum);
keyEx.set(key.toString());
context.write(keyEx, result);
}
}
...
上面的代码是核心的两个内部类,是hadoop对数据处理的工作模式
map 和 reduce 方法实际上相反的过程,就像分而治之的思想,皇帝要收税,把任务根据封地分为几个小任务,每个封地的诸侯收集封地的税,统一交给皇帝处理。数据在输入的时候,在输入拆分中为每个键值对调用一次map方法,把数据分成多份,让map并行处理,输出的结果作为reduce的输入,reduce方法为每一个键调用一次(这个键就是map出来的结果的键值)。
- 运行效果,31M的txt测试数据用了2~3分钟跑完,跟纯java的速度比火箭还快,!
后记–感悟:
1.学习新东西,都是从-迷茫-清晰-迷茫-清晰-…一步一步一个脚印,没有捷径
2.API,一年前的我很讨厌看API,尤其是纯英文的,而现在习惯了,经验+教训,一步一步一个脚印,没有捷径
3.编码,在业务系统中从来没注意过性能,但是当数据一大,平常一个毫无知觉的失误可能会引起很大的性能下降,数据量变到G级,我不得不考虑怎么减少循环、这个类要在哪里创建最好、我这一块代码有没有设计模式或是好的方法实现、用int还是Integer,异常要在哪里抛出…还是经验+教训,一步一步一个脚印,没有捷径
4.直觉:我现在用单机版本,本地跑,跟业务分开,那如果要部署上线呢,数据分析和业务结合怎么结合,如果客户要定制呢。现在强烈预感到下一个要接触的是工作流。
5.谢谢观看!!!