黑猴子的家:Spark Core 实操案例

结合实际生产情况,编写一个统计功能,通过分析CDN或者Nginx的日志文件,统计出访问的pv(访问量 Page View)、UV(访问数Unique Visitor指独立访客访问数)、IP地址、访问来源等相关数据。

1、日志数据格式
IP命中率响应时间请求时间请求方法请求URL请求协议状态吗响应大小推荐网址用户代理
ClientIPHit/MissResponseTime[Time Zone]MethodURLProtocolStatusCodeTrafficSizeRefererUserAgent
100.79.121.48HIT33[15/Feb/2017:00:00:46 +0800]GEThttp://cdn.v.abc.com.cn/videojs/video.jsHTTP/1.1200174055http://www.abc.com.cn/Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+Trident/4.0;)
2、计算独立IP数
(1)计算思路

计算独立IP数主要是两步1. 从每行日志中筛选出IP地址2. 去除重复的IP得到独立IP数

(2)计算过程

flatMap(x=>IPPattern findFirstIn(x)) 通过正则取出每行日志中的IP地址
map(x=>(x,1)) 将每行中的IP映射成 (IP,1),形成一个Pair RDD
reduceByKey((x,y)=>x+y) 将相同的IP合并,得到 (IP,数量)
sortBy(_._2,false) 按IP大小排序

(3)统计结果
(114.55.227.102,9348)
(220.191.255.197,2640)
(115.236.173.94,2476)
(183.129.221.102,2187)
(112.53.73.66,1794)
(115.236.173.95,1650)
(220.191.254.129,1278)
(218.88.25.200,751)
(183.129.221.104,569)
(115.236.173.93,529)
独立IP数:43649
(4)具体步骤
[heihouzi@hadoop102 spark]$ bin/spark-shell --master local[*]

scala> val cdnRDD = sc.textFile("./cdn.txt",20).cache
cdnRDD: org.apache.spark.rdd.RDD[String] 
               = ./cdn.txt MapPartitionsRDD[1] at textFile at <console>:24

//匹配IP地址
scala> val IPPattern = "((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))".r
IPPattern: scala.util.matching.Regex = ((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))

scala> val ipnums = cdnRDD.flatMap(x => (IPPattern findFirstIn x)).map(y => (y,1)).reduceByKey(_+_).sortBy(_._2,false)
ipnums: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[9] at sortBy at <console>:28

scala> ipnums.take(10).foreach(println _)
(114.55.227.102,9348)                                                           
(220.191.255.197,2640)
(115.236.173.94,2476)
(183.129.221.102,2187)
(112.53.73.66,1794)
(115.236.173.95,1650)
(220.191.254.129,1278)
(218.88.25.200,751)
(183.129.221.104,569)
(115.236.173.93,529)

scala> ipnums.count
res5: Long = 43649
3、统计每个视频独立IP数

有时我们不但需要知道全网访问的独立IP数,更想知道每个视频访问的独立IP数

(1)计算思路

计算过程主要分为三步1. 筛选视频文件,将每行日志拆分成 (文件名,IP地址)形式2. 按文件名分组,相当于数据库的Group by 这时RDD的结构为(文件名,[IP1,IP1,IP2,…]),这时IP有重复3. 将每个文件名中的IP地址去重,这时RDD的结果为(文件名,[IP1,IP2,…]),这时IP没有重复

(2)计算过程

filter(x=>x.matches(“.([0-9]+).mp4.“)) 筛选日志中的视频请求
map(x=>getFileNameAndIp(x)) 将每行日志格式化成 (文件名,IP)这种格式
groupByKey() 按文件名分组,这时RDD 结构为 (文件名,[IP1,IP1,IP2….]),IP有重复
map(x=>(x._1,x.2.toList.distinct)) 去除value中重复的IP地址
sortBy(
._2.size,false) 按IP数排序

(3)计算结果
视频:141081.mp4 独立IP数:2393
视频:140995.mp4 独立IP数:2050
视频:141027.mp4 独立IP数:1784
视频:141090.mp4 独立IP数:1702
视频:141032.mp4 独立IP数:1528
视频:89973.mp4 独立IP数:1523
视频:141080.mp4 独立IP数:1425
视频:141035.mp4 独立IP数:1321
视频:141082.mp4 独立IP数:1272
视频:140938.mp4 独立IP数:816
(4)具体步骤
scala> val cdnRDD = sc.textFile("./cdn.txt",20).cache
cdnRDD: org.apache.spark.rdd.RDD[String] = ./cdn.txt MapPartitionsRDD[1] at textFile at <console>:24

//匹配IP地址
scala> val IPPattern = "((?:(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d)))\\.){3}(?:25[0-5]|2[0-4]\\d|((1\\d{2})|([1-9]?\\d))))".r
IPPattern: scala.util.matching.Regex = ((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))

