版权声明:本文可以自由转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
作者:forge(作者的Blog:http://forge.iteye.com)
原文:http://forge.iteye.com/blog/571047
Drools Fusion Features
Fusion 是业务逻辑集成平台(Business Logic Integration Platform)一部分,是一个CEP/ESP引擎。
事件,是指在应用程序域中有意义状态改变的记录
一个复杂事件是简单事件的集合。CEP(Complex Event Processing) 处理复杂事件-从事件云中检测并选择事件,根据选择的事件和他们建立的关系发现他们的关系推理出新的数据。
比如,一系列的巨额提款激发可疑交易事件的发生。一个复杂事件的发生是由一系列简单事件的引导形成的。
ESP(Event Stream Processing) 是更实时(real-time)的大量事件处理。例如,根据时间计算实时平均交易量。
一、事件(Events).
从drools视图来看,事件就是一个特殊的事实(fact)。我们说,所有的事件都是事实,但是所有的事实并不都是事件。
1.事件和事实不同特征:
(1)通常不可变:事件是在应用程序域中状态改变的记录,记录已经发生的事情,可变的事件是没有意义的。
应用程序允许给没有赋值的事件属性赋值,但是已经赋值的属性是不应该被改变的。
(2)强时间约束:规则涉及的事件通常需要多个事件的相互关系,尤其某个事件相对其他事件发生的时间点的时间关系。
(3)可管理的生命周期:由于上两个事件特征的原因,事件通常在一个有限的时间窗(Time Window)匹配事件和事实,
这就让引擎管理事件的生命周期成为可能。一个事件插入working memory,引擎有能力发现一个不在匹配
其他事实的事件,然后自动回收它,释放相关的资源。
(4)滑动窗( sliding windows)的使用:由于所有的事件都有关联的时间戳,可以定义并使用滑动窗,让事件在特定的一段时间有效。
2.事件声明:
import some.package.StockTick
declare StockTick
@role( event )
end
将一个已有的StockTick(类)事实生命为事件类型
@role 元数据接受2个值:
fact:默认值,声明为常规的事实
event:声明为事件
由于Drools支持在DRL中声明一个事实,可以去掉StockTick(类),还可以这样:
declare StockTick
@role( event )
datetime : java.util.Date
symbol : String
price : double
end
3.事件元数据
所有的事件都有一个事件元数据集合。当事件插入working memory,事件元数据会自动赋予默认值,我们可以用元数据标签改变他们的默认值。
假设我们有这样的一个实体类:
/**
* A class that represents a voice call in
* a Telecom domain model
*/
public class VoiceCall {
private String originNumber;
private String destinationNumber;
private Date callDateTime;
private long callDuration; // in milliseconds
// constructors, getters and setters
}
(1) @timestamp
默认的,当事件插入working memory,会读取(会话时钟)Session Clock的timestamp赋值给事件的startTimestamp。
@timestamp( <attributeName> ) //以毫秒计算
表示把事件的attributeName属性值赋给事件元数据startTimestamp
declare VoiceCall
@role( event )
@timestamp( callDateTime )
end
(2) @duration
Drools支持时间点(point-in-time)事件和时间间隔(interval-based)事件,一个时间点事件可以理解为时间间隔为0。默认,所有的事件的持续时间(duration)为0.我们可以通过@duration标签来修改事件的duration属性值。
@duration( <attributeName> )// 以毫秒计算。
表示把事件的attributeName属性值赋给事件元数据duration
declare VoiceCall
@role( event )
@timestamp( callDateTime )
end
(3)@expires
这个标签在引擎为Stream模式下才有效。默认的,当一个事件不在匹配(match)事实,激活规则(有活化的规则),事件自动失效(回收).
@expires( <timeOffset> )
显示的定义事件的失效时间。
timeOffset可以是下列形式:
[#d][#h][#m][#s][#[ms]]
[]表示是可选参数
declare VoiceCall
@role( event )
@timestamp( callDateTime )
@duration( callDuration )
@expires( 1h35m )
end
VoiceCall事件在1小时35分钟后过期。
二、会话时钟(Session Clock)
Drools 5提供2种时钟。默认的是基于系统时间的即时时钟(real time clock)。另一个是虚拟(pseudo clock)时钟,可以手动控制。
1. 即时时钟(Real Time Clock)
虽然Drools默认使用即时时钟,我们仍然可以显示的配置:
KnowledgeSessionConfiguration config = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
config.setOption( ClockTypeOption.get("realtime") );
2. 配置虚拟时钟(Pseudo Clock)
KnowledgeSessionConfiguration config = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
config.setOption( ClockTypeOption.get("pseudo") );
怎样控制虚拟时钟的例子:
KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
conf.setOption( ClockTypeOption.get( "pseudo" ) );
StatefulKnowledgeSession session = kbase.newStatefulKnowledgeSession( conf, null );
SessionPseudoClock clock = session.getSessionClock();
// then, 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 );
新的SessionPseudoClock时间值为0毫秒,new Date(0) 为GMT 1970 年 1 月 1 日 00:00:00 ,
clock.advanceTime( 10, TimeUnit.SECONDS ); SessionPseudoClock时间值+10秒。
三、Entry Points
一个entry point是事实(facts)或者事件(events)进入引擎的入口点。
1.声明Entry Points
rule "authorize withdraw"
when
WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream"
CheckingAccount( accountId == $ai, balance > $am )
then
// authorize withdraw
end
只有账户的余额大于提款请求的金额,规则才被激活
2.使用Entry Points
// create your rulebase and your session as usual
StatefulKnowledgeSession session = ...
// get a reference to the entry point
WorkingMemoryEntryPoint atmStream = session.getWorkingMemoryEntryPoint( "ATM Stream" );
// and start inserting your facts into the entry point
atmStream.insert( aWithdrawRequest );
通常情况下,我们不需要硬编码把事实插入到WorkingMemory中,通过Drools pipeline,
将从JMS或者 其他非java对象的数据源 中获得信息(对象)插入到WorkingMemory。
默认的entry point是"DEFAULT",是事实进入WorkingMemory的入口点。
session.insert(fact)实际上是session.getWorkingMemoryEntryPoint("DEFAULT").insert(fact)。
四、时间推理(Temporal Reasoning)
Drools 实现了由Allen定义的13种时间操作运算
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
Temporal Operators ┃ Illustration ┃ Interpretation
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x before y ┃ ___y___ ┃ X发生在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___y___ ┃
x after y ┃ ___x___ ┃ X发生在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x meets y ┃ ___y___ ┃ x结束时y开始
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___y___ ┃
x metby y ┃ ___x___ ┃ y结束时x开始
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x overlaps y ┃ ______y______ ┃ x开始在y之前,结束在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x overlappedby y ┃ ______y______ ┃ x开始在y之后,结束在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x starts y ┃ ______y______ ┃ x和y同时开始,结束在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x startedby y ┃ ___y___ ┃ x和y同时开始,结束在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x during y ┃ ______y______ ┃ x发生在y期间
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x includes y ┃ ___y___ ┃ y发生在x期间
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x finishes y ┃ ______y______ ┃X开始在y之后,同y一起结束
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x finishedby y ┃ ___y___ ┃X开始在y之前,同y一起结束
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x coincides y ┃ y ┃ X和y同时发生
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
1.before
$eventA : EventA( this before[ 3m30s, 4m ] $eventB )
也就是:
3m30s <= $eventB.startTimestamp - $eventA.endTimeStamp <= 4m
before操作符的时间间隔距离是可选的:
如果2个值都被定义了,间隔开始于第1个值,结束于第2个值。
如果只有1个值被定义,间隔开始于该值,结束于正无穷大。
如果没有值定义,间隔开始于1毫秒,结束于正无穷大。
时间间隔距离也可以是负数的,例:
$eventA : EventA( this before[ -3m30s, -2m ] $eventB )
注意:当时间间隔第1个值大于第2个值时,引擎会自动的调换他们。
2.After
$eventA : EventA( this after[ 3m30s, 4m ] $eventB )
也就是:
3m30s <= $eventA.startTimestamp - $eventB.endTimeStamp <= 4m
after操作符的时间间隔距离是可选的:
如果2个值都被定义了,间隔开始于第1个值,结束于第2个值。
如果只有1个值被定义,间隔开始于该值,结束于正无穷大。
如果没有值定义,间隔开始于1毫秒,结束于正无穷大。
时间间隔距离也可以是负数的,例:
$eventA : EventA( this after[ -3m30s, -2m ] $eventB )
注意:当时间间隔第1个值大于第2个值时,引擎会自动的调换他们。
3.meets
$eventA : EventA( this meets $eventB )
也就是:
abs( $eventB.startTimestamp - $eventA.endTimestamp ) == 0
meets操作符有1个可选参数
$eventA : EventA( this meets[ 5s ] $eventB )
也就是:
abs( $eventB.startTimestamp - $eventA.endTimestamp) <= 5s
注意:时间间隔不能为负
4.metby
$eventA : EventA( this metby $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.endTimestamp ) == 0
metby操作符有1个可选参数
$eventA : EventA( this metby[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.endTimestamp) <= 5s
注意:时间间隔不能为负
5.overlaps
$eventA : EventA( this overlaps $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp
overlaps操作符有1个/2个可选参数
$eventA : EventA( this overlaps[ 5s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp &&
0 <= $eventA.endTimestamp - $eventB.startTimestamp <= 5s
$eventA : EventA( this overlaps[ 5s, 10s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp &&
5s <= $eventA.endTimestamp - $eventB.startTimestamp <= 10s
6.overlappedby
eventA : EventA( this overlappedby $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp
overlappedby操作符有1个/2个可选参数
$eventA : EventA( this overlappedby[ 5s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp &&
0 <= $eventB.endTimestamp - $eventA.startTimestamp <= 5s
$eventA : EventA( this overlappedby[ 5s, 10s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp &&
5s <= $eventB.endTimestamp - $eventA.startTimestamp <= 10s
7.starts
$eventA : EventA( this starts $eventB )
也就是:
$eventA.startTimestamp == $eventB.startTimestamp &&
$eventA.endTimestamp < $eventB.endTimestamp
starts操作符有1个可选参数
$eventA : EventA( this starts[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 5s &&
$eventA.endTimestamp < $eventB.endTimestamp
注意:时间间隔不能为负
8.startedby
$eventA : EventA( this startedby $eventB )
也就是:
$eventA.startTimestamp == $eventB.startTimestamp &&
$eventA.endTimestamp > $eventB.endTimestamp
starts操作符有1个可选参数
$eventA : EventA( this starts[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 5s &&
$eventA.endTimestamp > $eventB.endTimestamp
注意:时间间隔不能为负
9.during
$eventA : EventA( this during $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp <= $eventA.endTimestamp < $eventB.endTimestamp
during操作符有1个/2个/4个可选参数
$eventA : EventA( this during[ 5s ] $eventB )
也就是:
0 < $eventA.startTimestamp - $eventB.startTimestamp <= 5s &&
0 < $eventB.endTimestamp - $eventA.endTimestamp <= 5s
$eventA : EventA( this during[ 5s, 10s ] $eventB )
也就是:
5s <= $eventA.startTimestamp - $eventB.startTimestamp <= 10s &&
5s <= $eventB.endTimestamp - $eventA.endTimestamp <= 10s
$eventA : EventA( this during[ 2s, 6s, 4s, 10s ] $eventB )
也就是:
2s <= $eventA.startTimestamp - $eventB.startTimestamp <= 6s &&
4s <= $eventB.endTimestamp - $eventA.endTimestamp <= 10s
10.includes
$eventA : EventA( this includes $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp <= $eventB.endTimestamp < $eventA.endTimestamp
includes操作符有1个/2个/4个可选参数
$eventA : EventA( this includes[ 5s ] $eventB )
也就是:
0 < $eventB.startTimestamp - $eventA.startTimestamp <= 5s &&
0 < $eventA.endTimestamp - $eventB.endTimestamp <= 5s
$eventA : EventA( this includes[ 5s, 10s ] $eventB )
也就是:
5s <= $eventB.startTimestamp - $eventA.startTimestamp <= 10s &&
5s <= $eventA.endTimestamp - $eventB.endTimestamp <= 10s
$eventA : EventA( this includes[ 2s, 6s, 4s, 10s ] $eventB )
也就是:
2s <= $eventB.startTimestamp - $eventA.startTimestamp <= 6s &&
4s <= $eventA.endTimestamp - $eventB.endTimestamp <= 10s
11.finishes
$eventA : EventA( this finishes $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishes操作符有1个可选参数
$eventA : EventA( this finishes[ 5s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 5s
注意:时间间隔不能为负
12.finishedby
$eventA : EventA( this finishedby $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishedby操作符有1个可选参数
$eventA : EventA( this finishedby[ 5s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 5s
注意:时间间隔不能为负
13.coincides
$eventA : EventA( this coincides $eventB )
也就是:
$eventA.startTimestamp ==$eventB.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishedby操作符有1个/2个可选参数
$eventA : EventA( this coincides[15s] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 15s &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 15s
$eventA : EventA( this coincides[15s, 10s] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 15s &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 10s
注意:时间间隔不能为负
五、事件处理模式(Event Processing Modes)
Drools支持2种事件处理模式:云模式(Cloud Mode)和流模式(Stream Mode)
1.云模式(Cloud Mode)
云(Cloud)处理模式是默认的处理方式。
在云模式下,不会区分事实和事件,都看成是事实。
(1)没有时间的概念。尽管事件在插入引擎被赋予了时间戳,也不能判断该事件“多大了”,因为没有“现在”的概念。滑动窗(sliding windows)基于“现在”的概念,所以在云模式下无法使用。
(2)无序的事件云。由于事件无序,没有自动的生命周期管理,需要像正常的事实一样显示的删除事件。
云模式虽然是默认的执行模式,我们也可以配置它:
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( EventProcessingOption.CLOUD );
等同系统属性配置:
drools.eventProcessingMode = cloud
2.流模式(Stream Mode)
当处理事件流的时候需要选择流处理模式。
在流模式下:
(1) 插入到引擎里的事件必须是时间顺序的。
(2) 引擎强制性的和使用的会话时钟session clock同步。
配置流模式:
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( EventProcessingOption.STREAM );
等同配置系统属性:
drools.eventProcessingMode = stream
使用流(STREAM)模式,引擎有时间流和"现在"的概念(通过读取Session Clock的时间戳),
提供了以下3种支持:
(1) 滑动窗的支持
(2) 自动的时间生命周期管理
(3) 使用消极模式(Negative Patterns)自动的规则延迟
3.会话时钟(Session Clock)在流模式(Stream mode)中的作用
在云模式下,会话时钟只有一个作用,就是给插入到working momery 的事件赋予时间戳的值(如果规则没有定义时间戳属性)
在流模式下,会话时钟负责维护当前时间戳,基于当前的时间戳,引擎根据事件的年龄计算所有时间运算,从多种源同步流,安排未来任务等等。
4.流模式(in Stream Mode)中的消极模式(Negative Patterns)
消极模式在流模式和云模式意义是不同的。
在云模式下,所有的事实和事件都是预先知道的,消极模式立即计算执行
//a rule that activates immediately upon matching
rule "Sound the alarm"
when
$f : FireDetected( )
not( SprinklerActivated( ) )
then
// sound the alarm
end
在流模式下,带有时间约束的消极模式可以要求引擎等待一段时间后激活规则。
//a rule that automatically delays activation due to temporal constraints
rule "Sound the alarm"
when
$f : FireDetected( )
not( SprinklerActivated( this after[0s,10s] $f ) )
then
// sound the alarm
end
六. 滑动窗(Sliding Windows)
滑动窗是一种选择事件范围的方式。
滑动窗有2种实现方式:滑动时间窗(Sliding Time Windows),滑动长度窗(Sliding Length Windows)。
注意:滑动窗只有在引擎为(STREAM)流模式下才是可用的
1.滑动时间窗(Sliding Time Windows)
滑动时间窗(允许我们的规则)仅仅匹配发生在最近X时间单元的事件。
例如,如果只关心最近2分钟的股票行情,模式(pattern)可以这样写:
StockTick() over window:time( 2m )
更复杂的例子:
如果最近10分钟从传感器读来的温度高于最大的临界值,发出警报。
rule "Sound the alarm in case 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
2.滑动长度窗(Sliding Length Windows)
滑动长度窗(允许我们的规则)仅仅匹配发生在最近X个的事件。
例如,如果只关心最近10个IBM股票的行情,模式(pattern)可以这样写:
StockTick( company == "IBM" ) over window:length( 10 )
更复杂的例子:
如果最近100次从传感器读来的温度高于最大的临界值,发出警报。
rule "Sound the alarm in case 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
七.Knowledgebase分割(Partitioning)
(注意:这是个实验性的特征,将来可能会发生变化)
传统的Rete算法通常一个线程在执行,也是Drools默认的。但是,该算法本身是可平行(化)的。Drools执行的ReteOO算法通过Knowledgebase分割支持粗粒度的平行执行。
当这个选项可用,Knowledgebase分割成若干个独立的区域,然后用线程池通过这些区域传播事实。该实现保证最多有一个线程在给定的一个区域执行。
要点:这个特征只在LHS平行执行可用,不会改变规则激活行为。
1.平行执行在什么时候有益:
(1)多处理器
(2)knowledge session处理大量的事实
(3)规则的LHS计算量大
(4)knowledge base包含数百或者更多的规则
如果以上条件都符合,这个特征可能会提高knowledgebase计算总性能。
2.配置Knowledgebase分割
//enabling multithread evaluation (partitioning)
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( MultithreadEvaluationOption.YES );
等同系统属性配置:
drools.multithreadEvaluation = <true|false>
3.多线程管理
配置线程池:
//setting the maximum number of threads for rule evaluation to 5
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( MaxThreadsOption.get(5) );
等同系统属性配置:
drools.maxThreads = <-1|1..n>
配置默认值是3,负数值表示在Knowledgebase中有多少分区,引擎就会产生多少线程,使用负数值是很危险的。
八.事件的内存管理
要点:自动的事件内存管理只有在引擎为Stream模式下才有效。
引擎在Steam模式下的好处之一是引擎能够根据时间约束检测出不再匹配任何规则的事件,然后引擎可以没有负作用的安全地回收事件并释放其相关的资源。
引擎有2种基本方式计算一个事件有效期:
(1)显式,使用过期(expiration)策略
(2)隐式,分析事件的时间约束
1.显式的过期偏移量(有效期)
//explicitly defining an expiration offset of 30 minutes for StockTick events
declare StockTick
@expires( 30m )
end
当StockTick事件持续30分钟后,如果没有规则需要该事件,引擎将自动的回收该事件。
2.推知的过期偏移量
//example rule with temporal constraints
rule "correlate orders"
when
$bo : BuyOrderEvent( $id : id )
$ae : AckEvent( id == $id, this after[0,10s] $bo )
then
// do something
end
引擎需要保存BuyOrderEvent 10秒以匹配AckEvent。
BuyOrderEvent隐式的过期偏移量是10秒,AckEvent隐式的过期偏移量是0。
引擎会分析整个Knowledgebase,找出每个事件类型的过期偏移量。当隐式过期偏移量和显式过期偏移量冲突时,引擎会选择2个值中最大的1个
作者:forge(作者的Blog:http://forge.iteye.com)
原文:http://forge.iteye.com/blog/571047
Drools Fusion Features
Fusion 是业务逻辑集成平台(Business Logic Integration Platform)一部分,是一个CEP/ESP引擎。
事件,是指在应用程序域中有意义状态改变的记录
一个复杂事件是简单事件的集合。CEP(Complex Event Processing) 处理复杂事件-从事件云中检测并选择事件,根据选择的事件和他们建立的关系发现他们的关系推理出新的数据。
比如,一系列的巨额提款激发可疑交易事件的发生。一个复杂事件的发生是由一系列简单事件的引导形成的。
ESP(Event Stream Processing) 是更实时(real-time)的大量事件处理。例如,根据时间计算实时平均交易量。
一、事件(Events).
从drools视图来看,事件就是一个特殊的事实(fact)。我们说,所有的事件都是事实,但是所有的事实并不都是事件。
1.事件和事实不同特征:
(1)通常不可变:事件是在应用程序域中状态改变的记录,记录已经发生的事情,可变的事件是没有意义的。
应用程序允许给没有赋值的事件属性赋值,但是已经赋值的属性是不应该被改变的。
(2)强时间约束:规则涉及的事件通常需要多个事件的相互关系,尤其某个事件相对其他事件发生的时间点的时间关系。
(3)可管理的生命周期:由于上两个事件特征的原因,事件通常在一个有限的时间窗(Time Window)匹配事件和事实,
这就让引擎管理事件的生命周期成为可能。一个事件插入working memory,引擎有能力发现一个不在匹配
其他事实的事件,然后自动回收它,释放相关的资源。
(4)滑动窗( sliding windows)的使用:由于所有的事件都有关联的时间戳,可以定义并使用滑动窗,让事件在特定的一段时间有效。
2.事件声明:
import some.package.StockTick
declare StockTick
@role( event )
end
将一个已有的StockTick(类)事实生命为事件类型
@role 元数据接受2个值:
fact:默认值,声明为常规的事实
event:声明为事件
由于Drools支持在DRL中声明一个事实,可以去掉StockTick(类),还可以这样:
declare StockTick
@role( event )
datetime : java.util.Date
symbol : String
price : double
end
3.事件元数据
所有的事件都有一个事件元数据集合。当事件插入working memory,事件元数据会自动赋予默认值,我们可以用元数据标签改变他们的默认值。
假设我们有这样的一个实体类:
/**
* A class that represents a voice call in
* a Telecom domain model
*/
public class VoiceCall {
private String originNumber;
private String destinationNumber;
private Date callDateTime;
private long callDuration; // in milliseconds
// constructors, getters and setters
}
(1) @timestamp
默认的,当事件插入working memory,会读取(会话时钟)Session Clock的timestamp赋值给事件的startTimestamp。
@timestamp( <attributeName> ) //以毫秒计算
表示把事件的attributeName属性值赋给事件元数据startTimestamp
declare VoiceCall
@role( event )
@timestamp( callDateTime )
end
(2) @duration
Drools支持时间点(point-in-time)事件和时间间隔(interval-based)事件,一个时间点事件可以理解为时间间隔为0。默认,所有的事件的持续时间(duration)为0.我们可以通过@duration标签来修改事件的duration属性值。
@duration( <attributeName> )// 以毫秒计算。
表示把事件的attributeName属性值赋给事件元数据duration
declare VoiceCall
@role( event )
@timestamp( callDateTime )
end
(3)@expires
这个标签在引擎为Stream模式下才有效。默认的,当一个事件不在匹配(match)事实,激活规则(有活化的规则),事件自动失效(回收).
@expires( <timeOffset> )
显示的定义事件的失效时间。
timeOffset可以是下列形式:
[#d][#h][#m][#s][#[ms]]
[]表示是可选参数
declare VoiceCall
@role( event )
@timestamp( callDateTime )
@duration( callDuration )
@expires( 1h35m )
end
VoiceCall事件在1小时35分钟后过期。
二、会话时钟(Session Clock)
Drools 5提供2种时钟。默认的是基于系统时间的即时时钟(real time clock)。另一个是虚拟(pseudo clock)时钟,可以手动控制。
1. 即时时钟(Real Time Clock)
虽然Drools默认使用即时时钟,我们仍然可以显示的配置:
KnowledgeSessionConfiguration config = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
config.setOption( ClockTypeOption.get("realtime") );
2. 配置虚拟时钟(Pseudo Clock)
KnowledgeSessionConfiguration config = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
config.setOption( ClockTypeOption.get("pseudo") );
怎样控制虚拟时钟的例子:
KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
conf.setOption( ClockTypeOption.get( "pseudo" ) );
StatefulKnowledgeSession session = kbase.newStatefulKnowledgeSession( conf, null );
SessionPseudoClock clock = session.getSessionClock();
// then, 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 );
新的SessionPseudoClock时间值为0毫秒,new Date(0) 为GMT 1970 年 1 月 1 日 00:00:00 ,
clock.advanceTime( 10, TimeUnit.SECONDS ); SessionPseudoClock时间值+10秒。
三、Entry Points
一个entry point是事实(facts)或者事件(events)进入引擎的入口点。
1.声明Entry Points
rule "authorize withdraw"
when
WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream"
CheckingAccount( accountId == $ai, balance > $am )
then
// authorize withdraw
end
只有账户的余额大于提款请求的金额,规则才被激活
2.使用Entry Points
// create your rulebase and your session as usual
StatefulKnowledgeSession session = ...
// get a reference to the entry point
WorkingMemoryEntryPoint atmStream = session.getWorkingMemoryEntryPoint( "ATM Stream" );
// and start inserting your facts into the entry point
atmStream.insert( aWithdrawRequest );
通常情况下,我们不需要硬编码把事实插入到WorkingMemory中,通过Drools pipeline,
将从JMS或者 其他非java对象的数据源 中获得信息(对象)插入到WorkingMemory。
默认的entry point是"DEFAULT",是事实进入WorkingMemory的入口点。
session.insert(fact)实际上是session.getWorkingMemoryEntryPoint("DEFAULT").insert(fact)。
四、时间推理(Temporal Reasoning)
Drools 实现了由Allen定义的13种时间操作运算
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
Temporal Operators ┃ Illustration ┃ Interpretation
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x before y ┃ ___y___ ┃ X发生在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___y___ ┃
x after y ┃ ___x___ ┃ X发生在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x meets y ┃ ___y___ ┃ x结束时y开始
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___y___ ┃
x metby y ┃ ___x___ ┃ y结束时x开始
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x overlaps y ┃ ______y______ ┃ x开始在y之前,结束在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x overlappedby y ┃ ______y______ ┃ x开始在y之后,结束在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x starts y ┃ ______y______ ┃ x和y同时开始,结束在y之前
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x startedby y ┃ ___y___ ┃ x和y同时开始,结束在y之后
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x during y ┃ ______y______ ┃ x发生在y期间
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x includes y ┃ ___y___ ┃ y发生在x期间
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ___x___ ┃
x finishes y ┃ ______y______ ┃X开始在y之后,同y一起结束
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x finishedby y ┃ ___y___ ┃X开始在y之前,同y一起结束
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
┃ ______x______ ┃
x coincides y ┃ y ┃ X和y同时发生
━━━━━━━━━━┃━━━━━━━━━━━━━━┃━━━━━━━━━━━━━
1.before
$eventA : EventA( this before[ 3m30s, 4m ] $eventB )
也就是:
3m30s <= $eventB.startTimestamp - $eventA.endTimeStamp <= 4m
before操作符的时间间隔距离是可选的:
如果2个值都被定义了,间隔开始于第1个值,结束于第2个值。
如果只有1个值被定义,间隔开始于该值,结束于正无穷大。
如果没有值定义,间隔开始于1毫秒,结束于正无穷大。
时间间隔距离也可以是负数的,例:
$eventA : EventA( this before[ -3m30s, -2m ] $eventB )
注意:当时间间隔第1个值大于第2个值时,引擎会自动的调换他们。
2.After
$eventA : EventA( this after[ 3m30s, 4m ] $eventB )
也就是:
3m30s <= $eventA.startTimestamp - $eventB.endTimeStamp <= 4m
after操作符的时间间隔距离是可选的:
如果2个值都被定义了,间隔开始于第1个值,结束于第2个值。
如果只有1个值被定义,间隔开始于该值,结束于正无穷大。
如果没有值定义,间隔开始于1毫秒,结束于正无穷大。
时间间隔距离也可以是负数的,例:
$eventA : EventA( this after[ -3m30s, -2m ] $eventB )
注意:当时间间隔第1个值大于第2个值时,引擎会自动的调换他们。
3.meets
$eventA : EventA( this meets $eventB )
也就是:
abs( $eventB.startTimestamp - $eventA.endTimestamp ) == 0
meets操作符有1个可选参数
$eventA : EventA( this meets[ 5s ] $eventB )
也就是:
abs( $eventB.startTimestamp - $eventA.endTimestamp) <= 5s
注意:时间间隔不能为负
4.metby
$eventA : EventA( this metby $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.endTimestamp ) == 0
metby操作符有1个可选参数
$eventA : EventA( this metby[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.endTimestamp) <= 5s
注意:时间间隔不能为负
5.overlaps
$eventA : EventA( this overlaps $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp
overlaps操作符有1个/2个可选参数
$eventA : EventA( this overlaps[ 5s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp &&
0 <= $eventA.endTimestamp - $eventB.startTimestamp <= 5s
$eventA : EventA( this overlaps[ 5s, 10s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp &&
5s <= $eventA.endTimestamp - $eventB.startTimestamp <= 10s
6.overlappedby
eventA : EventA( this overlappedby $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp
overlappedby操作符有1个/2个可选参数
$eventA : EventA( this overlappedby[ 5s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp &&
0 <= $eventB.endTimestamp - $eventA.startTimestamp <= 5s
$eventA : EventA( this overlappedby[ 5s, 10s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp &&
5s <= $eventB.endTimestamp - $eventA.startTimestamp <= 10s
7.starts
$eventA : EventA( this starts $eventB )
也就是:
$eventA.startTimestamp == $eventB.startTimestamp &&
$eventA.endTimestamp < $eventB.endTimestamp
starts操作符有1个可选参数
$eventA : EventA( this starts[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 5s &&
$eventA.endTimestamp < $eventB.endTimestamp
注意:时间间隔不能为负
8.startedby
$eventA : EventA( this startedby $eventB )
也就是:
$eventA.startTimestamp == $eventB.startTimestamp &&
$eventA.endTimestamp > $eventB.endTimestamp
starts操作符有1个可选参数
$eventA : EventA( this starts[ 5s ] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 5s &&
$eventA.endTimestamp > $eventB.endTimestamp
注意:时间间隔不能为负
9.during
$eventA : EventA( this during $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp <= $eventA.endTimestamp < $eventB.endTimestamp
during操作符有1个/2个/4个可选参数
$eventA : EventA( this during[ 5s ] $eventB )
也就是:
0 < $eventA.startTimestamp - $eventB.startTimestamp <= 5s &&
0 < $eventB.endTimestamp - $eventA.endTimestamp <= 5s
$eventA : EventA( this during[ 5s, 10s ] $eventB )
也就是:
5s <= $eventA.startTimestamp - $eventB.startTimestamp <= 10s &&
5s <= $eventB.endTimestamp - $eventA.endTimestamp <= 10s
$eventA : EventA( this during[ 2s, 6s, 4s, 10s ] $eventB )
也就是:
2s <= $eventA.startTimestamp - $eventB.startTimestamp <= 6s &&
4s <= $eventB.endTimestamp - $eventA.endTimestamp <= 10s
10.includes
$eventA : EventA( this includes $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp <= $eventB.endTimestamp < $eventA.endTimestamp
includes操作符有1个/2个/4个可选参数
$eventA : EventA( this includes[ 5s ] $eventB )
也就是:
0 < $eventB.startTimestamp - $eventA.startTimestamp <= 5s &&
0 < $eventA.endTimestamp - $eventB.endTimestamp <= 5s
$eventA : EventA( this includes[ 5s, 10s ] $eventB )
也就是:
5s <= $eventB.startTimestamp - $eventA.startTimestamp <= 10s &&
5s <= $eventA.endTimestamp - $eventB.endTimestamp <= 10s
$eventA : EventA( this includes[ 2s, 6s, 4s, 10s ] $eventB )
也就是:
2s <= $eventB.startTimestamp - $eventA.startTimestamp <= 6s &&
4s <= $eventA.endTimestamp - $eventB.endTimestamp <= 10s
11.finishes
$eventA : EventA( this finishes $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishes操作符有1个可选参数
$eventA : EventA( this finishes[ 5s ] $eventB )
也就是:
$eventB.startTimestamp < $eventA.startTimestamp &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 5s
注意:时间间隔不能为负
12.finishedby
$eventA : EventA( this finishedby $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishedby操作符有1个可选参数
$eventA : EventA( this finishedby[ 5s ] $eventB )
也就是:
$eventA.startTimestamp < $eventB.startTimestamp &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 5s
注意:时间间隔不能为负
13.coincides
$eventA : EventA( this coincides $eventB )
也就是:
$eventA.startTimestamp ==$eventB.startTimestamp &&
$eventA.endTimestamp == $eventB.endTimestamp
finishedby操作符有1个/2个可选参数
$eventA : EventA( this coincides[15s] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 15s &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 15s
$eventA : EventA( this coincides[15s, 10s] $eventB )
也就是:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 15s &&
abs( $eventA.endTimestamp - $eventB.endTimestamp ) <= 10s
注意:时间间隔不能为负
五、事件处理模式(Event Processing Modes)
Drools支持2种事件处理模式:云模式(Cloud Mode)和流模式(Stream Mode)
1.云模式(Cloud Mode)
云(Cloud)处理模式是默认的处理方式。
在云模式下,不会区分事实和事件,都看成是事实。
(1)没有时间的概念。尽管事件在插入引擎被赋予了时间戳,也不能判断该事件“多大了”,因为没有“现在”的概念。滑动窗(sliding windows)基于“现在”的概念,所以在云模式下无法使用。
(2)无序的事件云。由于事件无序,没有自动的生命周期管理,需要像正常的事实一样显示的删除事件。
云模式虽然是默认的执行模式,我们也可以配置它:
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( EventProcessingOption.CLOUD );
等同系统属性配置:
drools.eventProcessingMode = cloud
2.流模式(Stream Mode)
当处理事件流的时候需要选择流处理模式。
在流模式下:
(1) 插入到引擎里的事件必须是时间顺序的。
(2) 引擎强制性的和使用的会话时钟session clock同步。
配置流模式:
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( EventProcessingOption.STREAM );
等同配置系统属性:
drools.eventProcessingMode = stream
使用流(STREAM)模式,引擎有时间流和"现在"的概念(通过读取Session Clock的时间戳),
提供了以下3种支持:
(1) 滑动窗的支持
(2) 自动的时间生命周期管理
(3) 使用消极模式(Negative Patterns)自动的规则延迟
3.会话时钟(Session Clock)在流模式(Stream mode)中的作用
在云模式下,会话时钟只有一个作用,就是给插入到working momery 的事件赋予时间戳的值(如果规则没有定义时间戳属性)
在流模式下,会话时钟负责维护当前时间戳,基于当前的时间戳,引擎根据事件的年龄计算所有时间运算,从多种源同步流,安排未来任务等等。
4.流模式(in Stream Mode)中的消极模式(Negative Patterns)
消极模式在流模式和云模式意义是不同的。
在云模式下,所有的事实和事件都是预先知道的,消极模式立即计算执行
//a rule that activates immediately upon matching
rule "Sound the alarm"
when
$f : FireDetected( )
not( SprinklerActivated( ) )
then
// sound the alarm
end
在流模式下,带有时间约束的消极模式可以要求引擎等待一段时间后激活规则。
//a rule that automatically delays activation due to temporal constraints
rule "Sound the alarm"
when
$f : FireDetected( )
not( SprinklerActivated( this after[0s,10s] $f ) )
then
// sound the alarm
end
六. 滑动窗(Sliding Windows)
滑动窗是一种选择事件范围的方式。
滑动窗有2种实现方式:滑动时间窗(Sliding Time Windows),滑动长度窗(Sliding Length Windows)。
注意:滑动窗只有在引擎为(STREAM)流模式下才是可用的
1.滑动时间窗(Sliding Time Windows)
滑动时间窗(允许我们的规则)仅仅匹配发生在最近X时间单元的事件。
例如,如果只关心最近2分钟的股票行情,模式(pattern)可以这样写:
StockTick() over window:time( 2m )
更复杂的例子:
如果最近10分钟从传感器读来的温度高于最大的临界值,发出警报。
rule "Sound the alarm in case 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
2.滑动长度窗(Sliding Length Windows)
滑动长度窗(允许我们的规则)仅仅匹配发生在最近X个的事件。
例如,如果只关心最近10个IBM股票的行情,模式(pattern)可以这样写:
StockTick( company == "IBM" ) over window:length( 10 )
更复杂的例子:
如果最近100次从传感器读来的温度高于最大的临界值,发出警报。
rule "Sound the alarm in case 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
七.Knowledgebase分割(Partitioning)
(注意:这是个实验性的特征,将来可能会发生变化)
传统的Rete算法通常一个线程在执行,也是Drools默认的。但是,该算法本身是可平行(化)的。Drools执行的ReteOO算法通过Knowledgebase分割支持粗粒度的平行执行。
当这个选项可用,Knowledgebase分割成若干个独立的区域,然后用线程池通过这些区域传播事实。该实现保证最多有一个线程在给定的一个区域执行。
要点:这个特征只在LHS平行执行可用,不会改变规则激活行为。
1.平行执行在什么时候有益:
(1)多处理器
(2)knowledge session处理大量的事实
(3)规则的LHS计算量大
(4)knowledge base包含数百或者更多的规则
如果以上条件都符合,这个特征可能会提高knowledgebase计算总性能。
2.配置Knowledgebase分割
//enabling multithread evaluation (partitioning)
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( MultithreadEvaluationOption.YES );
等同系统属性配置:
drools.multithreadEvaluation = <true|false>
3.多线程管理
配置线程池:
//setting the maximum number of threads for rule evaluation to 5
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( MaxThreadsOption.get(5) );
等同系统属性配置:
drools.maxThreads = <-1|1..n>
配置默认值是3,负数值表示在Knowledgebase中有多少分区,引擎就会产生多少线程,使用负数值是很危险的。
八.事件的内存管理
要点:自动的事件内存管理只有在引擎为Stream模式下才有效。
引擎在Steam模式下的好处之一是引擎能够根据时间约束检测出不再匹配任何规则的事件,然后引擎可以没有负作用的安全地回收事件并释放其相关的资源。
引擎有2种基本方式计算一个事件有效期:
(1)显式,使用过期(expiration)策略
(2)隐式,分析事件的时间约束
1.显式的过期偏移量(有效期)
//explicitly defining an expiration offset of 30 minutes for StockTick events
declare StockTick
@expires( 30m )
end
当StockTick事件持续30分钟后,如果没有规则需要该事件,引擎将自动的回收该事件。
2.推知的过期偏移量
//example rule with temporal constraints
rule "correlate orders"
when
$bo : BuyOrderEvent( $id : id )
$ae : AckEvent( id == $id, this after[0,10s] $bo )
then
// do something
end
引擎需要保存BuyOrderEvent 10秒以匹配AckEvent。
BuyOrderEvent隐式的过期偏移量是10秒,AckEvent隐式的过期偏移量是0。
引擎会分析整个Knowledgebase,找出每个事件类型的过期偏移量。当隐式过期偏移量和显式过期偏移量冲突时,引擎会选择2个值中最大的1个
相关推荐
数学建模学习资料 神经网络算法 参考资料-Matlab 共26页.pptx
happybirthday2 升级版生日祝福密码0000(7).zip
本项目是一个基于SSM框架的税务门户网站实现,结合了Vue技术,旨在提供一个全面的税务信息管理平台。该项目主要功能包括税务信息查询、税务申报、税务政策浏览及用户管理等多个模块。通过这些功能,用户可以方便地查询和管理税务相关的各类信息,同时也能及时了解最新的税务政策和规定。 项目采用SSM框架,即Spring、Spring MVC和MyBatis,这三者的结合为项目提供了强大的后端支持,确保了数据的安全性和系统的稳定性。前端则采用Vue.js框架,以其高效的数据绑定和组件化开发模式,提升了用户界面的响应速度和用户体验。 开发此项目的目的不仅是为了满足计算机相关专业学生在毕业设计中的实际需求,更是为了帮助Java学习者通过实战练习,深入理解并掌握SSM框架的应用,从而在实际工作中能够更好地运用这些技术。
php7.4.33镜像7z压缩包
本项目是一个基于Java的珠宝购物网站系统,采用SSM框架进行开发,旨在为计算机相关专业学生提供一个实践平台,同时也适合Java学习者进行实战练习。项目的核心功能涵盖商品展示、用户注册登录、购物车管理、订单处理和支付系统等。通过这一系统,用户可以浏览各类珠宝商品,包括详细的商品描述、高清图片和价格信息,同时能够方便地添加商品至购物车,并进行结算和支付操作。 在技术实现方面,项目运用了Spring、Spring MVC和MyBatis三大框架,确保系统的稳定性和扩展性。Spring负责业务逻辑层,提供依赖注入和面向切面编程的支持;Spring MVC则处理Web层的请求和响应,实现MVC设计模式;MyBatis作为持久层框架,简化了数据库操作。 此外,项目采用JSP技术进行前端页面展示,结合HTML、CSS和JavaScript等技术,为用户提供友好的交互界面。
基于java的高校大学生党建系统设计与实现.docx
本项目是一个基于Python-Django框架开发的疫情数据可视化分析系统,旨在为计算机相关专业的学生提供一个实践平台,同时也适用于需要进行项目实战练习的同学。项目集成了疫情数据的收集、处理、分析和可视化功能,为用户提供了一个直观、高效的数据分析环境。 在功能方面,系统能够自动抓取最新的疫情数据,包括确诊、疑似、治愈和死亡人数等关键指标。数据处理模块则负责清洗和整理这些数据,以确保分析的准确性。分析模块采用了多种统计方法和机器学习算法,以揭示疫情的发展趋势和潜在模式。可视化模块则通过图表和地图等形式,直观地展示了分析结果,便于用户理解和分享。 项目的开发框架选择了Django,这是一个高级Python Web框架,它鼓励快速开发和清晰、务实的设计。Django的强大功能和灵活性,使得项目能够快速响应需求变化,同时保证了系统的稳定性和安全性。
果树领养计划.docx
环境说明:开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat 开发软件:eclipse/myeclipse/idea Maven包:Maven 浏览器:谷歌浏览器。 项目均可完美运行 基于Java的云平台信息安全攻防实训平台提供了以下核心功能: 1. **实训课程与项目**:平台提供了丰富多样的实训课程和项目,覆盖网络安全基础知识、漏洞挖掘与利用、渗透测试技术、安全防护策略等多个领域。 2. **在线学习模块**:学员可以通过在线学习模块观看教学视频、阅读文档资料,系统地学习信息安全知识。 3. **虚拟实验室环境**:平台提供虚拟实验室环境,学员可以在模拟的真实网络场景中进行攻防演练,包括漏洞扫描、攻击测试和防御措施的学习。 4. **教学管理功能**:教师可以创建和管理课程内容,制定教学计划,布置实训作业和考试任务。 5. **监控和统计功能**:教师可以实时了解学员的学习进度、实践操作情况和考试成绩,进行有针对性的指导和辅导。 6. **平台管理功能**:管理员负责用户管理、资源分配、系统安全维护等,确保平台稳定运行和实训环境的安全性。 7. **实时监控和评估**:系统具备实时监控和评估功能,能够及时反馈学生的操作情况和学习效果。 8. **用户认证和授权机制**:平台采用了严格的用户认证和授权机制,确保数据的安全性和保密性。 这些功能共同构建了一个功能丰富、操作便捷的实训环境,旨在提升学员的信息安全技能,为信息安全领域的发展输送专业人才。
基于GrampusFramework的轻量级单体RBAC权限管理系统
内容概要:本文档全面整理了软考(中级-软件设计师)的关键知识点,涵盖了计算复杂度、网络协议、数据结构、编程语言、数据库理论、软件测试、编译原理、设计模式、安全协议等多个方面的内容。具体涉及环路复杂度计算、SSH协议、数据字典与数据流图、对象的状态与数字签名、编程语言分类、海明码、著作权法、物理层与数据链路层设备、归纳法与演绎法、模块间耦合、能力成熟度模型集成、配置管理与风险管理、数据库关系范式、内存技术、计算机网络端口、路由协议、排序算法、中间代码、软件测试类型、编译器各阶段任务、设计模式、耦合与内聚、计算机病毒种类等。 适用人群:备考软考(中级-软件设计师)的技术人员,尤其是有一定工作经验但希望进一步提升自身技能和知识的IT从业人员。 使用场景及目标:帮助考生系统梳理考试重点,理解和掌握软件设计师应具备的专业知识和技术。适合考前复习和巩固基础知识。文档还可以作为参考资料,用于日常工作中遇到相关问题时查阅。 其他说明:本文档不仅提供了丰富的知识点,还附带了一些关键术语的定义和详细的解释,确保读者能够全面理解相关内容。建议在复习过程中结合实际案例进行练习,加深理解。
数学建模学习资料 神经网络算法 Hopfield网络 共58页.pptx
工作寻(JobHunter)是一款招聘信息整合的网站,目前固定的模板有拉勾网,中华英才网,前程无忧。工作寻可以在线通过关
本项目是基于Python实现的协同过滤音乐推荐系统,旨在为计算机相关专业学生提供一个完整的毕设实战案例。项目以协同过滤算法为核心,通过分析用户历史行为数据,为用户推荐符合其兴趣偏好的音乐。 主要功能包括用户兴趣建模、音乐推荐生成以及用户反馈机制。系统能够实时捕捉用户听歌行为,动态更新用户兴趣模型,从而更精准地推送个性化音乐推荐。同时,系统设计了友好的用户界面,使用户能够方便地获取推荐音乐,并通过反馈机制不断完善推荐算法。 在技术框架方面,项目采用了Python编程语言,借助scikit-learn等机器学习库实现协同过滤算法,并结合Flask框架搭建了Web服务,确保了系统的性能和稳定性。此项目的开发,不仅能够帮助学生深入理解协同过滤算法及音乐推荐系统的工作原理,还能提升其软件开发和项目管理能力。
微型餐饮补正备案材料通知书.docx
食品生产许可质量跟踪监督建议书.docx
基于django的音乐推荐系统.zip
如果让某人推荐Python技术书,请让他看这个列表很棒的 Python 书籍如果让某人推荐Python技术书,请让他看这个列表前言好的技术书籍可以帮助我们快速成长,大部分人新生儿或者少部分受益于经典的技术书籍。在「Python开发者」微信公号后台,我们经常能收到帮忙推荐书籍的消息。此类问题在@Python开发者微博和伯乐在线的Python小组讨论中也绝非耳熟能详。 7月3日,伯乐在线在「Python开发者」微信公号发起了一个讨论(注PC端无法看到大家的评论,需要关注微信公号后,从微信公号才可以看到),通过这个讨论话题,在评论中分享对自己有帮助的大量Python技术书籍。 (Python开发者)入门《Head First Python》+入门级+微信49票+豆瓣评分9.5推荐语**66**浅显易懂,编排的顺序特别,有大量插图、对话,感觉枯燥古心通熟易懂,大量の图片,不会觉得枯燥,是一本不错的入门书《集体智慧编程》+入门级+微信123票+豆瓣评分 9.0推荐语**Mèrçurý**以实例具体的方式来展示Python的编程技巧,受益良多《Py
基于java的博客系统设计与实现.docx
建设工程基本建设程序检查表.docx