复杂事件处理(CEP)
CEP方案具有以下关键特征:
- 场景通常处理大量事件,但是只有一小部分事件是相关的。
- 事件通常是不可变的,代表状态变化的记录。
- 规则和查询针对事件运行,并且必须对检测到的事件模式做出反应。
- 相关事件通常具有很强的时间关系。
- 个别事件没有优先级。CEP系统对相关事件的模式及其之间的关系进行优先排序。
- 通常需要对事件进行组合和汇总。
Drools CEP支持:
- 具有适当语义的事件处理
- 事件检测,关联,聚合和组合
- 事件流处理
- 时间约束,用于建模事件之间的时间关系
- 重要事件的滑动窗口
- 会话范围的统一时钟
- CEP用例所需的事件量
- 反应式规则
- 事件输入到Drools引擎的适配器(管道)
CEP event
Eent的特性:
- 不可更改
- 有很强的时间限制
- 管理生命周期
- 可以使用滑动窗口
可以在Java类或者DRL规则文件中将fact声明为event.
event的两种时间类型:
- 时间间隔:基于间隔的事件有一个持续时间,并持续存在于Drools引擎的工作内存中,直到其持续时间失效。
- 时间点:没有持续时间,本质上是基于间隔的事件,持续时间为0。
event配置元数据:
meta | 描述 | 实例 |
---|---|---|
@role | 参数值:fact,event。 默认为fact | declare VoiceCall @role( event ) end |
@timestamp | 默认情况下,drools给每个event创建一个时间戳。可以通过timestamp指定时间戳的属性。 | declare VoiceCall @role( event ) @timestamp( callDateTime ) end |
@duration( ) | 该标签确定Drools引擎中事件的持续时间。事件可以是基于时间间隔的事件或时间点事件。基于间隔的事件具有持续时间,并且持续到Drools引擎的工作内存中,直到其持续时间过去。时间点事件没有持续时间,本质上是持续时间为零的基于间隔的事件。默认情况下,Drools引擎中的每个事件的持续时间为零。 | declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) end |
@expires | 该标签确定事件在Drools引擎的工作内存中到期之前的持续时间。默认情况下,当事件不再匹配并激活任何当前规则时,事件就会过期。 | declare VoiceCall @role( event ) @timestamp( callDateTime ) @duration( callDuration ) @expires( 1h35m ) end |
Drools引擎中的事件处理模式
Drools引擎以云模式或流模式运行。
在云模式下,Drools引擎将事实作为事实进行处理,没有时间限制,与时间无关,并且没有特定的顺序。在流模式下,Drools引擎实时或接近实时地将事实作为具有强大时间约束的事件进行处理。
云模式是Drools引擎的默认操作模式。在云模式下,Drools引擎将事件视为无序云。事件仍然具有时间戳,但是以云模式运行的Drools引擎无法从时间戳中获取相关性,因为云模式会忽略当前时间。此模式使用规则约束来查找匹配的元组以激活和执行规则。
云模式不对事实施加任何其他要求。但是,由于在此模式下的Drools引擎没有时间概念,因此它无法使用诸如滑动窗口或自动生命周期管理之类的临时功能。在云模式下,不再需要事件时,必须显式撤消事件。
在云模式下不强加以下要求:
- 没有时钟同步,因为Drools引擎没有时间概念
- 没有事件的排序,因为Drools引擎将事件作为无序的云处理,Drools引擎根据该云对规则进行匹配
设置方式:
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.KieServices.Factory;
KieBaseConfiguration config = KieServices.Factory.get().newKieBaseConfiguration();
config.setOption(EventProcessingOption.CLOUD);
在流模式下,Drools引擎同步事件流(以便可以按时间顺序处理不同流中的事件),实现时间或长度的滑动窗口,并实现自动生命周期管理。
以下要求适用于流模式:
- 每个流中的事件必须按时间顺序排序。
- 必须存在会话时钟才能同步事件流。
设置方式:
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.KieServices.Factory;
KieBaseConfiguration config = KieServices.Factory.get().newKieBaseConfiguration();
config.setOption(EventProcessingOption.STREAM);
event临时运算符
在流模式下,Drools引擎支持以下临时运算符,用于将事件插入Drools引擎的工作存储器中。云模式不支持时间运算符。
after
该运算符指定当前事件是否在相关事件之后发生。
例子:
#如果$eventA在$eventB完成后3分钟30秒至4分钟之间开始,则以下模式匹配。
$eventA : EventA(this after[3m30s, 4m] $eventB)
参数:
- 如果定义了两个值,则间隔从第一个值(示例中为3分钟30秒)开始,以第二个值(示例中为4分钟)结束。
- 如果仅定义一个值,则间隔从提供的值开始,并无限期地运行,没有结束时间。
- 如果未定义任何值,则间隔从1毫秒开始,并无限期地运行,没有结束时间。
before
该运算符指定当前事件是否在相关事件之前发生。
和after相似。
coincides
此运算符指定两个事件是否同时发生。
判断条件:eventA的开始时间和veventB的开始时间差在指定范围内,且结束时间差也在指定范围内。
coincides的参数制定时间差:
- 如果仅给出一个参数,则该参数用于设置两个事件的开始时间和结束时间的阈值。
- 如果给出了两个参数,则第一个用作开始时间的阈值,第二个用作结束时间的阈值。
例子:
$eventA : EventA(this coincides[15s, 10s] $eventB)
则判定条件为:
abs($eventA.startTimestamp - $eventB.startTimestamp) <= 15s
&&
abs($eventA.endTimestamp - $eventB.endTimestamp) <= 10s
during
该运算符指定当前事件是否在相关事件开始和结束的时间范围内发生。
例子:
$eventA : EventA(this during $eventB)
则计算方式为:
$eventB.startTimestamp < $eventA.startTimestamp <= $eventA.endTimestamp < $eventB.endTimestamp
includes
该运算符指定相关事件是否在当前事件发生的时间范围内发生。
例子:
$eventA : EventA(this includes $eventB)
则计算方法:
$eventA.startTimestamp < $eventB.startTimestamp <= $eventB.endTimestamp < $eventA.endTimestamp
finishes
该运算符指定当前事件是否在相关事件之后开始,但是两个事件同时结束。
例子:
$eventA : EventA(this finishes $eventB)
计算方法:
$eventB.startTimestamp < $eventA.startTimestamp
&&
$eventA.endTimestamp == $eventB.endTimestamp
$eventA : EventA(this finishes[5s] $eventB)
计算方法:
$eventB.startTimestamp < $eventA.startTimestamp
&&
abs($eventA.endTimestamp - $eventB.endTimestamp) <= 5s
finished by
该运算符指定相关事件是否在当前事件之后开始,但是两个事件同时结束。(此运算finishes符的行为与运算符的行为相反。)
例子:
$eventA : EventA(this finishedby $eventB)
计算方法:
$eventA.startTimestamp < $eventB.startTimestamp
&&
$eventA.endTimestamp == $eventB.endTimestamp
$eventA : EventA(this finishedby[5s] $eventB)
计算方法:
$eventA.startTimestamp < $eventB.startTimestamp
&&
abs($eventA.endTimestamp - $eventB.endTimestamp) <= 5s
meets
该运算符指定当前事件是否在相关事件开始的同时结束。
例子:
$eventA : EventA(this meets $eventB)
算法:
abs($eventB.startTimestamp - $eventA.endTimestamp) == 0
$eventA : EventA(this meets[5s] $eventB)
算法:
abs($eventB.startTimestamp - $eventA.endTimestamp) <= 5s
met by
该运算符指定相关事件是否在当前事件开始的同时结束。(此运算meets符的行为与运算符的行为相反。)
overlaps
该运算符指定当前事件是否在关联事件开始之前开始,是否在关联事件发生的时间段内结束。当前事件必须在相关事件的开始时间和结束时间之间结束。
overlapped by
此运算符指定相关事件是否在当前事件开始之前开始,是否在当前事件发生的时间段内结束。相关事件必须在当前事件的开始时间和结束时间之间结束。(此运算overlaps符的行为与运算符的行为相反。)
starts
该运算符指定两个事件是否同时开始,但是当前事件在相关事件结束之前结束。
例子:
$eventA : EventA(this starts $eventB)
算法:
$eventA.startTimestamp == $eventB.startTimestamp
&&
$eventA.endTimestamp < $eventB.endTimestamp
$eventA : EventA(this starts[5s] $eventB)
算法:
abs($eventA.startTimestamp - $eventB.startTimestamp) <= 5s
&&
$eventA.endTimestamp < $eventB.endTimestamp
started by
该运算符指定两个事件是否同时开始,但是相关事件在当前事件结束之前结束。(此运算starts符的行为与运算符的行为相反。)
Drools引擎中的会话时钟实现
Drools引擎支持实时时钟和伪时钟。
实时时钟
实时时钟是Drools引擎中的默认时钟实现,并使用系统时钟来确定时间戳的当前时间。
伪时钟
Drools引擎中的伪时钟实现有助于测试时间规则,并且可以由应用程序控制。要将Drools引擎配置为使用伪时钟,请将KIE会话配置参数设置为pseudo:
import org.kie.api.runtime.conf.ClockTypeOption;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.KieServices.Factory;
KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration();
config.setOption(ClockTypeOption.get("pseudo"));
当插入fact时,手动更改时钟:
import java.util.concurrent.TimeUnit;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.KieServices.Factory;
import org.kie.api.runtime.KieSession;
import org.drools.core.time.SessionPseudoClock;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.api.runtime.conf.ClockTypeOption;
KieSessionConfiguration conf = KieServices.Factory.get().newKieSessionConfiguration();
conf.setOption( ClockTypeOption.get("pseudo"));
KieSession session = kbase.newKieSession(conf, null);
SessionPseudoClock clock = session.getSessionClock();
// While inserting facts, advance the clock as necessary.
FactHandle handle1 = session.insert(tick1);
clock.advanceTime(10, TimeUnit.SECONDS);
FactHandle handle2 = session.insert(tick2);
clock.advanceTime(30, TimeUnit.SECONDS);
FactHandle handle3 = session.insert(tick3);
滑动时间或长度的窗口
在流模式下,Drools引擎可以从时间或长度的指定滑动窗口中处理事件。
实例:
# 从最近2分钟开始处理库存点(滑动时间窗口)
StockPoint() over window:time(2m)
# 处理最后10个存货点(滑动长度窗口)
StockPoint() over window:length(10)
# 滑动时间范围内的平均温度
rule "Sound the alarm if temperature rises above threshold"
when
TemperatureThreshold($max : max)
Number(doubleValue > $max) from accumulate(
SensorReading($temp : temperature) over window:time(10m),
average($temp))
then
// Sound the alarm.
end
# 滑动长度窗口上的平均温度
rule "Sound the alarm if temperature rises above threshold"
when
TemperatureThreshold($max : max)
Number(doubleValue > $max) from accumulate(
SensorReading($temp : temperature) over window:length(100),
average($temp))
then
// Sound the alarm.
end