1.部分代码展示
import java.sql.Timestamp
import java.util
import java.util.regex.Pattern
import Streamning03.{InfluxDBUtils, ScalaBroadcastAlert, ScalaCDHRoleLog}
import org.apache.kafka.common.serialization.StringDeserializer
import org.apache.spark.sql.{Dataset, Row, SparkSession}
import org.apache.spark.streaming.kafka010.ConsumerStrategies.Subscribe
import org.apache.spark.streaming.kafka010.KafkaUtils
import org.apache.spark.streaming.kafka010.LocationStrategies.PreferConsistent
import org.apache.spark.streaming.{Duration, StreamingContext}
import org.influxdb.{InfluxDB, InfluxDBFactory}
import org.json.JSONObject
object ScalaOnLineLogAnalysis {
System.setProperty("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
//没有这个序列化,后面运行的时候会报一个序列化错误的
/**
* Created by jpwu on 2017/2/14.
*
* 主要使用spark streaming and spark sql来实现:
* 1.从kafka0.10 cluster读取json格式的log ,其格式: 机器名称 服务名称 时间 日志级别 日志信息
* {"time":"2017-04-11 22:40:47,981","logtype":"INFO","loginfo":"org.apache.hadoop.hdfs.server.datanode.DataNode:PacketResponder: BP-469682100-172.16.101.55-1489763711932:blk_1073775313_34497, type=HAS_DOWNSTREAM_IN_PIPELINE terminating"}
* {"time":"2017-04-11 22:40:48,120","logtype":"INFO","loginfo":"org.apache.hadoop.hdfs.server.datanode.DataNode:Receiving BP-469682100-172.16.101.55-1489763711932:blk_1073775314_34498 src: /172.16.101.59:49095 dest: /172.16.101.60:50010"}
*
* 2.每隔5秒统计最近15秒出现的机器,日志级别为info,debug,warn,error次数
*
* 3.每隔5秒统计最近15秒出现的机器,日志信息出现自定义alert词的次数
*
* 1.消费kafka json数据转换为DF,然后show()
* 2.group by语句
* 3.写入到InfluxDB
* 4.广播变量+更新(自定义预警关键词)
*
*/
val slide_interval: Duration = new Duration(5 * 1000)
//定义task滚动时间,Time interval after which the DStream generates a RDD (每隔一段时间返回一个RDD)
val window_width: Duration = new Duration(5 * 1000)
//定义窗口的时间, of the window; must be a multiple of this DStream's interval(必须是这个DStream时间在倍数)
val regexSpace: Pattern = Pattern.compile(" ")
//将给定的正则表达式编译成模式,当遇到需要匹配、查找、替换、判断字符串的情况发生,而这些情况有时又比较复杂
//正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串。
// 正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
val spiltstr: Array[String] = null
var scalacdhRoleLog = new ScalaCDHRoleLog()
//通过ScalaCDHRoleLog得到一个scalacdhRoleLog
var sqlstr: String = null
val recordTimestamp: Timestamp = null
val key: String = null
var value: String = null
var host_service_logtype: String = null
var influxDB:InfluxDB = null
val dbName: String = "online_log_analysis"
var jsonlogline: JSONObject = null
val alertList :util.ArrayList[String]= new util.ArrayList[String]
var alertsql: String = ""
def onlineLogsAnalysis(): Unit = {
try { //定义连接influxdb
influxDB = InfluxDBFactory.connect("http://" + InfluxDBUtils.getInfluxIP + ":" + InfluxDBUtils.getInfluxPORT(true), "admin", "admin")
//通过InfluxDBUtils里面的方法得到influxDB的ip和port来创建连接
val rp: String = InfluxDBUtils.defaultRetentionPolicy(influxDB.version)
//1.使用 SparkSession,JavaSparkContext, JavaStreamingContext来定义 对象 jssc
val ss: SparkSession = new SparkSession.Builder().master("local[2]")
.appName("OnLineLogAnalysis")
.config("spark.streaming.kafka.consumer.poll.ms", 150000)
.getOrCreate
//SparkSession 是Spark SQL 的入口。使用 Dataset 或者 Datafram 编写 Spark SQL 应用的时候,第一个要创建的对象就是 SparkSession。
//Note:在 Spark 2.0 中, SparkSession 合并了 SQLContext 和 HiveContext。
// SparkSession 的内部, 包含了SparkContext, SharedState,SessionState 几个对象
val sc = ss.sparkContext
/* SparkContext
1,Spark程序在运行的时候分为Driver和Executors两部分;
2,Spark的程序编写是基于SparkContext的,具体来说包含两方面:
Spark编程的核心基础—RDD,是由SparkContext来最初创建(第一个RDD一定是由SparkContext来创建的);
Spark程序的调度优化也是基于SparkContext;
3,Spark程序的注册时通过SparkContext实例化时候生产的对象来完成的(其实是SchedulerBackend来注册程序)
4,Spark程序运行的时候要通过Cluster Manager获得具体的计算资源,计算资源的获取也是通过SparkContext产生的对象来申请的(其实是SchedulerBackend来获取计算资源的);
5,SparkContext崩溃或者结束的时候整个Spark程序也结束啦!*/
val ssc = new StreamingContext(sc,slide_interval)
/*StreamingContext
1、通过创建输入DStream来创建输入数据源。
2、通过对DStream定义transformation和output算子操作,来定义实时计算逻辑。
3、调用StreamingContext的start()方法,来开始实时处理数据。
4、调用StreamingContext的awaitTermination()方法,来等待应用程序的终止。可以使用CTRL+C手动停止,或者就是让它持续不断的运行进行计算。
5、也可以通过调用StreamingContext的stop()方法,来停止应用程序。*/
/* 2.开启checkpoint机制,把checkpoint中的数据目录设置为hdfs目录
hdfs dfs -mkdir -p hdfs://nameservice1/spark/checkpointdata
hdfs dfs -chmod -R 777 hdfs://nameservice1/spark/checkpointdata
hdfs dfs -ls hdfs://nameservice1/spark/checkpointdata
*/
// ssc.checkpoint(".")
//3.设置kafka的map参数
val kafkaParams = Map[String, Object](
"bootstrap.servers" -> "192.168.137.251:9092" //这个只是连接kafka broker的一个接口
,"key.deserializer" -> classOf[StringDeserializer] //key,value的俩个序列化类
,"value.deserializer" -> classOf[StringDeserializer]
,"group.id" -> "use_a_separate_group_id_for_each_stream"//这个属性是使用Subscribe必须的
,"auto.offset.reset" -> "latest" //自动将偏移量重置为最小的
,"enable.auto.commit" -> (false: java.lang.Boolean)//表示偏移量不后期提交时时更新
,"max.partition.fetch.bytes" -> (10485760 : java.lang.Integer)//配置每个分区的最大数据量
,"request.timeout.ms" -> (210000 : java.lang.Integer)//配置客户端请求响应的最长时间
,"session.timeout.ms" -> (180000 : java.lang.Integer) //用于检测消费者失败的超时,如果再这个时间内broker都没有收到心跳,broker将会将这个消费者移除出去,并且再启动一个再平衡
,"heartbeat.interval.ms" -> (30000 : java.lang.Integer) //心跳机制确保消费者的会话保持活跃,并在新消费者加入团队时促进再平衡,大小通常是流的1/3
,"receive.buffer.bytes" -> (10485760 : java.lang.Integer)//读取数据时,TCP接收缓冲区的大小
)
//3.创建要从kafka去读取的topic的集合对象
//Collection<String> topics = Arrays.asList("online_log_analysis");
val topics = "onlinelogs".split(",").toSet
//4.输入流
val lines = KafkaUtils.createDirectStream[String, String](
ssc,
PreferConsistent, //它将始终在所有executor之间分配分区,防止负载不均
Subscribe[String, String](topics,kafkaParams) //这个是10之后版本的方法,传入的是map和object,前面是主题,后面是相应的参数
) //返回一个DStream
//5.将DS的RDD解析为JavaDStream<CDHRoleLog> A DStream of RDD's that contain parsed CDH Role Logs.
val cdhRoleLogFilterDStream = lines.map(x =>
{
//做一个map数据结构变成key,value类型
if(x.value().contains("INFO")==true || x.value().contains("WARN")==true || x.value().contains("ERROR")==true || x.value().contains("DEBUG")==true|| x.value().contains("FATAL")==true)
{ //contains这个函数返回的序列是否包含这个关键字
try{
jsonlogline = new JSONObject(x.value()) //这是一个json格式的转换方法
scalacdhRoleLog = new ScalaCDHRoleLog(jsonlogline.getString("hostname"),//获取与key相关联的字符串
jsonlogline.getString("servicename"),
jsonlogline.getString("time"),
jsonlogline.getString("logtype"),
jsonlogline.getString("loginfo"))
}
catch{
case e:Exception => {
println(e.toString())
scalacdhRoleLog=null;}
}
}
else {
scalacdhRoleLog = null;
}
scalacdhRoleLog
}).filter(_!= null)
//7.Splits the cdhRoleLogFilterDStream into a dstream of time windowed rdd's.
val windowDStream = cdhRoleLogFilterDStream.window(window_width, slide_interval)
//8.使用foreachRDD
windowDStream.foreachRDD(x => {
if (x.count == 0) {
println("No cdh role logs in this time interval")
//用来判断是否有RDD进来
}
// 8.2从RDD创建Dataset
val cdhRoleLogDR: Dataset[Row] = ss.createDataFrame(x, classOf[ScalaCDHRoleLog])
//x是传进来的rdd,后面就是schema
//8.3注册为临时表
cdhRoleLogDR.createOrReplaceTempView("cdhrolelogs")
//8.4调用自定义alert广播变量
val alertInfoList = ScalaBroadcastAlert.updateAndGet(ss).value
//8.5拼接SQL
if (alertInfoList.size > 0)
{ //定义alertsql
alertsql = ""
for (alertInfo <- alertInfoList) {
alertsql = alertsql + " logInfo like '%" + alertInfo + "%' "
}
alertsql = alertsql.substring(0, alertsql.length - 2)
//定义sql
sqlstr = "SELECT hostName,serviceName,logType,COUNT(logType) FROM cdhrolelogs GROUP BY hostName,serviceName,logType union " +
"SELECT t.hostName,t.serviceName,t.logType,COUNT(t.logType) FROM " +
"(SELECT hostName,serviceName,'alert' as logType FROM cdhrolelogs where " + alertsql + ") t " +
" GROUP BY t.hostName,t.serviceName,t.logType"
}
else {
sqlstr = "SELECT hostName,serviceName,logType,COUNT(logType) FROM cdhrolelogs GROUP BY hostName,serviceName,logType"
}
//8.6计算结果为List<Row>
val logtypecount = ss.sql(sqlstr).collectAsList
value = ""
//8.7循环处理
import scala.collection.JavaConversions._
for (rowlog <- logtypecount) {
host_service_logtype = rowlog.get(0) + "_" + rowlog.get(1) + "_" + rowlog.get(2)
value = value + "logtype_count,host_service_logtype=" + host_service_logtype + " count=" + rowlog.getLong(3).toString + "\n"
}
//8.8 存储至influxdb
if (value.length > 0) {
value = value.substring(0, value.length) //去除最后一个字符“,”
//打印
println(value)
//保存
influxDB.write(dbName, rp, InfluxDB.ConsistencyLevel.ONE, value)
}
})
ssc.start() //启动流式计算
ssc.awaitTermination() //等待中断
} catch {
case e: Exception =>
e.printStackTrace()
}
}
def main(args: Array[String]): Unit = {
onlineLogsAnalysis
}
}