Flink CEP 简介
什么是复杂时间的处理的CEP
复杂事件处理(CEP)是一种基于流处理的技术
将系统数据看做不同的类型的数据
通过分析事件之间的关系,建立不同的事件关系序列库
利用过滤,关联,聚合等技术,由简单事件产生高级事件
通过模式规则的方式对重要是的信息进行跟踪和分析,从数据中发掘有价值的信息.
Flink CEP是一个基于Flink的复杂事件处理库,和机器学习库是一样的,是Flink的一个分支
Flink CEP可以从多个数据流中发现复杂时间,识别有意义的事件(例如机会或者威胁),并尽快的做出响应,而不是需要等待几天或者几个月相当长的时间才发现问题.
例如:监控银行卡被人盗刷. 滴滴打车对乘客的乘车时间进行监控.
** 代码开发步骤**
1.开启Flink流处理执行环境[env]
2.env.setStreamingTimeCharacteristic(TimeCharacteristic.EventTime)
3.set…(流处理执行中的程序设定)
4.定义匹配模式
1).设置begin
2).设置第一个where
3).设置next
4).设置第二个where
5).设置within(时间在2秒内)
5.匹配流数据
1)使用keyBy进行分流,确保同一个用户的登录日志,能够进入到一个数据流中.
6.从匹配好的数据流中获取数据
1)调用select方法,可以后去到每个阶段所匹配的数据
//定义一个样例类
case class LoginEvent(userId:String, message:String, timestamp:Long){
override def toString: String = userId
}
//main方法实现方式
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.setParallelism(1)
val loginEventStream = env.fromCollection(List(
LoginEvent(1, "192.168.0.1", "fail", 1558430842),
LoginEvent(1, "192.168.0.2", "fail", 1558430843),
LoginEvent(1, "192.168.0.3", "fail", 1558430844),
LoginEvent(2, "192.168.10.10", "success", 1558430845)
)).assignAscendingTimestamps(_.eventTime * 1000)
// 定义匹配模式
val loginFailPattern = Pattern.begin[LoginEvent]("begin")
.where(_.eventType == "fail")
.next("next")
.where(_.eventType == "fail")
.within(Time.seconds(2))
// 在数据流中匹配出定义好的模式
val patternStream = CEP.pattern(loginEventStream.keyBy(_.userId), loginFailPattern)
// .select方法传入一个 pattern select function,当检测到定义好的模式序列时就会调用
val loginFailDataStream = patternStream
.select(pattern => {
val first = pattern.getOrElse("begin", null).iterator.next()
val second = pattern.getOrElse("next", null).iterator.next()
(second.userId, second.ip, second.eventType)
})
// 将匹配到的符合条件的事件打印出来
loginFailDataStream.print()
env.execute("Login Fail Detect Job")
Flink CEP API
模式与模式匹配
模式API定义从输入流中提取的复杂模式序列
每个复杂模式序列是由多个简单的模式组成,例如:
模式:寻找具有相同的属性的单个事件.
将简单的模式称为模式,将最终在数据流中进行搜索匹配的复杂模式序列称为模式序列.
匹配是一系列输入事件,这些事件通过一系列有效的模式转换,能够访问复杂模式图的所有模式.
每个模式必须具有唯一的名称,我们可以使用模式名称来表示该模式匹配到的事件.
单个模式
一个模式既可以是单例的,也可以是循环的
1).单例模式接受单个事件
2).循环模式可以接受多个事件.
一般情况下,模式都是单例的,可以使用量词将其转换为循环模式
每个模式可以带有一个或者多个条件,这些条件是基于事件接受进行定义的.
或者说,每个模式通过一个或多个条件来匹配和接收事件.
量词
在Flink CEP 中,可以使用一下方式指定循环模式:
1)pattern.oneOrMore() 一个给定的时间有一次或多次出现.
2)pattern.times(#oftimes)一个给定类型的事件出现了指定的次数.
3)pattern.times(#fromTimes,#toTimes)一个给定类型的事件出现的次数在指定次数范围内.
4)可以使用pattern.greedy()方法将模式变成循环模式,但是不能让一组模式都变成循环模式.
5)使用pattern.optional()方法将循环模式变成可选的,即可以是循环模式也可以是单个模式.
6)greedy: as many as possible 贪婪模式尽可能的重复.
案例:
用户如果在10分钟内,同事输入TMD超过5次,就认为为恶意用户,并识别该用户
使用Flink CEP 检测刷屏用户.
package cn.itcast.cep
import java.util
import org.apache.flink.api.scala._
import org.apache.flink.cep.PatternSelectFunction
import org.apache.flink.cep.scala.{CEP, PatternStream}
import org.apache.flink.cep.scala.pattern.Pattern
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala.{DataStream, OutputTag, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.windowing.time.Time
object FlinkDemo01 {
case class LoginEvent(userId:String, message:String, timestamp:Long){
override def toString: String = userId
}
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
// 使用IngestionTime作为EventTime
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
// 用于观察测试数据处理顺序
env.setParallelism(1)
// 模拟数据源
val loginEventStream: DataStream[LoginEvent] = env.fromCollection(
List(
LoginEvent("1", "TMD", 1558430842),
LoginEvent("1", "TMD", 1558430843),
LoginEvent("1", "TMD", 1558430845),
LoginEvent("1", "TMD", 1558430850),
LoginEvent("2", "TMD", 1558430851),
LoginEvent("1", "TMD", 1558430852)
)
).assignAscendingTimestamps(_.timestamp * 1000)
//定义模式
val loginEventPattern: Pattern[LoginEvent, LoginEvent] = Pattern.begin[LoginEvent]("begin")
.where(_.message == "TMD")
.times(5)
.within(Time.minutes(10))
//匹配模式
val patternStream: PatternStream[LoginEvent] = CEP.pattern(loginEventStream.keyBy(_.userId), loginEventPattern)
import scala.collection.Map
val result = patternStream.select((pattern:Map[String, Iterable[LoginEvent]])=> {
val first = pattern.getOrElse("begin", null).iterator.next()
(first.userId, first.timestamp)
})
//恶意用户
result.print("恶意用户>>>")
env.execute()
}
}
迭代条件
该条件基于先前接收事件的属性或其子集的系统信息来接受后续事件.
案例:
用户在10分钟内,同事连续说同一句话5次,进行识别
使用Flink CEP 检测用户刷屏
object FlinkDemo02 {
case class Message(userId: String, ip: String, msg: String)
def main(args: Array[String]): Unit = {
//初始化运行环境
val env = StreamExecutionEnvironment.getExecutionEnvironment
//设置并行度
env.setParallelism(1)
// 模拟数据源
val loginEventStream: DataStream[Message] = env.fromCollection(
List(
Message("1", "192.168.0.1", "beijing"),
Message("1", "192.168.0.2", "beijing"),
Message("1", "192.168.0.3", "beijing"),
Message("1", "192.168.0.4", "beijing"),
Message("2", "192.168.10.10", "shanghai"),
Message("3", "192.168.10.10", "beijing"),
Message("3", "192.168.10.11", "beijing"),
Message("4", "192.168.10.10", "beijing"),
Message("5", "192.168.10.11", "shanghai"),
Message("4", "192.168.10.12", "beijing"),
Message("5", "192.168.10.13", "shanghai"),
Message("5", "192.168.10.14", "shanghai"),
Message("5", "192.168.10.15", "beijing"),
Message("6", "192.168.10.16", "beijing"),
Message("6", "192.168.10.17", "beijing"),
Message("6", "192.168.10.18", "beijing"),
Message("5", "192.168.10.18", "shanghai"),
Message("6", "192.168.10.19", "beijing"),
Message("6", "192.168.10.19", "beijing"),
Message("5", "192.168.10.18", "shanghai")
)
)
//定义模式
val loginbeijingPattern = Pattern.begin[Message]("start")
.where(_.msg != null) //一条登录失败
.times(5).optional //将满足五次的数据配对打印
.within(Time.minutes(10))
//进行分组匹配
val loginbeijingDataPattern = CEP.pattern(loginEventStream.keyBy(_.userId), loginbeijingPattern)
//查找符合规则的数据
val loginbeijingResult: DataStream[Option[Iterable[Message]]] = loginbeijingDataPattern.select(patternSelectFun = (pattern: collection.Map[String, Iterable[Message]]) => {
var loginEventList: Option[Iterable[Message]] = null
loginEventList = pattern.get("start") match {
case Some(value) => {
if (value.toList.map(x => (x.userId, x.msg)).distinct.size == 1) {
Some(value)
} else {
None
}
}
}
loginEventList
})
//打印测试
loginbeijingResult.filter(x=>x!=None).map(x=>{
x match {
case Some(value)=> value
}
}).print()
env.execute("FlinkDemo02")
}
}