一、CEP和ESP的概念
CEP:复杂事件处理,是一种新兴的基于事件流的技术,它用于处理事件、从事件流中发现复杂模式。它将系统数据看做不同类型的事件,通过分析事件间的关系,建立不同事件关系序列库,利用过滤、关联、聚合等技术,最终由简单事件产生高级事件或商业流程。
ESP:事件流处理,目的是从事件流中获得有用的事件,进而从中获得有用信息。
CEP/ESP适合的场景包括:监控、犯罪预防、网络攻击、市场趋势分析。
比如示例:
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPRuntime;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
/**
1. 这是一个EventClass对象
*/
public class Apple {
private int id;
private int price;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int getPrice()
{
return price;
}
public void setPrice(int price)
{
this.price = price;
}
}
// 2. 创建一个class实现UpdateListener接口
class AppleListener implements UpdateListener
{
public void update(EventBean[] newEvents, EventBean[] oldEvents)
{
if (newEvents != null)
{
Double avg = (Double) newEvents[0].get("avg(price)");
System.out.println("Apple's average price is " + avg);
}
}
}
调用:
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPRuntime;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EPStatement;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
/**
*/
public class Test {
public static void main(String[] args) {
// 创建一条语句(类似于JDBC里面的Statement的)
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
String product = Apple.class.getName();
// 获取类SQL语句.
String epl = "select avg(price) from " + product + ".win:length_batch(3)";
EPStatement state = admin.createEPL(epl);
// 添加一个监听器
state.addListener(new AppleListener());
// 发送事件
EPRuntime runtime = epService.getEPRuntime();
Apple apple1 = new Apple();
apple1.setId(1);
apple1.setPrice(5);
runtime.sendEvent(apple1); // 发送事件
Apple apple2 = new Apple();
apple2.setId(2);
apple2.setPrice(2);
runtime.sendEvent(apple2); // 发送事件
Apple apple3 = new Apple();
apple3.setId(3);
apple3.setPrice(5);
runtime.sendEvent(apple3); // 发送事件
}
}
输出:4.0
使用Esper的基本步骤:
- 创建一个EventClass
- 创建一个class实现UpdateListener接口
- 创建一个Configuration实例
- 通过EPServiceProviderManager获得EPServiceProvider对象
- 通过EPServiceProder和EPL查询语句,创建EPStatement对象
- 将listener和EPStatement挂钩
- 使用epService.getEPRuntime().sendEvent(event)向引擎发送事件
我们要做的事情就是定义DSL语言,然后让它执行。
关于如何让EPS调用自定义函数?
二、关于EPL能够处理的事件类型
目前EPL能够处理的事件类型:POJO、MAP、ObjectArray、XML
2.1 POJO
Esper要求对每一个私有属性要有getter方法。不必按JavaBean规定的格式,但getter方法是必须的。也支持复杂的数据结构
比如如下的一个POJO对象
import java.util.List;
import java.util.Map;
public class Person
{
String name;
int age;
List<Child> children;
Map<String, Integer> phones;
Address address;
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public List<Child> getChildren()
{
return children;
}
public Map<String, Integer> getPhones()
{
return phones;
}
public Address getAddress()
{
return address;
}
}
class Child
{
String name;
int gender;
// 省略getter方法
}
class Address
{
String road;
String street;
int houseNo;
// 省略getter方法
}
支持这样写类SQL语句:#select age,children,address from Person where name=''张三'
这样得到的children是一个列表,如果不想要所有的,而是想要第二个,那Person需要改一下
import java.util.List;
import java.util.Map;
public class Person
{
String name;
int age;
List<Child> children;
Map<String, Integer> phones;
Address address;
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
// 可以这样写get的方法
public Child getChildren(int index)
{
return children.get(index);
}
public int getPhones(String name)
{
return phones.get(name);
}
public Address getAddress()
{
return address;
}
// Address,Child不变
}
对应的EPL如下:#select children[1],phones('home'),address.road from Person where name='张三'
select 后面的这些列其实就是从POJo中的get方法拿数据的。
Esper支持事件更新,对此Esper需要提供setter方法。示例如下:
import java.util.List;
import java.util.Map;
public class Person
{
String name;
int age;
List<Child> children;
Map<String, Integer> phones;
Address address;
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
public Child getChildren(int index)
{
return children.get(index);
}
// 此方法用于phones属性的更新 、 支持set方法
public void setPhones(String name, Integer number){
phones.put(name, number);
}
public int getPhones(String name)
{
return phones.get(name);
}
public Address getAddress()
{
return address;
}
// Address,Child不变
}
对应的EPL如下:#update Person set phones('home') = 123456789 where name='张三'
2. Map
Esper支持原生Java map结构的事件。相对POJO,Map结构更利于热加载。不需要重启JVM,如果系统对重启比较敏感,建议使用Map来定义事件结构。
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
public class PersonMap
{
public static void main(String[] args)
{
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();
// Person定义
Map<String,Object> person = new HashMap<String,Object>();
person.put("name", String.class);
person.put("age", int.class);
person.put("children", List.class);
person.put("phones", Map.class);
// 注册Person到Esper
admin.getConfiguration().addEventType("Person", person);
}
}
对应的EPL跟POJO没有区别:#select age,children from Person where name='张三'
3. Object Array
对象数组和Map很像,基本没有差别。只是定义方式不一样。
三、关于进程模型
Esper是怎么处理事件的,即Esper的进程模型。
3.1 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数组。#EPL: select name from User
#newEvents[0].get("name") 能得到进入的User事件的name属性值
2. Insert and remove stream
Insert表示进入引擎,remove表示移出引擎。
3. Filter and where-clause
EPL有两种过滤事件的方式:一种是过滤事件进入view,即Filter, 另一种是让事件都进入view,但不触发UpdateListener,即where 子句。
// Apple事件进入Esper,只有amount > 200才进入win.length,并且length长度为5
EPL: select * from Apple(amount > 200).win:length(5)
4. 聚合与分组
类SQL里面的sum与group by 操作类似的。
// 统计进入的5个Apple事件,amount的总数是多少
select sum(amount) from Apple.win:length_batch(5)
四、关于context
4.1 context基本语法
create context context_name partition [by] event_property [and event_property [and ...]] from stream_def
[, event_property [...] from stream_def] [, ...]
说明:context_name: 为context的名字,并且唯一。如果重复,会说明已存在
event_property为事件的属性名,多个属性名之间用and连接,也可以用逗号连接。
参考文档:
http://blog.youkuaiyun.com/luonanqin/article/details/10946329