Esper學習之四:Context

Context是Esper裏一個很有意思的概念,要是理解爲上下文,我覺得有點不妥。以我的理解,Context就像一個框,把不同的事件按照框的規則框起來,並且有可能有多個框,而框與框之間不會互相影響。不知道各位在看完這篇文章後是否認同我的觀點,我願洗耳恭聽。


1.Context基本語法

語法結構如下

[plain] view plaincopy
  1. create context context_name partition [by] event_property [and event_property [and ...]] from stream_def   
  2. [, event_property [...] from stream_def] [, ...]  

說明:
context_name爲context的名字,並且唯一。如果重複,會說明已存在。

event_property爲事件的屬性名,多個屬性名之間用and連接,也可以用逗號連接。

stream_def爲事件流的定義,簡單的定義可以是一個事件的名稱,比如之前定義了一個Map結構的事件爲User,那麼這裏就可以寫User。複雜的流定義後面會說到

舉個例子:

[plain] view plaincopy
  1. create context NewUser partition by id and name from User  
  2. // id和name是User的屬性  

如果context包含多個流,例子如下:

[plain] view plaincopy
  1. create context Person partition by sid from Student, tid from Teacher  
  2. // sid是Student的屬性,tid是Teacher的屬性  

多個流一定要注意,每個流的中用於context的屬性的數量要一樣,數據類型也要一致。比如下面這幾個就是錯誤的:

[plain] view plaincopy
  1. create context Person partition by sid from Student, tname from Teacher  
  2. // 錯誤:sid是int,tname是String,數據類型不一致  
  3.   
  4. create context Person partition by sid from Student, tid,tname from Teacher  
  5. // 錯誤:Student有一個屬性,Teacher有兩個屬性,屬性數量不一致  
  6.   
  7. create context Person partition by sid,sname from Student, tname,tid from Teacher  
  8. // 錯誤:sid對應tname,sname對應tid,並且sname和tname是String,sid和tid是int,屬性數量一樣,但是對應的數據類型不一致  

實際上可以對進入context的事件增加過濾條件,不符合條件的就被過濾掉,就像下面這樣:

[plain] view plaincopy
  1. create context Person partition by sid from Student(age > 20)  
  2. // age大於20的Student事件才能建立或者進入context  

       看了這麼多,可能大家只是知道context的一些基本定義方法,但是不知道什麼意思。其實很簡單,partition by後面的屬性,就是作爲context的一個約束,比如說id,如果id相等的則進入同一個context裏,如果id不同,那就新建一個context。好比根據id分組,id相同的會被分到一個組裏,不同的會新建一個組並等待相同的進入。

       如果parition by後面跟着同一個流的兩個屬性,那麼必須兩個屬性值一樣才能進入context。比如說A事件id=1,name=a,那麼會以1和a兩個值建立context,有點像數據庫裏的聯合主鍵。然後B事件id=1,name=b,則又會新建一個context。接着C事件id=1,name=a,那麼會進入A事件建立的context。

       如果partition by後面跟着兩個流的一個屬性,那麼兩個屬性值一樣才能進入context。比如說Student事件sid=1,那麼會新建一個context,然後來了個Teacher事件tid=1,則會進入sid=1的那個context。多個流也一樣,不用關心是什麼事件,只用關心事件的屬性值一樣即可進入同一個context。

要是說了這麼多還是不懂,可以看看下面要講的context自帶屬性也許就能明白一些了。


2. Built-In Context Properties

Context本身自帶一些屬性,最關鍵的是可以查看所創建的context的標識,並幫助我們理解context的語法。

如上所示,name表示context的名稱,這個是不會變的。id是每個context的唯一標識,從0開始。key1和keyN表示context定義時所選擇的屬性的值,1和N表示屬性的位置。例如:

[plain] view plaincopy
  1. EPL: create context Person partition by sid, sname from Student  
  2. // key1爲sid,key2爲sname  

