进程模型
主要解释一下 Esper 是怎么处理事件的,即Esper的进程模型。
1、UpdateListener
UpdaterListener 是 Esper 提供的一个接口,用于监听某个 EPL 在引擎中的运行情况,即事件进入并产生结果后会通知 UpdateListener。接口如下import com.espertech.esper.client.EventBean;
public interface UpdateListener {
public void update(EventBean[] newEvents, EventBean[] oldEvents);
}
接口很简单,就一个 update 方法,其中包括两个 EventBean 数组。EventBean 中有一个最常用的 get 方法,是用来得到 EPL 中某个字段的值。
如下代码:假设 newEvents 长度为1// 能得到进入的User事件的name属性值
EPL:select name from User
newEvents[0].get("name")
// 能得到5秒内进入引擎的User事件数量有多少
EPL:select count(*) from User.win:time(5 sec)
newEvents[0].get("count(*)");
get 方法最常用,此外还有 getUnderlying 等方法,
2、Insert 和 Remove Stream
Insert 表示进入引擎,Remove 表示移出引擎,事件在 Esper 中会因为某类EPL才会经历这两种状态。对应于 UpdateListener 接口就是 newEvents 和 oldEvents,因为处于这两种状态的事件不一定只有一个,所以 newEvents 和 oldEvents 就是数组形式。
举例如下EPL:select * from User
从此图可以看出,随着时间推移,每个进入到引擎的 W 事件都是 newEvents,即 Insert Stream。W 后括号里的值为属性值(500、100、...),可忽略。
有人可能要问了,为什么这里 oldeEvents 什么都没有。那是因为 EPL 的关系。看下面的例子。EPL:select * from User.win:length(5)
注:win:length(5) 是个 view(可以把view理解为一个窗口),这里先暂时理解为 Esper 开放一个空间并最多可同时存放 5 个事件(此空间其实就是大小为 5 的数组)
由图可知,length window 可存放 w1,w2 等事件,在 w6 事件进入之前,每个事件进入都属于 newEvents。
直到 w6 进入后,length window 不能容纳 w1~w6 的事件,必须把 w1 事件移出,即 w1 为 oldEvents。
length window 就像一个队列,每当事件进入队列时,就会触发 updateListener 并告知有新事件进入。当队列满了,再进入一个新事件时,Esper 会触发 UpdateListener 告知有新事件进入并且有旧事件移出,正如上图所示的 w6 和 w1。
实际上这个EPL触发监听器都只能看到 newEvents,看不到 oldEvents。如果想看到 oldEvents,EPL 要改写一下:EPL:select irstream * from User.win:length(5)
默认情况下,Esper 认为你只想让 newEvents 触发监听器,即 istream(insert stream)。如果想让 oldEvents 触发监听器,那么为 rstream(remove stream)。如果两个都想,那么为 irstream。当然这个默认情况是可以配置的,以后会说到这个问题。
3、Filter 过滤、Where 条件查询
EPL 有两种过滤事件的方式,一种是过滤事件进入view,即 Filter。
一种是让事件都进入view,但不触发 UpdateListener,即 where 子句。
Filter:过滤器-- Apple事件进入Esper,只有 amount 大于 200 才能进入 win:length,并且 length 长度为5
EPL:select * from Apple(amount>200).win:length(5)
从图上可以看出,只有amount大于200,Esper才允许Apple事件进入view,并且作为一个 newEvent 触发 UpdateListener。
Where:条件查询-- Apple事件进入Esper 并进入win:length(5),但是只有 amount 大于 200 的才能触发
UpdateListener EPL:select * from Apple.win:length(5) where amount>200
由图上可以看出,Apple事件先进入view,然后才被 where 子句过滤,以至于被过滤掉的事件不会作为 newEvent 触发 UpdateListener
4、Aggregation 聚合、 Grouping 分组
之前说过 EPL 是类 SQL 语法,所以也会有聚合和分组的功能。语法和SQL基本一样,如下代码:-- 统计进入的5个Apple事件,amount的总数是多少
select sum(amount) from Apple.win:length_batch(5)
-- 统计进入的5个Apple事件,amount的总数是多少,并按照price分组
select price, sum(amount) from Apple.win:length_batch(5) group by price
-- 统计进入的5个Apple事件,amount的总数和name,并按照price分组
select price, name, sum(amount) from Apple.win:length_batch(5) group by price
最后一个和前一个的区别在于 name 也在统计的范围内,所以当 name 和 price 都一样的两个事件进入Esper,会有两个一模一样的事件作为 newEvent 触发 UpdaterListener,即 price,name,sum(amount) 都一样。当然要是 group by name, price 的话,就只会有一个事件触发监听器了。