package HomeWork.Core_logAnaly
import org.apache.spark.{SparkConf, SparkContext}
/***
* User:yunfei
* Date:22/10/23
* Description:
* 日志文件格式:
* 域名 流量 资源 ip 访问时间
*
* www.zhibo8.com 3040 https://www.zhibo8.com/video/music/djembe.pdf 10.0.0.1 [2017-07-13 19:19:00]
*
* */
class LogAnalyzer(fileName: String) extends Serializable {
val lines=sc.textFile(fileName)
@transient val sc=createLocalSparkContext()
/**
* (1)task not serializable 出现“task not serializable"这个错误,一般是因为在map、filter等的参数使用了外部的变量,
* 但是这个变量不能序列化(比如上面这个SparkContext)。特别是当引用了某个类(经常是当前类)的
* (2)当引用了某个类(经常是当前类)的成员函数或变量时,会导致这个类的所有成员(整个类)都需要支持序列化。虽然许多情形下,当前类使用了“extends Serializable”声明支持序列化,
* 但是由于某些字段不支持序列化,仍然会导致整个类序列化时出现问题,最终导致出现Task未序列化问题。
* (3)解决办法:要么就直接在
* 如果程序依赖的值相对固定,可取固定的值,或定义在map、filter等操作内部,或定义在scala
* object对象中(类似于Java中的static变量)
* 如果依赖值需要程序调用时动态指定(以函数参数形式),则在map、filter等操作时,可不直接引用该成员变量,
* 而是在类似上面例子的getResult函数中根据成员变量的值重新定义一个局部变量,这样map等算子就无需引用类的成员变量。
*/
def createLocalSparkContext()={
val sparkconf=new SparkConf().setMaster("local[2]").setAppName("LogAnalyzer")
new SparkContext(sparkconf)
}
def stopSparkContext()={
sc.stop()
}
//TODO..,求每个域名的流量
/**
*(1)获取1,2字段
*(2)按域名分组
*(3)分组求和reduceBykey(_+_)
**/
def prinTrafficByDomain()={
lines.map(x=>{
val recod=x.split("\t")
var traffic=0L //这里用val要报错,好像是因为var才能tolong
try {
traffic=recod(LogAnalyzer.TRAFFIC_IDX).trim.toLong //有问题就搞为0,例如遇到脏数据,比如是字符串(字符串tolong会有问题啊 ),就变为0
}
catch {
case e:Exception=>traffic=0L //
}
(recod(0),traffic)
}).reduceByKey(_+_).sortBy(_._2,false).foreach(println)
//回顾reduceBykey,它是将相同的key(域名),values(流量)相加,sortBy排序,false表示降序,true表示升序。
}
//TODO...求省份的访问次数
/**
* (1)获取ip
* (2)改造数据结构,每个key赋值1,变为(省份,1)
*/
def printByProvince()={
lines.map(x=>{
val record=x.split("\t")
var province=""
try {
province=record(LogAnalyzer.IP_IDX).trim
}
catch {
case e:Exception =>province=""
}
(province,1)
}).reduceByKey(_+_).sortBy(_._2,false).take(40).foreach(println)
}
//TODO..求每个域名下访问次数最多的文件资源
/**
* (1)数据处理,由下面的资源文件的定义可知,需要去掉https://www.youku.com
* https://www.youku.com/video/music/bass.mp4?key1=value1xxxxxxxx(后面x表示不同的访问同一个资源)
* /video/music/bass.mp4 <-- 资源文件定义
* (2)getResource方法取出资源文件。
* (3)需求分析,首先是域名下的资源,所以数据结构应该是(域名,资源),其次需要求TopN的,所以应该是
* ((域名,资源),1)
* */
def printMaxResourceByDomain()={
lines.map(x=>{
val record=x.split("\t")
((record(LogAnalyzer.DOMAIN_IDX),getResource(record(2))),1)
})
}.reduceByKey(_+_).groupBy(_._1._1)//xcz
.mapValues(_.toList.sortBy(_._2).reverse.take(10)).map(_._2).foreach(println)
//TODO...改造数据结构,得到资源文件
def getResource(url:String)={
val pathTemp=url.replaceFirst("//","")
var pathIndex=pathTemp.indexOf("/")
var path=""
if (pathIndex != -1){
path=pathTemp.substring(pathIndex)
pathIndex=path.indexOf("?")
if (pathIndex != -1){
path= path.substring(0,pathIndex)
}
path
}
}
}
object LogAnalyzer{
val APP_NAME = "LogAnalyzer"
val DOMAIN_IDX = 0 //域名
val TRAFFIC_IDX = 1 //流量
val RESOURCE_IDX = 2 //资源
val IP_IDX = 3 //ip
val GET_TIME = 4 //访问时间
def apply(fileName: String): LogAnalyzer = new LogAnalyzer(fileName)
}