爲了說明對這幾個屬性的應用,我舉了一個比較完整的例子。

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;  
  2. import com.espertech.esper.client.EPRuntime;  
  3. import com.espertech.esper.client.EPServiceProvider;  
  4. import com.espertech.esper.client.EPServiceProviderManager;  
  5. import com.espertech.esper.client.EPStatement;  
  6. import com.espertech.esper.client.EventBean;  
  7. import com.espertech.esper.client.UpdateListener;  
  8.   
  9. class ESB  
  10. {  
  11.   
  12.     private int id;  
  13.     private int price;  
  14.   
  15.     public int getId()  
  16.     {  
  17.         return id;  
  18.     }  
  19.   
  20.     public void setId(int id)  
  21.     {  
  22.         this.id = id;  
  23.     }  
  24.   
  25.     public int getPrice()  
  26.     {  
  27.         return price;  
  28.     }  
  29.   
  30.     public void setPrice(int price)  
  31.     {  
  32.         this.price = price;  
  33.     }  
  34.   
  35. }  
  36.   
  37. class ContextPropertiesListener2 implements UpdateListener  
  38. {  
  39.   
  40.     public void update(EventBean[] newEvents, EventBean[] oldEvents)  
  41.     {  
  42.         if (newEvents != null)  
  43.         {  
  44.             EventBean event = newEvents[0];  
  45.             System.out.println("context.name " + event.get("name") + ", context.id " + event.get("id") + ", context.key1 " + event.get("key1")  
  46.                     + ", context.key2 " + event.get("key2"));  
  47.         }  
  48.     }  
  49. }  
  50.   
  51. public class ContextPropertiesTest2  
  52. {  
  53.     public static void main(String[] args)  
  54.     {  
  55.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();  
  56.         EPAdministrator admin = epService.getEPAdministrator();  
  57.         EPRuntime runtime = epService.getEPRuntime();  
  58.   
  59.         String esb = ESB.class.getName();  
  60.         // 創建context  
  61.         String epl1 = "create context esbtest partition by id,price from " + esb;  
  62.         // context.id針對不同的esb的id,price建立一個context,如果事件的id和price相同,則context.id也相同,即表明事件進入了同一個context  
  63.         String epl2 = "context esbtest select context.id,context.name,context.key1,context.key2 from " + esb;  
  64.   
  65.         admin.createEPL(epl1);  
  66.         EPStatement state = admin.createEPL(epl2);  
  67.         state.addListener(new ContextPropertiesListener2());  
  68.   
  69.         ESB e1 = new ESB();  
  70.         e1.setId(1);  
  71.         e1.setPrice(20);  
  72.         System.out.println("sendEvent: id=1, price=20");  
  73.         runtime.sendEvent(e1);  
  74.   
  75.   
  76.         ESB e2 = new ESB();  
  77.         e2.setId(2);  
  78.         e2.setPrice(30);  
  79.         System.out.println("sendEvent: id=2, price=30");  
  80.         runtime.sendEvent(e2);  
  81.   
  82.         ESB e3 = new ESB();  
  83.         e3.setId(1);  
  84.         e3.setPrice(20);  
  85.         System.out.println("sendEvent: id=1, price=20");  
  86.         runtime.sendEvent(e3);  
  87.   
  88.         ESB e4 = new ESB();  
  89.         e4.setId(4);  
  90.         e4.setPrice(20);  
  91.         System.out.println("sendEvent: id=4, price=20");  
  92.         runtime.sendEvent(e4);  
  93.     }  
  94. }  
執行結果:

[plain] view plaincopy
  1. sendEvent: id=1, price=20  
  2. context.name esbtest, context.id 0, context.key1 1, context.key2 20  
  3. sendEvent: id=2, price=30  
  4. context.name esbtest, context.id 1, context.key1 2, context.key2 30  
  5. sendEvent: id=1, price=20  
  6. context.name esbtest, context.id 0, context.key1 1, context.key2 20  
  7. sendEvent: id=4, price=20  
  8. context.name esbtest, context.id 2, context.key1 4, context.key2 20  

      這個例子說得比較明白,針對不同的id和price,都會新建一個context,並context.id會從0開始增加作爲其標識。如果id和price一樣,事件就會進入之前已經存在的context,所以e3這個事件就會和e1一樣存在於context.id=0的context裏面。

      對於epl2這個句子,意思是在esbtest這個context限制下進行事件的計算,不過這個句子很簡單,可以說沒有什麼計算,事件進入後就顯示出來了。實際上寫成什麼樣都可以,但是必須以context xxx開頭(xxx表示context定義時的名字),比如說:

