一、测试数据:手机上网日志

1.1 日志

假设我们如下一个日志文件,这个文件的内容是来自某个电信运营商的手机上网日志,文件的内容已经经过了优化,格式比较规整,便于学习研究。

每一行不同的字段又有不同的含义,具体的含义如下图所示:




ios 网络日志收集 手机 网络日志_ios 网络日志收集


1.2 要实现的目标

有了上面的测试数据—手机上网日志,那么问题来了,如何通过map-reduce实现统计不同手机号用户的上网流量信息?通过上表可知,第6~9个字段是关于流量的信息,也就是说我们需要为每个用户统计其upPackNum、downPackNum、upPayLoad以及downPayLoad这个四个字段的数量和,达到以下的显示结果:

一、解决思路

2.1 Writable接口

经过上一篇的学习,我们知道了在Hadoop中操作所有的数据类型都需要实现一个叫Writable的接口,实现了该接口才能够支持序列化,才能方便地在Hadoop中进行读取和写入。


ios 网络日志收集 手机 网络日志_字段_02


从上面的代码中可以看到Writable 接口只有两个方法的定义,一个是write 方法,一个是readFields 方法。前者是把对象的属性序列化到DataOutput 中去,后者是从DataInput 把数据反序列化到对象的属性中。(简称“读进来”,“写出去”)

java 中的基本类型有char、byte、boolean、short、int、float、double 共7 中基本类型,除了char,都有对应的Writable 类型。但是,没有我们需要的对应类型。于是,我们需要仿照现有的对应Writable 类型封装一个自定义的数据类型,以供本次试验使用。

2.2 封装KpiWritable类型

我们需要为每个用户统计其upPackNum、downPackNum、upPayLoad以及downPayLoad这个四个字段的数量和,而这个四个字段又都是long 类型,于是我们可以封装以下代码:


ios 网络日志收集 手机 网络日志_自定义_03


通过实现Writable接口的两个方法,就封装好了KpiWritable类型。

三、编程实现:依然MapReduce

3.1 自定义Mapper类


ios 网络日志收集 手机 网络日志_自定义_04


这里将第6~9个字段的数据都封装到KpiWritable类型中,并将手机号和KpiWritable作为传入下一阶段;

3.2 自定义Reducer类


ios 网络日志收集 手机 网络日志_自定义_05


这里将Map阶段每个手机号所对应的流量记录都一一进行相加求和,最后生成一个新的KpiWritable类型对象与手机号作为新的返回;

3.3 完整代码实现

完整的代码如下所示:


ios 网络日志收集 手机 网络日志_字段_06


ios 网络日志收集 手机 网络日志_封装_07


ios 网络日志收集 手机 网络日志_封装_08


ios 网络日志收集 手机 网络日志_自定义_09


3.4 调试运行效果


ios 网络日志收集 手机 网络日志_ios 网络日志收集_10