精通Apache Flink读书笔记--5
来源:互联网 发布:赢顺云交易软件下载 编辑:程序博客网 时间:2024/04/29 08:03
5、复杂事件处理(CEP)
5.1、什么是CEP?
CEP用于分析低延迟、频繁产生的不同来源的事件流。CEP可以帮助在复杂的、不相关的事件流中找出有意义的模式和复杂的关系,以接近实时或准实时的获得通知并阻止一些行为。
CEP支持在流上进行模式匹配,根据模式的条件不同,分为连续的条件或不连续的条件;模式的条件允许有时间的限制,当在条件范围内没有达到满足的条件时,会导致模式匹配超时。
CEP的工作流图:
看起来很简单,但是它有很多不同的功能:
1、输入的流数据,尽快产生结果2、在2个event流上,基于时间进行聚合类的计算3、提供实时/准实时的警告和通知4、在多样的数据源中产生关联并分析模式5、高吞吐、低延迟的处理
市场上有多种CEP的解决方案,例如Spark、Samza、Beam等,但他们都没有提供专门的library支持。但是Flink提供了专门的CEP library。
5.2、Flink CEP
Flink为CEP提供了专门的Flink CEP library,它包含如下组件:
1、Event Stream2、pattern定义3、pattern检测4、生成Alert
首先,开发人员要在DataStream流上定义出模式条件,之后Flink CEP引擎进行模式检测,必要时生成告警。
为了使用Flink CEP,我们需要导入依赖:
<dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-cep_2.10</artifactId> <version>1.2.0</version></dependency>
5.2.1、Event Streams
我们首先需要为Stream Event设计java pojo,但是注意,由于要对event对象进行对比,所以我们需要重写hashCode()方法和equals()方法。下面进行监控温度事件流。
创建抽象类MonitoringEvent,重写hashCode()和equals()方法;再创建POJO:TemperatureEvent,同样重写hashCode()和equals()方法:
MonitoringEvent:
package flink.cep;public abstract class MonitoringEvent { private String machineName; public String getMachineName() { return machineName; } public void setMachineName(String machineName) { this.machineName = machineName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((machineName == null) ? 0 : machineName.hashCode()); return result; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; MonitoringEvent other = (MonitoringEvent) obj; if(machineName == null) { if(other.machineName != null) { return false; }else if(!machineName.equals(other.machineName)) { return false; } } return true; } public MonitoringEvent(String machineName) { super(); this.machineName = machineName; }}
TemperatureEvent:
package flink.cep;public class TemperatureEvent extends MonitoringEvent{ public TemperatureEvent(String machineName) { super(machineName); } private double temperature; public double getTemperature() { return temperature; } public void setTemperature(double temperature) { this.temperature = temperature; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); long temp; temp = Double.doubleToLongBits(temperature); result = (int) (prime * result +(temp ^ (temp >>> 32))); return result; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(!super.equals(obj)) return false; if(getClass() != obj.getClass()) return false; TemperatureEvent other = (TemperatureEvent) obj; if(Double.doubleToLongBits(temperature) != Double.doubleToLongBits(other.temperature)) return false; return true; } @Override public String toString() { return "TemperatureEvent [getTemperature()=" + getTemperature() + ", getMachineName=" + getTemperature() + "]"; } public TemperatureEvent(String machineName, double temperature) { super(machineName); this.temperature = temperature; }}
创建env,创建source:
package temp;import flink.cep.TemperatureEvent;import org.apache.flink.streaming.api.datastream.DataStream;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Test { public static void main(String[] args) { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<TemperatureEvent> inputEventStream = env.fromElements( new TemperatureEvent("xyz",22.0), new TemperatureEvent("xyz",20.1), new TemperatureEvent("xyz",21.1), new TemperatureEvent("xyz",22.2), new TemperatureEvent("xyz",22.1), new TemperatureEvent("xyz",22.3), new TemperatureEvent("xyz",22.1), new TemperatureEvent("xyz",22.4), new TemperatureEvent("xyz",22.7), new TemperatureEvent("xyz",27.0)); }}
5.2.2、Pattern API
每个Pattern都应该包含几个步骤,或者叫做state。从一个state到另一个state,通常我们需要定义一些条件,例如下列的代码:
DataStream<Event> input = ...Pattern<Event, ?> pattern = Pattern.begin("start").where(evt -> evt.getId() == 42) .next("middle").subtype(SubEvent.class).where(subEvt -> subEvt.getVolume() >= 10.0) .followedBy("end").where(evt -> evt.getName().equals("end"));PatternStream<Event> patternStream = CEP.pattern(input, pattern);DataStream<Alert> result = patternStream.select(pattern -> { return createAlertFrom(pattern);});
每个state都应该有一个标示:
Pattern<Event, ?> start = Pattern.<Event>begin("start");
每个state都需要有一个唯一的名字,而且需要一个filter来过滤条件:
start.where(new FilterFunction<Event>() { @Override public boolean filter(Event value) { return ... // some condition }});
我们也可以通过subtype来限制event的子类型:
start.subtype(SubEvent.class).where(new FilterFunction<SubEvent>() { @Override public boolean filter(SubEvent value) { return ... // some condition }});
事实上,你可以多次调用subtype和where方法;而且如果where条件是不相关的,你可以通过or来指定一个单独的filter函数:
pattern.where(new FilterFunction<Event>() { @Override public boolean filter(Event value) { return ... // some condition }}).or(new FilterFunction<Event>() { @Override public boolean filter(Event value) { return ... // or condition }});
之后,我们可以在此条件基础上,通过next或者followedBy方法切换到下一个state,next()的意思是说上一步符合条件的元素之后紧挨着的元素;而followedBy并不要求一定是挨着的元素。这两者分别称为严格近邻和非严格近邻。
Pattern<Event, ?> strictNext = start.next("middle");
Pattern<Event, ?> nonStrictNext = start.followedBy("middle");
最后,我们可以将所有的Pattern的条件限定在一定的时间范围内:
next.within(Time.seconds(10));
这个时间可以是processing time,也可以是Event time。
5.2.3、Pattern 检测
通过一个input DataStream以及刚刚我们定义的Pattern,我们可以创建一个PatternStream:
DataStream<Event> input = ...Pattern<Event, ?> pattern = ...PatternStream<Event> patternStream = CEP.pattern(input, pattern);
一旦获得PatternStream,我们就可以通过select或flatSelect,从一个Map序列找到我们需要的告警信息。
5.2.3.1、select
select方法需要实现一个PatternSelectFunction,通过select方法来输出需要的警告。它接受一个Map对,包含string/event,其中key为state的名字,event则为真是的Event。
class MyPatternSelectFunction<IN, OUT> implements PatternSelectFunction<IN, OUT> { @Override public OUT select(Map<String, IN> pattern) { IN startEvent = pattern.get("start"); IN endEvent = pattern.get("end"); return new OUT(startEvent, endEvent); }}
其返回值仅为1条记录。
5.2.3.2、flatSelect
通过实现PatternFlatSelectFunction,实现与select相似的功能。唯一的区别就是flatSelect方法可以返回多条记录。
class MyPatternFlatSelectFunction<IN, OUT> implements PatternFlatSelectFunction<IN, OUT> { @Override public void select(Map<String, IN> pattern, Collector<OUT> collector) { IN startEvent = pattern.get("start"); IN endEvent = pattern.get("end"); for (int i = 0; i < startEvent.getValue(); i++ ) { collector.collect(new OUT(startEvent, endEvent)); } }}
5.2.4、超时事件的处理
通过within方法,我们的parttern规则限定在一定的窗口范围内。当有超过窗口时间后还到达的event,我们可以通过在select或flatSelect中,实现PatternTimeoutFunction/PatternFlatTimeoutFunction来处理这种情况。
PatternStream<Event> patternStream = CEP.pattern(input, pattern);DataStream<Either<TimeoutEvent, ComplexEvent>> result = patternStream.select( new PatternTimeoutFunction<Event, TimeoutEvent>() {...}, new PatternSelectFunction<Event, ComplexEvent>() {...});DataStream<Either<TimeoutEvent, ComplexEvent>> flatResult = patternStream.flatSelect( new PatternFlatTimeoutFunction<Event, TimeoutEvent>() {...}, new PatternFlatSelectFunction<Event, ComplexEvent>() {...});
5.3、例子
我们继续最开始时的温度检测的例子。
我们创建一个Alert类,表示在满足一定的pattern条件后,需要告警的内容:
package flink.cep;public class Alert { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Alert(String message) { this.message = message; } @Override public String toString() { return "Alert [message=" + message + "]"; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((message == null) ? 0 : message.hashCode()); return result; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(obj == null) return false; if(getClass() != obj.getClass()) return false; Alert other = (Alert) obj; if(message == null) { if(other.message != null) { return false; }else if(!message.equals(other.message)) { return false; } } return true; }}
最后,我们定义一个Pattern:当Event的温度超过26度时,立刻产生一个Alert信息,最终实现如下:
import flink.cep.Alert;import flink.cep.TemperatureEvent;import org.apache.flink.api.common.functions.FilterFunction;import org.apache.flink.cep.CEP;import org.apache.flink.cep.PatternSelectFunction;import org.apache.flink.cep.pattern.Pattern;import org.apache.flink.streaming.api.datastream.DataStream;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;import org.apache.flink.streaming.api.windowing.time.Time;import java.util.Map;public class Test { public static void main(String[] args) throws Exception{ StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); // DataStream : source DataStream<TemperatureEvent> inputEventStream = env.fromElements(new TemperatureEvent("xyz",22.0), new TemperatureEvent("xyz",20.1), new TemperatureEvent("xyz",21.1), new TemperatureEvent("xyz",22.2), new TemperatureEvent("xyz",22.1), new TemperatureEvent("xyz",22.3), new TemperatureEvent("xyz",22.1), new TemperatureEvent("xyz",22.4), new TemperatureEvent("xyz",22.7), new TemperatureEvent("xyz",27.0), new TemperatureEvent("xyz",30.0)); // 定义Pattern,检查10秒钟内温度是否高于26度 Pattern<TemperatureEvent,?> warningPattern = Pattern.<TemperatureEvent>begin("start") .subtype(TemperatureEvent.class) .where(new FilterFunction<TemperatureEvent>() { private static final long serialVersionUID = 1L; @Override public boolean filter(TemperatureEvent value) throws Exception { if(value.getTemperature() >= 26.0){ return true; } return false; } }) .within(Time.seconds(10)); //匹配pattern并select事件,符合条件的发生警告,即输出 DataStream<Alert> patternStream = CEP.pattern(inputEventStream, warningPattern) .select(new PatternSelectFunction<TemperatureEvent, Alert>() { private static final long serialVersionUID = 1L; @Override public Alert select(Map<String, TemperatureEvent> event) throws Exception { return new Alert("Temperature Rise Detected: " + event.get("start").getTemperature() + " on machine name: " + event.get("start").getMachineName()); } }); patternStream.print(); env.execute("CEP on Temperature Sensor"); }}
这个pattern非常简单,只要TemperatureEvent中有超过26度的记录,就发出一条警告。
5.4、总结
Flink CEP作用于DataStream上,定义pattern,即规则,当触发这些规则时,给出警告。
这里有一个更加复杂的例子供参考:
cep-monitoring。
这个例子是根据Flink CEP library来监控数据中心中每个机柜的温度。当在一定的时间内,如果有2个连续的Event中的温度超过设置的阈值时,就产生一条警告;一条警告也许还不是很坏的结果,但是如果我们在同一个机柜上连续看到2条这种警告,这种情况比较严重了。所以根据第一个警告流的输出,通过定义另一个Pattern,以上一步的输出作为第二个pattern的输入,来定义一个“严重”的问题。
下一节我们将简要介绍FlinkML library。
- 精通Apache Flink读书笔记--5
- 精通Apache Flink读书笔记
- 精通Apache Flink读书笔记--1、2
- 精通Apache Flink读书笔记--3、4
- Apache Flink简介
- Apache Flink:详细入门
- Apache Flink:Session Window
- Apache Flink:详细入门
- Apache Flink的特性
- Apache Flink异军突起受欢迎!
- Apache Flink SQL示例
- Why Apache Flink®?
- Apache Flink:详细入门
- Apache Flink:详细入门
- Apache Spark vs Apache Flink
- Apache Spark vs Apache Flink
- Apache Flink vs Apache Spark
- 深入理解Apache Flink核心技术
- MATLAB 私人使用手册之画图
- PopupWindow的创建及点击空白处弹出窗dismiss掉
- LintCode Maximum Number in Mountain
- 插件化
- 匿名对象
- 精通Apache Flink读书笔记--5
- 将字符串中的某一个关键字加粗加红
- Bugly使用
- document.body.scrollXX的用法总结
- Spring框架IOC容器
- ajax 返回JOSN 内的时间字段 jquery转换方式
- SSH学习之Hibernate的第一个程序
- UITableView 自定义的索引View点击放大动画
- POJ