[plain] view plaincopy
  1. // context定義  
  2. create context esbtest2 partition by id from ESB  
  3.   
  4. // 每當5個id相同的ESB事件進入時,統計price的總和  
  5. context esbtest select sum(price) from ESB.win:length_batch(5)  
  6.   
  7. // 根據不同的id,統計3秒內進入的事件的平均price,且price必須大於10  
  8. context esbtest select avg(price) from ESB(price>10).win:time(3 sec)  

       也許你會發現爲什麼我寫的句子都會帶有".win:length"或者".win:time",那是因爲我要計算的都是一堆事件,所以必須用一定條件才能把事件聚集起來。當然並不是一個事件沒法計算,只不過更多情況下計算都是以多個事件爲基礎的。關於這一點,學習到後面就會有更多的接觸。


3. Hash Context

       前面介紹的Context語法是以事件屬性來定義的,Esper提供了以Hash值爲標準定義Context,通俗一點說就是提供事件屬性參與hash值的計算,計算的值再對某個值(這是什麼)是同餘的則進入到同一個context中。詳細語法如下:

[plain] view plaincopy
  1. create context context_name coalesce [by]  
  2. hash_func_name(hash_func_param) from stream_def  
  3. [, hash_func_name(hash_func_param) from stream_def ]  
  4. [, ...]  
  5. granularity granularity_value  
  6. [preallocate]   


a). hash_func_name爲hash函數的名稱,Esper提供了CRC32或者使用Java的hashcode函數來計算hash值,分別爲consistent_hash_crc32和hash_code。你也可以自己定義hash函數,不過這需要配置。

b). hash_func_param爲參與計算的屬性列表,比如之前的sid或者tname什麼的。

c). stream_def就是事件類型,可以一個可以多個。不同於前面的Context語法要求,Hash Context不管有多個少屬性作爲基礎來計算hash值,hash值都只有一個,並且爲int型。所以就不用關心這些屬性的個數以及數據類型了。

d). granularity是必選參數,表示爲最多能創建多少個context

e). granularity_value就是那個用於取餘的“某個值”,因爲Esper爲了防止內存溢出,就想出了取餘這種辦法來限制context創建的數量。也就是說context.id=hash_func_name(hash_func_param)  % granularity_value。

f). preallocate是一個可選參數,如果使用它,那麼Esper會預分配空間來創建granularity_value數量的context。比如說granularity_value爲1024,那麼Esper會預創建1024個context。內存不大的話不建議使用這個參數。

Hash Context同樣可以過濾事件,舉個完整的例子:

[plain] view plaincopy
  1. // 以java的hashcode方法計算sid的值(sid必須大於5),以CRC32算法計算tid的值,然後對10取餘後的值來建立context  
  2. create context HashPerson coalesce by hash_code(sid) from Student(sid>5), consistent_hash_crc32(tid) from Teacher granularity 10  


Hash Context也有Built-In Context Properties,只不過只有context.id和context.name了。用法和前面說的一樣,這裏就不列舉了。

小貼士:

1.如果用於hash計算的屬性比較多,那麼就不建議使用CRC32算法了,因爲他會把這些屬性值先序列化字節數組以後才能計算hash值。hashcode方法相對它能快很多。

2.如果使用preallocate參數,建議granularity_value不要超過1000

3.如果granularity_value超過65536,引擎查找context會比較費勁,進而影響計算速度


4. Category Context

Category Context相對之前的兩類context要簡單許多,也更容易理解。語法說明如下:

[plain] view plaincopy
  1. create context context_name  
  2. group [by] group_expression as category_label  
  3. [, group [by] group_expression as category_label]  
  4. [, ...]  
  5. from stream_def  

       我相信基本上不用我說,大家都能理解。group_expression表示分組策略的表達式,category_label爲策略定義一個名字,一個context可以有多個策略同時存在,但是特殊的是之能有一個stream_def。例如:

[plain] view plaincopy
  1. create context CategoryByTemp  
  2. group temp < 5 as cold, group temp between 5 and 85 as normal, group temp > 85 as large  
  3. from Temperature  


Category Context也有它自帶的屬性。