//匹配视频文件名
scala> val videoPattern = "([0-9]+).mp4".r
videoPattern: scala.util.matching.Regex = ([0-9]+).mp4

scala> cdnRDD.filter(x => x.matches(".*([0-9]+)\\.mp4.*")).map(x => (videoPattern findFirstIn x,IPPattern findFirstIn x)).groupByKey.map(x => (x._1,x._2.toList.distinct)).sortBy(_._2.size,false).take(1).foreach(x => println("\n视频:"+x._1+"-------独立ip数:    "+x._2))
4、统计一天中每个小时间的流量

有时我想知道网站每小时视频的观看流量,看看用户都喜欢在什么时间段过来看视频

(1)计算思路

将日志中的访问时间及请求大小,两个数据提取出来形成 RDD (访问时间,访问大小),这里要去除404之类的非法请求

按访问时间分组形成 RDD (访问时间,[大小1,大小2,….])

将访问时间对应的大小相加形成 (访问时间,总大小)

(2)计算过程

filter(x=>isMatch(httpSizePattern,x)).filter(x=>isMatch(timePattern,x)) 过滤非法请求
map(x=>getTimeAndSize(x)) 将日志格式化成 RDD(请求小时,请求大小)
groupByKey() 按请求时间分组形成 RDD(请求小时,[大小1,大小2,….])
map(x=>(x._1,x._2.sum)) 将每小时的请求大小相加,形成 RDD(请求小时,总大小)

(3)计算结果
00时 CDN流量=14G
01时 CDN流量=3G
02时 CDN流量=5G
03时 CDN流量=3G
04时 CDN流量=3G
05时 CDN流量=4G
06时 CDN流量=11G
07时 CDN流量=22G
08时 CDN流量=43G
09时 CDN流量=52G
10时 CDN流量=61G
11时 CDN流量=45G
12时 CDN流量=46G
13时 CDN流量=51G
14时 CDN流量=55G
15时 CDN流量=45G
16时 CDN流量=45G
17时 CDN流量=44G
18时 CDN流量=45G
19时 CDN流量=51G
20时 CDN流量=55G
21时 CDN流量=53G
22时 CDN流量=42G
23时 CDN流量=25G
(4)具体步骤
 scala> val cdnRDD = sc.textFile("./cdn.txt",20).cache
cdnRDD: org.apache.spark.rdd.RDD[String] = ./cdn.txt MapPartitionsRDD[1] at textFile at <console>:24

//匹配 http 响应码和请求数据大小
scala> val httpSizePattern = ".*\\s(200|206|304)\\s([0-9]+)\\s.*".r
httpSizePattern: scala.util.matching.Regex = .*\s(200|206|304)\s([0-9]+)\s.*

//[15/Feb/2017:11:17:13 +0800]  匹配 2017:11 按每小时播放量统计
scala> val timePattern = ".*(2017):([0-9]{2}):[0-9]{2}:[0-9]{2}.*".r
timePattern: scala.util.matching.Regex = .*(2017):([0-9]{2}):[0-9]{2}:[0-9]{2}.*

scala> import scala.util.matching.Regex
import scala.util.matching.Regex

scala> :paste
// Entering paste mode (ctrl-D to finish)

def isMatch(pattern: Regex, str: String) = {
  str match {
    case pattern(_*) => true
    case _ => false
  }
}

def getTimeAndSize(line: String) = {
  var res = ("", 0L)
  try {
    val httpSizePattern(code, size) = line
    val timePattern(year, hour) = line
    res = (hour, size.toLong)
  } catch {
    case ex: Exception => ex.printStackTrace()
  }
  res
}


// Exiting paste mode, now interpreting.

isMatch: (pattern: scala.util.matching.Regex, str: String)Boolean
getTimeAndSize: (line: String)(String, Long)

scala> cdnRDD.filter(x=>isMatch(httpSizePattern,x)).filter(x=>isMatch(timePattern,x)).map(x=>getTimeAndSize(x)).groupByKey().map(x=>(x._1,x._2.sum)).sortByKey().foreach(x=>println(x._1+"时 CDN流量="+x._2/(102424*1024)+"G"))
02时 CDN流量=5G                                                                 
00时 CDN流量=14G
01时 CDN流量=3G
03时 CDN流量=3G
04时 CDN流量=3G
05时 CDN流量=4G
06时 CDN流量=11G
07时 CDN流量=22G
09时 CDN流量=52G
08时 CDN流量=43G
11时 CDN流量=45G
10时 CDN流量=61G
12时 CDN流量=46G
13时 CDN流量=51G
14时 CDN流量=55G
15时 CDN流量=45G
16时 CDN流量=45G
17时 CDN流量=44G
18时 CDN流量=45G
19时 CDN流量=51G
20时 CDN流量=55G
22时 CDN流量=42G
21时 CDN流量=53G
23时 CDN流量=25G
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值