【Esper技术专题】针对于Esper事件驱动框架的数据模型分析| 8月更文挑战

简介: 【Esper技术专题】针对于Esper事件驱动框架的数据模型分析| 8月更文挑战

数据模型介绍


Esper对事件有特殊的数据结构约定。能处理的事件结构有:POJO,java.util.Map,Object Array。




POJO


对于POJO,Esper要求对每一个私有属性要有getter方法。Esper允许不必按照JavaBean规定的格式,但是getter方法是必须的。又或者可以在配置文件中配置可访问的方法来代替getter。

简单示例如下:


public class Person{
    String name;
    int age;
    public String getName(){
  return name;
    }
    public int getAge(){
  return age;
    }
}
复制代码

Esper同样也能支持复杂的数据类型以及嵌套。




稍微复杂的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;
    }
    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方法
}
复制代码


如上所示,Esper能支持包含了集合类型和嵌套类的POJO,示例的EPL语句如下:

当Person类型的事件中name为luonanqin时,Esper能得到对应的age,children和address。

select age,children,address from Person where name="luonanqin" 
复制代码


如果我不想要所有的child,而是想要第二个。并且我想得到家里的电话号码,那么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;
    }
    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如下:


当Person类型的事件中name为luonanqin时,Esper能得到对应的第二个孩子,家里的电话和家庭住址在哪条路上


select children[1], phones('home'), address.road where Person where name="luonanqin"
复制代码

Esper支持事件的更新,对此Esper要求提供对应的setter方法。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;
  }
  public Child getChildren(int index){
            return children.get(index);
  }
  // 此方法用于phones属性的更新
  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如下:


当Person类型的事件中name为luonanqin时,更新家里的电话

update Person set phones('home') = 123456789 where name="luonanqin"
复制代码

Esper对POJO的支持基本上就是上面所说的,另外他还支持实现了多个接口类或者抽象类的POJO,使用方法和普通的POJO没什么区别,这里就不列举了。



Map


Esper支持原生Java Map结构的事件。相对于POJO来说,Map的结构更利于事件类型的热加载,毕竟不是class,所以不需要重启JVM。所以如果系统对重启比较敏感,建议使用Map来定义事件的结构。Map的结构很简单,主要分为事件定义名和事件属性列表。我们继续拿Person来讲解

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);
  }
}
复制代码

如上所示,Map结构的事件需要将属性名作为key,属性的数据类型作为value保存到Map中,然后再通过Esper的接口注册到Esper。其中addEventType的两个参数分别代表事件定义的名称和所定义的结构。



对应的EPL和POJO的没有区别


当Person类型的事件中name为luonanqin时,Esper能得到对应的age,children。

select age,children from Person where name="luonanqin" 
复制代码


Map对于嵌套类的定义比较特别。如果嵌套的类是POJO,那就如上面所示。如果嵌套的还是Map,那么定义方式就需要改变。我们为Person加上Address,示例如下:

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();
    // Address定义
    Map<String, Object> address = new HashMap<String, Object>();
    address.put("road", String.class);
    address.put("street", String.class);
    address.put("houseNo", int.class);
    // 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.put("address", "Address");
    // 注册Address到Esper
    admin.getConfiguration().addEventType("Address", address);
    // 注册Person到Esper
    admin.getConfiguration().addEventType("Person", person);
  }
}
复制代码



如上所示,有两个关键点:


  1. Person在定义Address属性时,map的value不是Address.class,而是Address字符串,而这就代表引擎里的Address对应的Map结构定义。
  2. 事件定义注册必须是Address先于Person,因为Person用到了Address,而引擎是根据Address注册时用的名字去查找Address定义的,所以如果名字写错,引擎就找不到Address了。


如果Person有多个Address,则以数组方式定义Person的多个Address时,代码又变成下面的样子了。

person.put("addresses", "Address[]");
复制代码



另外对于Map,Esper只支持增量更新,也就是说只能增加Map中的属性定义,而不能修改或者删除某个属性(实际上属性增多并不影响其处理性能,所以没有删除在我看来也没什么。至于修改,也只能是先注销再注册了)。我们为Person增加一个gender属性,示例如下


import java.util.Arrays;
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;
import com.espertech.esper.client.EventType;
public class PersonMap
{
  public static void main(String[] args)
  {
    EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
    EPAdministrator admin = epService.getEPAdministrator();
    // Address定义
    Map<String, Object> address = new HashMap<String, Object>();
    address.put("road", String.class);
    address.put("street", String.class);
    address.put("houseNo", int.class);
    // 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.put("address", "Address");
    // 注册Address到Esper
    admin.getConfiguration().addEventType("Address", address);
    // 注册Person到Esper
    admin.getConfiguration().addEventType("Person", person);
    // 新增一个gender属性
    person.put("gender", int.class);
    admin.getConfiguration().updateMapEventType("Person", person);
    /** 输出结果:
     * Person props: [address, age, name, children, phones, gender]
     */
    EventType event = admin.getConfiguration().getEventType("Person");
    System.out.println("Person props: " + Arrays.asList(event.getPropertyNames()));
  }
}
复制代码



Object Array


对象数组和Map很像,基本没有差别。只是定义方式不一样,Esper同样也只支持增量更新。这里继续用Person为大家做例子