label指明進入的事件所處的group是什麼。完整例子如下:

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;  
  2. import com.espertech.esper.client.EPRuntime;  
  3. import com.espertech.esper.client.EPServiceProvider;  
  4. import com.espertech.esper.client.EPServiceProviderManager;  
  5. import com.espertech.esper.client.EPStatement;  
  6. import com.espertech.esper.client.EventBean;  
  7. import com.espertech.esper.client.UpdateListener;  
  8.   
  9. class ESB3  
  10. {  
  11.     private int id;  
  12.     private int price;  
  13.   
  14.     public int getId()  
  15.     {  
  16.         return id;  
  17.     }  
  18.   
  19.     public void setId(int id)  
  20.     {  
  21.         this.id = id;  
  22.     }  
  23.   
  24.     public int getPrice()  
  25.     {  
  26.         return price;  
  27.     }  
  28.   
  29.     public void setPrice(int price)  
  30.     {  
  31.         this.price = price;  
  32.     }  
  33. }  
  34.   
  35. class ContextPropertiesListener4 implements UpdateListener  
  36. {  
  37.     public void update(EventBean[] newEvents, EventBean[] oldEvents)  
  38.     {  
  39.         if (newEvents != null)  
  40.         {  
  41.             EventBean event = newEvents[0];  
  42.             System.out.println("context.name " + event.get("name") + ", context.id " + event.get("id") + ", context.label " + event.get("label"));  
  43.         }  
  44.     }  
  45. }  
  46.   
  47. public class ContextPropertiesTest4  
  48. {  
  49.     public static void main(String[] args)  
  50.     {  
  51.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();  
  52.         EPAdministrator admin = epService.getEPAdministrator();  
  53.         EPRuntime runtime = epService.getEPRuntime();  
  54.   
  55.         String esb = ESB3.class.getName();  
  56.         String epl1 = "create context esbtest group by id<0 as low, group by id>0 and id<10 as middle,group by id>10 as high from " + esb;  
  57.         String epl2 = "context esbtest select context.id,context.name,context.label, price from " + esb;  
  58.   
  59.         admin.createEPL(epl1);  
  60.         EPStatement state = admin.createEPL(epl2);  
  61.         state.addListener(new ContextPropertiesListener4());  
  62.   
  63.         ESB3 e1 = new ESB3();  
  64.         e1.setId(1);  
  65.         e1.setPrice(20);  
  66.         System.out.println("sendEvent: id=1, price=20");  
  67.         runtime.sendEvent(e1);  
  68.   
  69.   
  70.         ESB3 e2 = new ESB3();  
  71.         e2.setId(0);  
  72.         e2.setPrice(30);  
  73.         System.out.println("sendEvent: id=0, price=30");  
  74.         runtime.sendEvent(e2);  
  75.   
  76.         ESB3 e3 = new ESB3();  
  77.         e3.setId(11);  
  78.         e3.setPrice(20);  
  79.         System.out.println("sendEvent: id=11, price=20");  
  80.         runtime.sendEvent(e3);  
  81.   
  82.         ESB3 e4 = new ESB3();  
  83.         e4.setId(-1);  
  84.         e4.setPrice(40);  
  85.         System.out.println("sendEvent: id=-1, price=40");  
  86.         runtime.sendEvent(e4);  
  87.     }  
  88. }  
輸出結果爲:

[plain] view plaincopy
  1. sendEvent: id=1, price=20  
  2. context.name esbtest, context.id 1, context.label middle  
  3. sendEvent: id=0, price=30  
  4. sendEvent: id=11, price=20  
  5. context.name esbtest, context.id 2, context.label high  
  6. sendEvent: id=-1, price=40  
  7. context.name esbtest, context.id 0, context.label low  

可以發現,id=0的事件,並沒有觸發監聽器,那是因爲context裏的三個category沒有包含id=0的情況,所以這個事件就被排除掉了。


5. Non-Overlapping Context

這類Context有個特點,是由開始和結束兩個條件構成context。語法如下:

