目的:统计某个手机号在某个时间段内产生多少流量。
文件在
一共11个字段,需要知道 时间【本例数据太少,基本全加】,手机号,流量上行,流量下行,其实就是三个字段。
数据准备:把谁作为key,把谁作为value?【我要按照手机号分组的啊!类似Wordcount一样,hello 5次,Jerry 1次。我们可以把属性封装为一个对象里(类似c语言的结构体)。
这是个封装的思想】因此,我们首先应该定义一个bin,实现Writable接口。
为了模拟真实的生产环境,我们使用maven,(当然有一些公司要求必须在Linux下开发),Windows和Linux的Java分隔符是不一样的。
这个mapreduce在Linux下开发,以后在Linux下开发要使用Linux的图形界面,要在图形界面上安装eclipse。首先将eclipse上传
首先下载一个适合虚拟机的eclipse-jee-linux.tar.gz
之后tar -zxvf eclipse-jee.tar.gz -C /usr/local/ 把它解压到这个文件夹里
然后,创建一个快捷方式,桌面上右键——创建启动器——之后在路径里面选择usr/local/eclipse,然后点选里面的eclipse。桌面上的图片也可以换;如果不换是一个小弹簧,换的话同样在eclipse那个文件夹里面找,最下面就是一张图片,双击即可。
打开eclipse,选择workbench,之后在project explorer里面右键——创建一个project——然后点击文件夹maven——选择maven project——勾选“create a simple project ”
GAV听老师说,是所谓的“坐标”
点finish之后,发现有问题,我的虚拟机不能上网,需要maven的一些依赖,于是我们手动下载maven需要的包m2.tar.gz
之后放在虚拟机里解压 tar -zxvf m2.tar.gz
之后,先更新maven,右键datacount——maven——update maven
我并没有安装maven程序,因为eclipse自带了maven的插件,下载了m2文件,在目录里解压之后,无法右键——maven——update project,我这里报错了
百度了好多方法,也没能解决,求大神。。。
http://www.oschina.net/question/2263364_220988?sort=time
有人说了是没配置myeclipse。我准备看看。。如何配置。。
后来,我机智的放弃了。。。我选择了用Windows里的eclipse来玩。因为Windows里能联网,直接能配置好maven
看到这个标志,就是maven配置好的样子了
正常情况下,上面带个叉,是不行的,后来联网后,那个×变成了叹号! 就是可以了。
接下来,第一步是配置pom文件。
双击,点pom.xml。 出现是这样的
【后续】:我后来试了好几次,无论如何也在Linux上安装不成这个datacount,后来,我按照txt上的说明,先下载了maven的安装包,然后再解压maven的包,再配置maven的环境变量,再配置xml,再安装eclipse,再把.m2.tar.gz解压到root根目录下,成功了。
我怀疑上次失败的原因有两点:第一是没有安装maven,第二是因为解压.m2次数过多,产生了太多次的lastUpdate文件删不干净【网上有人说必须全删了才能成功,但是我电脑搜索起来太费时间】。最后的方式是记得给虚拟机快照。。我后来重新回到了之前的快照,然后再干干净净地弄一次,才成功的。。
还有第三点!我怀疑是解压.m2位置的关系,如果你是root用户,你就解压到/root/下面,如果你是哪个用户就解压到哪个下面,也可能是这个位置的原因
所以在这儿也有个教训。。没事儿记得多给虚拟机拍快照。。
好啦 接下来继续往下走,第一步继续配置pom.xml文件
dependencies,hadoop-common包,不管你是用mapreduce还是hdfs,都要引用的,
此时,我左边的maven dependencies里面还是啥都没有,我一Ctrl+s就都有了,这些都是maven的依赖
由于我们要开发mapreduce程序,因此还要增加hadoop-mapreduce-client-core依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.2.0</version>
</dependency>
【项目经理特喜欢】
接下来,要写mapreduce程序了,新建一个class
包名和类名如图所示
注意:Linux下面的eclipse和Windows下面的eclipse有一点点小区别,快捷键有可能不一样,这样的话,要去Windows——preference——keys——content assist里面改
map阶段干什么?把字段拆分,之后封装在对象里面,(一次上网信息我封装在DataBean里面)
接下来reduce阶段,key是手机号,value是4个DataBean的集合,我对DataBean里的数据进行求和
代码如下:
package cn.itcast.hadoop.mr.dc;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.jobhistory.ReduceAttemptFinished;
public class DataCount {
public static void main(String[] args) {
// 最后,写它的入口程序
Configuration conf =new Configuration();
Job job = Job.getInstance(conf);
//我的configuration要对job进行个性化配置,不配置的话MR会读全局到信息
job.setJarByClass(DataCount.class);
//把main方法所在的类设上去
job.setMapperClass(DCMapper.class);
//在k2k3 v2v3类型一样到时候,下面两个可以省略
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(DataBean.class);
//args可以接收数组
FileInputFormat.setInputPaths(job, new Path(args[0]));
job.setReducerClass(DCReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(DataBean.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);//等待把详情打印出来
}
//首先计算我的输入和输出,先看一下我到输入文件和输出接口
//首先定义一个类,继承mapper,确定四个泛型
public static class DCMapper extends Mapper<LongWritable, Text, Text, DataBean>
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
//实现map方法
//十一个字段,key所偏移量,value是一行内容
String line = value.toString();
String[] fields = line.split("\t");
String telNo = fields[1];
long up = Long.parseLong(fields[8]);
long down = Long.parseLong(fields[9]);
//然后是传递,由于不能一下子传递这么多数据,因此封装到bean里边
DataBean bean = new DataBean(telNo,up,down);
context.write(new Text(telNo), bean);
}
//如上所言,我定义类一个静态内部类,四个泛型分别代表mapper的输入和输出,K1V1K2V2
//k1我们用不上,还是longwritable,V1由于所文本类型,每行代表一个文本输出,因此是TEXT,k2,我们按照手机号来分组,可以统计流量。手机号也是text类型。我们自己定义一个databean
//我自己写一个databean,这个一定要实现wiritable接口
//写完databean类之后,我下一步可以在这个类里面重写map方法了
}
public static class DCReducer extends Reducer<Text, DataBean, Text, DataBean>{
@Override
protected void reduce(Text key, Iterable<DataBean> v2s,
Context context)
throws IOException, InterruptedException {
//跟wordcount一样
long up_sum = 0;
long down_sum = 0;
for(DataBean bean:v2s){
up_sum +=bean.getUpPayLoad();
down_sum += bean.getDownPayLoad();
}
DataBean bean = new DataBean("",up_sum,down_sum);
context.write(key, bean);
}
}
后面是自己定义的DataBean的代码:
package cn.itcast.hadoop.mr.dc;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.Writable;
public class DataBean implements Writable{
private String telNo;
private long upPayLoad;
private long downPayLoad;
private long totalPayLoad;
//我为了赋值方便,添加一个有参的构造方法
//添加里一个有参的构造方法之后最好还要添加一个无参的构造方法
public DataBean(){}
//没有返回值,构造方法名与类名相同,说明是一个构造方法
public DataBean(String telNo, long upPayLoad, long downPayLoad) {
this.telNo = telNo;
this.upPayLoad = upPayLoad;
this.downPayLoad = downPayLoad;
this.totalPayLoad = upPayLoad + downPayLoad;
}
//序列化:将内存(对象)里到信息放在字节流里
//注意:1.序列和反序列化顺序要一致.2 类型(手机号用UTF)
public void write(DataOutput out) throws IOException {
// TODO Auto-generated method stub
out.writeUTF(telNo);
out.writeLong(upPayLoad);
out.writeLong(downPayLoad);
out.writeLong(totalPayLoad);
}
//反序列化:将字节流里到内容读出来给对象赋值
public void readFields(DataInput in) throws IOException {
// TODO Auto-generated method stub
this.telNo = in.readUTF();
this.upPayLoad = in.readLong();
this.downPayLoad = in.readLong();
this.totalPayLoad = in.readLong();
}
//重写toString方法
@Override
public String toString() {
// TODO Auto-generated method stub
return this.upPayLoad + "\t" + this.downPayLoad;
}
public String getTelNo() {
return telNo;
}
public void setTelNo(String telNo) {
this.telNo = telNo;
}
public long getUpPayLoad() {
return upPayLoad;
}
public void setUpPayLoad(long upPayLoad) {
this.upPayLoad = upPayLoad;
}
public long getDownPayLoad() {
return downPayLoad;
}
public void setDownPayLoad(long downPayLoad) {
this.downPayLoad = downPayLoad;
}
public long getTotalPayLoad() {
return totalPayLoad;
}
public void setTotalPayLoad(long totalPayLoad) {
this.totalPayLoad = totalPayLoad;
}
}
之后在hadoop上面上传HTTP那个文件,之后把这个两个Java打成jar包,放在/root/examples里面,然后就在hadoop里可以打开,
很遗憾,我的mapreduce并没有运行成功。我也不知道为什么。。不过我心好累。。先消化消化这些代码吧。。