import java.util.Arrays;
import java.util.Map;
import com.espertech.esper.client.EPAdministrator;
import com.espertech.esper.client.EPServiceProvider;
import com.espertech.esper.client.EPServiceProviderManager;
import com.espertech.esper.client.EventType;
public class PersonArray
{
  /**
   * @param args
   */
  public static void main(String[] args)
  {
    EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
    EPAdministrator admin = epService.getEPAdministrator();
    // Address定义
    String[] addressPropNames = { "road", "street", "houseNo" };
    Object[] addressPropTypes = { String.class, String.class, int.class };
    // Child定义
    String[] childPropNames = { "name", "age" };
    Object[] childPropTypes = { String.class, int.class };
    // Person定义
    String[] personPropNames = { "name", "age", "children", "phones", "address" };
    Object[] personPropTypes = { String.class, int.class, "Child[]", Map.class, "Address" };
    // 注册Address到Esper
    admin.getConfiguration().addEventType("Address", addressPropNames, addressPropTypes);
    // 注册Child到Esper
    admin.getConfiguration().addEventType("Child", childPropNames, childPropTypes);
    // 注册Person到Esper
    admin.getConfiguration().addEventType("Person", personPropNames, personPropTypes);
    // 新增一个gender属性
    admin.getConfiguration().updateObjectArrayEventType("Person", new String[] { "gender" }, new Object[] { int.class });
    /** 输出结果:
     * Person props: [name, age, children, phones, address, gender]
     */
    EventType event = admin.getConfiguration().getEventType("Person");
    System.out.println("Person props: " + Arrays.asList(event.getPropertyNames()));
  }
}
复制代码


上面的例子包含了对象数组这种事件格式的所有特性,我就不多加解释了。



监听案例实现


import java.util.List;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
public class MyListener implements UpdateListener {
    public static final String EPL =
        "select age, children, address, children[1], phones('home') from " + Person.class.getName() + " where name='liwei' ";
    @Override
    public void update(EventBean[] newEvents, EventBean[] oldEvents) {
        if (newEvents != null) {
            Integer age = (Integer) newEvents[0].get("age");
            System.out.println(age);
            List<Child> childs = (List<Child>) newEvents[0].get("children");
            System.out.println(childs.get(0).getName());
            Address ad = (Address) newEvents[0].get("address");
            System.out.println(ad.getRoad());
            Child child = (Child) newEvents[0].get("children[1]");
            System.out.println(child.getName());
            Integer num = (Integer) newEvents[0].get("phones('home')");
            System.out.println(num);
        }
    }
}
public class InvocationMethodTest {
  public static void main(String arg[]) {
    EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
    EPRuntime runtime = epService.getEPRuntime();
    EPAdministrator admin = epService.getEPAdministrator();
    /**
     * 调用外部方法返回Java类型数据
     */
    String epl1 = MyListener.EPL;
    EPStatement state1 = admin.createEPL(epl1);
    state1.addListener(new MyListener());
    Times times = new Times();
    times.setTimes(2);
    runtime.sendEvent(times); 
    /**
     * 调用外部方法返回Map类型数据
     */
    String imName = InvocationMethodMap.class.getName();
    String epl2 = "select * from method:" + imName + ".getMapObject()";
    System.out.println(epl2+"\n");
    EPStatement state2 = admin.createEPL(epl2);
    Iterator<EventBean> iter = state2.iterator();
    while (iter.hasNext()) {
      EventBean event = iter.next();
      System.out.println(event.getUnderlying());
    }
  }




相关文章
|
6月前
|
消息中间件 存储 Cloud Native
【深入浅出RocketMQ原理及实战】「底层原理挖掘系列」打造新一代云原生"消息、事件、流"统一消息引擎的融合处理平台
【深入浅出RocketMQ原理及实战】「底层原理挖掘系列」打造新一代云原生"消息、事件、流"统一消息引擎的融合处理平台
89 0
|
消息中间件 存储 缓存
「事件驱动架构」技术架构师必看事件溯源,CQRS,流处理和Kafka之间的复杂关系
「事件驱动架构」技术架构师必看事件溯源,CQRS,流处理和Kafka之间的复杂关系
|
SQL 存储 XML
【Esper技术专题】带你进入事件驱动领域(内存计算、事件驱动、SQL支持)| 8月更文挑战
【Esper技术专题】带你进入事件驱动领域(内存计算、事件驱动、SQL支持)| 8月更文挑战
986 0
|
数据采集 消息中间件 XML
【Esper技术专题】一个简单的事件响应功能的案例(2)复杂事件处理引擎
【Esper技术专题】一个简单的事件响应功能的案例(2)复杂事件处理引擎
313 0
|
机器学习/深度学习 算法
联邦学习产品及算法运行机制简介(上)
联邦学习产品及算法运行机制简介(上)
230 0
联邦学习产品及算法运行机制简介(上)
|
算法
联邦学习产品及算法运行机制简介(下)
联邦学习产品及算法运行机制简介(下)
134 0
联邦学习产品及算法运行机制简介(下)
|
数据采集 网络协议 应用服务中间件
多线程、事件驱动与推荐引擎框架选型
  事件驱动编程是一种编程范式,这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环,当外部事件发生时使用回调机制来触发相应的处理。多线程是另一种常用编程范式,并且更容易理解。   高性能通用型C++网络框架 Nebula 是基于事件驱动的多进程网络框架(适用于即时通讯、数据采集、实时计算、消息推送等应用场景),已有即时通讯、埋点数据采集及实时分析的生产应用案例。
2449 0