[plain] view plaincopy
  1. create context context_name start start_condition end end_condition  

       這個context有兩個條件做限制,形成一個約束範圍。當開始條件和結束條件都沒被觸發時,引擎會觀察事件的進入是否會觸發開始條件。如果開始條件被觸發了,那麼就新建一個context,並且觀察結束條件是否被觸發。如果結束條件被觸發,那麼context結束,引擎繼續觀察開始條件何時被觸發。所以說這類Context的另一個特點是,要麼context存在並且只有一個,要麼條件都沒被觸發,也就一個context都沒有了。

start_condition和end_condition可以是時間,或者是事件類型。比如說:

[plain] view plaincopy
  1. create context NineToFive start (0, 9, *, *, *) end (0, 17, *, *, *)  
  2. //  9點到17點此context纔可用(以引擎的時間爲準)。如果事件進入的事件不在此範圍內,則不受該context影響  

我列了一個完整的例子,以某類事件開始,以某類事件結束

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;  
  2. import com.espertech.esper.client.EPRuntime;  
  3. import com.espertech.esper.client.EPServiceProvider;  
  4. import com.espertech.esper.client.EPServiceProviderManager;  
  5. import com.espertech.esper.client.EPStatement;  
  6. import com.espertech.esper.client.EventBean;  
  7. import com.espertech.esper.client.UpdateListener;  
  8.   
  9. class StartEvent  
  10. {  
  11. }  
  12.   
  13. class EndEvent  
  14. {  
  15. }  
  16.   
  17. class OtherEvent  
  18. {  
  19.     private int id;  
  20.   
  21.     public int getId()  
  22.     {  
  23.         return id;  
  24.     }  
  25.   
  26.     public void setId(int id)  
  27.     {  
  28.         this.id = id;  
  29.     }  
  30. }  
  31.   
  32. class NoOverLappingContextTest3 implements UpdateListener  
  33. {  
  34.   
  35.     public void update(EventBean[] newEvents, EventBean[] oldEvents)  
  36.     {  
  37.         if (newEvents != null)  
  38.         {  
  39.             EventBean event = newEvents[0];  
  40.             System.out.println("Class:" + event.getUnderlying().getClass().getName() + ", id:" + event.get("id"));  
  41.         }  
  42.     }  
  43. }  
  44.   
  45. public class NoOverLappingContextTest  
  46. {  
  47.     public static void main(String[] args)  
  48.     {  
  49.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();  
  50.         EPAdministrator admin = epService.getEPAdministrator();  
  51.         EPRuntime runtime = epService.getEPRuntime();  
  52.   
  53.         String start = StartEvent.class.getName();  
  54.         String end = EndEvent.class.getName();  
  55.         String other = OtherEvent.class.getName();  
  56.         // 以StartEvent事件作爲開始條件,EndEvent事件作爲結束條件  
  57.         String epl1 = "create context NoOverLapping start " + start + " end " + end;  
  58.         String epl2 = "context NoOverLapping select * from " + other;  
  59.   
  60.         admin.createEPL(epl1);  
  61.         EPStatement state = admin.createEPL(epl2);  
  62.         state.addListener(new NoOverLappingContextTest3());  
  63.   
  64.         StartEvent s = new StartEvent();  
  65.         System.out.println("sendEvent: StartEvent");  
  66.         runtime.sendEvent(s);  
  67.   
  68.         OtherEvent o = new OtherEvent();  
  69.         o.setId(2);  
  70.         System.out.println("sendEvent: OtherEvent");  
  71.         runtime.sendEvent(o);  
  72.   
  73.         EndEvent e = new EndEvent();  
  74.         System.out.println("sendEvent: EndEvent");  
  75.         runtime.sendEvent(e);  
  76.   
  77.         OtherEvent o2 = new OtherEvent();  
  78.         o2.setId(4);  
  79.         System.out.println("sendEvent: OtherEvent");  
  80.         runtime.sendEvent(o2);  
  81.     }  
  82. }  

執行結果:

[plain] view plaincopy
  1. sendEvent: StartEvent  
  2. sendEvent: OtherEvent  
  3. Class:blog.OtherEvent, id:2  
  4. sendEvent: EndEvent  
  5. sendEvent: OtherEvent  

由此可以看出,在NoOverLapping這個Context下監控OtherEvent,必須是在StartEvent被觸發才能監控到,所以在EndEvent發送後,再發送一個OtherEvent是不會觸發Listener的。


