1、CEP编程
上一节的代码实现中我们可以看到,直接把每次登录失败的数据存起来、设置定时器一段时间后再读取,这种做法尽管简单,但和我们开始的需求还是略有差异的。这种做法只能隔2秒之后去判断一下这期间是否有多次失败登录,而不是在一次登录失败之后、再一次登录失败时就立刻报警。这个需求如果严格实现起来,相当于要判断任意紧邻的事件,是否符合某种模式。这听起来就很复杂了,那有什么方式可以方便地实现呢?
很幸运,flink为我们提供了CEP(Complex Event Processing,复杂事件处理)库,用于在流中筛选符合某种复杂模式的事件。接下来我们就基于CEP来完成这个模块的实现。
在src/main/scala下继续创建LoginFailWithCep.scala文件,新建一个单例对象。样例类LoginEvent由于在LoginFail.scala已经定义,我们在同一个模块中就不需要再定义了。
2、在子模块(LoginFailDetect)
(1)创建LoginFailWithCep类
具体代码实现:
package com.study.LoginFailDetect
import org.apache.flink.cep.scala.CEP
import org.apache.flink.cep.scala.pattern.Pattern
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
object LoginFailWithCep {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.setParallelism(1)
val loginStream =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)
//定义一个匹配模式,next紧邻发生的事件
val loginFailPattern = Pattern.begin[LoginEvent]("begin").where(_.eventType == "fail")
.next("next").where(_.eventType == "fail")
.within(Time.seconds(2))
//在keyby之后的流中匹配出定义好的pattern stream
val patternStream = CEP.pattern(loginStream,loginFailPattern)
import scala.collection.Map
//从pattern stream中获取匹配到的事件流
val loginFailDataStream =patternStream.select(
(pattern:Map[String,Iterable[LoginEvent]]) =>{
val next =pattern.getOrElse("next",null).iterator.next()
(next.userId,next.ip,next.eventType)
}
)
.print()
env.execute("Login Fail Detect Job")
}
}
启动程序,控制台打印输出谢谢: