轉載請註明出處:http://blog.csdn.net/luonanqin
之前對Esper所能處理的事件結構進行了概述,並結合了例子進行講解,不清楚的同學請看Esper學習之二:事件類型。今天主要爲大家解釋一下Esper是怎麼處理事件的,即Esper的進程模型。
1.UpdateListener
UpdaterListener是Esper提供的一個接口,用於監聽某個EPL在引擎中的運行情況,即事件進入併產生結果後會通知UpdateListener。接口如下
- package com.espertech.esper.client;
- import com.espertech.esper.client.EventBean;
- public interface UpdateListener
- {
- public void update(EventBean[] newEvents, EventBean[] oldEvents);
- }
接口很簡單,就一個update方法,其中包括兩個EventBean數組,至於兩個參數的含義稍後再說。EventBean中有一個最常用的get方法,是用來得到EPL中某個字段的值。例如:
- EPL:select name from User
- //假設newEvents長度爲一
- newEvents[0].get("name")能得到進入的User事件的name屬性值
- EPL:select count(*) from User.win:time(5 sec)
- //假設newEvents長度爲一
- newEvents[0].get("count(*))能得到5秒內進入引擎的User事件數量有多少
get方法最常用,此外還有getUnderlying等方法,以後會專門寫一篇介紹EventBean的。
2.Insert and Remove Stream
Insert表示進入引擎,Remove表示移出引擎,事件在Esper中會因爲某類EPL纔會經歷這兩種狀態。對應於UpdateListener接口就是newEvents和oldEvents,因爲處於這兩種狀態的事件不一定只有一個,所以newEvents和oldEvents就是數組形式。舉個例子說明下
- EPL:select * from User
從此圖可以看出,隨着時間推移,每個進入到引擎的W事件都是newEvents,即Insert Stream。W後括號裏的值爲屬性值,可忽略。
有人可能要問了,爲什麼這裏oldeEvents什麼都沒有。那是因爲EPL的關係。看下面的例子
- EPL:select * from User.win:length(5)
注:win:length(5)是個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。當然這個默認情況是可以配置的,以後會說到這個問題。
不過對於rstream,在我看來他有個bug,因爲在運行時我發現,oldEvents觸發監聽器時,理論上應該是oldEvents這個參數有值,並且api裏也明確說明了這一點(就算他沒說明,按照常理推斷也應該是oldEvents有值),但是實際上是newEvents有值,oldEvents爲null。雖然說數據沒有錯,但是這個似乎不合常理。
3.Filter and Where-Clause
EPL有兩種過濾事件的方式,一種是過濾事件進入view(可以把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-Clause:
- // Apple事件進入Esper並進入win:length(5),但是隻有amount大於200的才能觸發UpdateListener
- EPL:select * from Apple.win:length(5) where amount>200
由圖上可以看出,Apple事件先進入view,然後才被where子句過濾,以至於被過濾掉的事件不會作爲newEvent觸發UpdateListener
其實單看兩個EPL,就能發現一個過濾是在進入view前,一個過濾是在view後,所以大家在應用的時候要注意。PS:在我寫這段的時候才發現以前認爲這兩種是一樣的效果是錯誤滴- -!
4.Aggregation and 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的話,就只會有一個事件觸發監聽器了。
今天介紹了Esper處理事件時的一些基本知識,對以後的深入學習是必不可少的。明天會再寫一篇,內容是關於context,中文翻譯爲上下文我覺得不妥,我的理解是對EPL的運行增加了外在約束,想繼續學習的請繼續關注。