6. OverLapping

OverLapping和NoOverLapping一樣都有兩個條件限制,但是區別在於OverLapping的初始條件可以被觸發多次,並且只要被觸發就會新建一個context,但是當終結條件被觸發時,之前建立的所有context都會被銷燬。他的語法也很簡單:

[plain] view plaincopy
  1. create context context_name initiated [by] initiating_condition terminated [by] terminating_condition  

initiating_condition和terminating_condition可以爲事件類型,事件或者別的條件表達式。下面給出了一個完整的例子。

[java] view plaincopy
  1. import com.espertech.esper.client.EPAdministrator;  
  2. import com.espertech.esper.client.EPRuntime;  
  3. import com.espertech.esper.client.EPServiceProvider;  
  4. import com.espertech.esper.client.EPServiceProviderManager;  
  5. import com.espertech.esper.client.EPStatement;  
  6. import com.espertech.esper.client.EventBean;  
  7. import com.espertech.esper.client.UpdateListener;  
  8.   
  9. class InitialEvent{}  
  10.   
  11. class TerminateEvent{}  
  12.   
  13. class SomeEvent  
  14. {  
  15.     private int id;  
  16.   
  17.     public int getId()  
  18.     {  
  19.         return id;  
  20.     }  
  21.   
  22.     public void setId(int id)  
  23.     {  
  24.         this.id = id;  
  25.     }  
  26. }  
  27.   
  28. class OverLappingContextListener implements UpdateListener  
  29. {  
  30.   
  31.     public void update(EventBean[] newEvents, EventBean[] oldEvents)  
  32.     {  
  33.         if (newEvents != null)  
  34.         {  
  35.             EventBean event = newEvents[0];  
  36.             System.out.println("context.id:" + event.get("id") + ", id:" + event.get("id"));  
  37.         }  
  38.     }  
  39. }  
  40.   
  41. class OverLappingContextListener2 implements UpdateListener  
  42. {  
  43.   
  44.     public void update(EventBean[] newEvents, EventBean[] oldEvents)  
  45.     {  
  46.         if (newEvents != null)  
  47.         {  
  48.             EventBean event = newEvents[0];  
  49.             System.out.println("Class:" + event.getUnderlying().getClass().getName() + ", id:" + event.get("id"));  
  50.         }  
  51.     }  
  52. }  
  53.   
  54. public class OverLappingContextTest  
  55. {  
  56.     public static void main(String[] args)  
  57.     {  
  58.         EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();  
  59.         EPAdministrator admin = epService.getEPAdministrator();  
  60.         EPRuntime runtime = epService.getEPRuntime();  
  61.   
  62.         String initial = InitialEvent.class.getName();  
  63.         String terminate = TerminateEvent.class.getName();  
  64.         String some = SomeEvent.class.getName();  
  65.         // 以InitialEvent事件作爲初始事件,TerminateEvent事件作爲終結事件  
  66.         String epl1 = "create context OverLapping initiated " + initial + " terminated " + terminate;  
  67.         String epl2 = "context OverLapping select context.id from " + initial;  
  68.         String epl3 = "context OverLapping select * from " + some;  
  69.   
  70.         admin.createEPL(epl1);  
  71.         EPStatement state = admin.createEPL(epl2);  
  72.         state.addListener(new OverLappingContextListener());  
  73.         EPStatement state1 = admin.createEPL(epl3);  
  74.         state1.addListener(new OverLappingContextListener2());  
  75.   
  76.         InitialEvent i = new InitialEvent();  
  77.         System.out.println("sendEvent: InitialEvent");  
  78.         runtime.sendEvent(i);  
  79.   
  80.         SomeEvent s = new SomeEvent();  
  81.         s.setId(2);  
  82.         System.out.println("sendEvent: SomeEvent");  
  83.         runtime.sendEvent(s);  
  84.   
  85.         InitialEvent i2 = new InitialEvent();  
  86.         System.out.println("sendEvent: InitialEvent");  
  87.         runtime.sendEvent(i2);  
  88.   
  89.         TerminateEvent t = new TerminateEvent();  
  90.         System.out.println("sendEvent: TerminateEvent");  
  91.         runtime.sendEvent(t);  
  92.   
  93.         SomeEvent s2 = new SomeEvent();  
  94.         s2.setId(4);  
  95.         System.out.println("sendEvent: SomeEvent");  
  96.         runtime.sendEvent(s2);  
  97.     }  
  98. }  

執行結果:

[plain] view plaincopy
  1. sendEvent: InitialEvent  
  2. context.id:0, id:0  
  3. sendEvent: SomeEvent  
  4. Class:blog.SomeEvent, id:2  
  5. sendEvent: InitialEvent  
  6. context.id:1, id:1  
  7. context.id:0, id:0  
  8. sendEvent: TerminateEvent  
  9. sendEvent: SomeEvent  

從結果可以看得出來,每發送一個InitialEvent,都會新建一個context,以至於context.id=0和1。並且當發送TerminateEvent後,再發送SomeEvent監聽器也不會被觸發了。

另外,context.id是每一種Context都會有的自帶屬性,而且針對OverLapping,還增加了startTime和endTime兩種屬性,表明context的開始時間和結束時間。


7. Context Condition

Context Condition主要包含Filter,Pattern,Crontab以及Time Period

A). Filter主要就是對屬性值的過濾,比如:

[plain] view plaincopy
  1. create context NewUser partition by id from User(id > 10)  

B). Pattern是複雜事件流的代表,比如說“A事件到達後跟着B事件到達”這是一個完整的Pattern。Pattern是Esper裏面很特別的東西,並且用它描述複雜的事件流是最合適不過的了。這裏暫且不展開說,後面會有專門好幾篇來講解Pattern。

C). Crontab是定時任務,主要用於NoOverLapping,就像前面提到的(0, 9, *, *, *),括號裏的五項代表分,時,天,月,年。關於這個後面也會有講解。

D). Time Period在這裏只有一種表達式,就是after time_period_expression。例如:after 1 minute,after 5 sec。結合Context的例子如下:

[plain] view plaincopy
  1. // 以0秒爲時間初始點,新建一個context,於10秒後開始,1分鐘後結束。下一個context從1分20秒開始  
  2. create context NonOverlap10SecFor1Min start after 10 seconds end after 1 minute  


8. Context Nesting

Context也可以嵌套,意義就是多個Context聯合在一起組成一個大的Context,以滿足複雜的限制需求。語法結構:

[plain] view plaincopy
  1. create context context_name  
  2. context nested_context_name [as] nested_context_definition ,  
  3. context nested_context_name [as] nested_context_definition [, ...]  

舉個例子:

[plain] view plaincopy
  1. create context NineToFiveSegmented  
  2. context NineToFive start (0, 9, *, *, *) end (0, 17, *, *, *),  
  3. context SegmentedByUser partition by userId from User  

應用和普通的Context沒區別,在此就不舉例了。另外針對嵌套Context,其自帶的屬性使用方式會有些變化。比如針對上面這個,若想查看NineToFive的startTime和SegmentedByUser的第一個屬性值,要按照下面這樣寫:

[plain] view plaincopy
  1. context NineToFiveSegmented select  
  2.  context.NineToFive.startTime,  
  3.  context.SegmentedByUser.key1  
  4.  from User  


9. Output When Context Partition Ends

當Context銷燬時,如果你想同時查看此時Context裏的東西,那麼Esper提供了一種辦法來輸出其內容。例如:

[plain] view plaincopy
  1. create context OverLapping initiated InitialEvent terminated TerminateEvent  
  2. context OverLapping select * from User output snapshot when terminated  

那麼當終結事件發送到引擎後,會立刻輸出OverLapping的快照。

如果你想以固定的頻率查看Context的內容,Esper也支持。例如:

[plain] view plaincopy
  1. context OverLapping select * from User output snapshot every 2 minute // 每兩分鐘輸出OverLapping的事件  

關於output表達式,後面也會有詳解。


       以上的內容算是包含了Context的所有方面,可能還有些細節需要各位自己去研讀他的手冊,並且多加練習。Esper的內容之多以至於我說了很多次“後面會專門講解”,不過也確實是因爲內容複雜,所以不得不先跳過這些。在學習到之後的內容以後,再回過頭來理解Context可能會有另一番效果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章