1.1。复杂事件处理简介
Esper编译器和运行时的开发旨在满足分析和响应事件的应用程序的需求。一些典型的应用示例是:
- 业务流程管理和自动化(流程监控,BAM,报告异常)
- 财务(算法交易,欺诈检测,风险管理)
- 网络和应用程序监控(入侵检测,SLA监控)
- 传感器网络应用(RFID读取,生产线的调度和控制,空中交通)
这些应用程序的共同点是需要实时或接近实时地处理事件(或消息)。这有时被称为复杂事件处理(CEP)和事件序列分析。这些类型的应用程序的关键考虑因素是吞吐量,延迟和所需逻辑的复杂性。
- 高吞吐量 - 处理大量消息的应用程序(每秒1,000到100k消息)
- 低延迟 - 对发生的条件(从几毫秒到几秒)实时响应的应用程序
- 复杂计算 - 检测事件之间模式(事件关联),过滤事件,事件聚合时间或长度窗口,连接事件序列,基于事件缺失的触发器等的应用程序。
EPL编译器和运行时旨在使构建和扩展CEP应用程序变得更加容易。
有关CEP的更多信息,请访问常见问题解答。
1.2。架构简介
Esper是一种语言,语言编译器和运行时环境。
Esper语言是事件处理语言(EPL)。它是一种声明性的,面向数据的语言,用于处理基于时间的高频事件数据。EPL符合SQL-92标准,并扩展用于分析一系列事件和时间。
Esper编译器将EPL源代码编译为Java虚拟机(JVM)字节码,以便生成的可执行代码在Esper运行时环境中的JVM上运行。
Esper运行时在JVM之上运行。您可以使用Esper运行时运行Esper编译器生成的字节代码。
Esper架构类似于编译为JVM字节码的其他编程语言,例如Scala,Clojure和Kotlin。然而,Esper EPL不是命令式(程序性)编程语言。
1.3。EPL简介
Esper语言是为复杂事件处理和流分析而设计的事件处理语言(EPL)。
EPL被组织成模块。编译器将模块编译为字节代码。我们将术语模块用于EPL源代码单元。
模块由语句组成。语句是执行事件和时间分析的声明性代码。大多数陈述都是以。的形式出现的"select ... from ..."。我们对构成模块的每个声明代码单元使用术语语句。
您的应用程序通过回调或迭代语句的当前结果从语句接收输出。
声明可以声明一个EPL对象,如下所示:
- 事件类型定义流类型信息,并使用create schema或通过配置添加。
- 变量是自由格式值持有者,使用create variable或通过配置添加。
- 命名窗口是可共享的命名数据窗口,并使用添加create window。
- 表是可共享的有组织行,其列具有简单,聚合和复杂类型,并使用添加create table。
- 上下文定义分析生命周期并使用添加create context。
- 表达式和脚本是可重用的表达式,并使用它们添加create expression。
- 索引组织命名窗口事件和表行以进行快速查找,并使用create index。
使用访问修饰符如private,protected并public控制访问EPL-对象。
模块可以选择具有模块名称。模块名称与编程语言中的包名称或命名空间名称具有相似的用途。模块名称用于组织EPL对象并避免名称冲突。
部署已编译的模块时,运行时会为部署分配部署ID。部署标识唯一标识已编译模块的给定部署。已编译的模块可以多次参数化和部署。
语句始终具有语句名称。语句名称标识已部署模块中的语句,并且在部署中是唯一的。部署标识和语句名称的组合唯一标识运行时内的语句。
EPL是类型安全的,因为EPL不允许对对该对象无效的对象执行操作。
1.4。编译器入门
1.4.1。编译器 - 第一步:设置编译器类路径
请将Esper编译器jar文件,公共jar文件和编译器依赖项添加到将编译EPL的程序的类路径中。除esper-common-版本外,运行时不需要此处列出的jar文件.jar。
- 常见的jar文件esper-common-版本.jar
- 编译器jar文件esper-compiler-版本.jar
- ANTLR解析器jar文件 antlr4-runtime-4.7.1.jar
- SLF4J日志库 slf4j-api-1.7.25.jar
- Janino Java编译器janino-3.0.10.jar和commons-compiler-3.0.10.jar
可选的,对于使用Log4j的日志记录,请加slf4j-log4j12-1.7.25.jar和log4j-1.2.17.jar到classpath中。
Esper不需要额外的jar文件来将Esper与JSON格式的事件文档一起使用。
可选的,使用Apache Avro的,请加esper-common-avro-版本.jar的类路径。
1.4.2。编译器 - 第二步:提供有关输入事件的信息
您的应用程序可以注册一个事件类型,以指示编译器输入事件的外观。编译模块时,编译器会检查可用的事件类型信息,以确定模块是否有效。
此示例假定存在Java类,PersonEvent并且该类的每个实例PersonEvent都是一个事件。
小费
没有必要为每种事件类型创建类。
没有必要预先配置每个事件类型。没有必要设置Configuration对象。
这个循序渐进的过程让您轻松上手。
我们的逐步事件类是:
包com.mycompany.myapp;
公共类PersonEvent {
私有字符串名称;
私人年龄;
public PersonEvent(String name,int age){
this.name = name;
this.age =年龄;
}
public String getName(){
返回名称;
}
public int getAge(){
回归年龄;
}
}
第3.5节“比较事件表示” 讨论了不同的事件表示。
有关使用EPL声明事件类型的信息create schema,请参见第5.15节“声明事件类型:创建架构”。
1.4.3。编译器 - 第三步:编译EPL
您的应用程序可以获取调用该类的getCompiler静态方法的编译器EPCompilerProvider:
EPCompiler compiler = EPCompilerProvider.getCompiler();
逐步Configuration向编译器提供添加预定义人员事件的对象:
配置配置= new Configuration();
。configuration.getCommon()addEventType(PersonEvent.class);
此入门部分的示例模块只有一个语句,用于选择每个到达人员事件的名称和年龄。它使用@name注释指定语句名称,并为my-statement语句指定名称。
@name('my-statement')从PersonEvent中选择名称,年龄
使用将compile配置作为编译器参数的一部分传递的方法编译模块:
CompilerArguments args = new CompilerArguments(configuration);
EPCompiled epCompiled;
尝试{
epCompiled = compiler.compile(“@ name('my-statement')select Person,age from PersonEvent”,args);
}
catch(EPCompileException ex){
//在这里处理异常
抛出新的RuntimeException(ex);
}
编译此模块后,编译器将验证该模块是否PersonEvent存在,因为它在from-clause中列出。编译器还会验证name和age属性是否可用,PersonEvent因为它们在select-clause 中列出。编译器生成用于提取属性值和生成输出事件的字节代码。编译器构建内部数据结构供以后由过滤器索引使用,以确保在PersonEvent进入时它将被快速处理。
有关编译API的更多信息,请参见第15章,编译器参考和JavaDoc。
1.5。运行时入门
1.5.1。运行时 - 第一步:设置运行时类路径
请将Esper公共jar文件,运行时jar文件和运行时依赖项添加到将执行已编译模块的程序的类路径中。编译器不需要运行时jar文件。
- 常见的jar文件esper-common-版本.jar
- 运行时jar文件esper-runtime-版本.jar
- SLF4J日志库 slf4j-api-1.7.25.jar
可选的,对于使用Log4j的日志记录,请加slf4j-log4j12-1.7.25.jar和log4j-1.2.17.jar到classpath中。
Esper不需要额外的jar文件来将Esper与JSON格式的事件文档一起使用。
可选的,使用Apache Avro的,请加esper-common-avro-版本.jar的类路径。
1.5.2。运行时 - 第二步:获取运行时
逐步Configuration向运行时提供一个对象,用于添加预定义的人员事件:
配置配置= new Configuration();
。configuration.getCommon()addEventType(PersonEvent.class);
小费
没有必要预先配置每个事件类型。没有必要设置Configuration对象。
但是,对于此示例,由于编译器知道PersonEvent预定义类型,因此必须为运行时预先配置它。
您的应用程序可以通过调用类的getDefaultRuntime静态方法EPRuntimeProvider并传递配置来获取运行时:
EPRuntime runtime = EPRuntimeProvider.getDefaultRuntime(configuration);
有关运行时的更多信息,请参见第16章,运行时参考和JavaDoc。
有关配置的更多信息,请参见第17章,配置和JavaDoc。
1.5.3。运行时 - 第三步:部署EPL编译模块并附加回调
您的应用程序可以使用deploy管理界面的方法部署已编译的模块。
API调用是:
EPDeployment部署;
尝试{
deployment = runtime.getDeploymentService()。deploy(epCompiled);
}
catch(EPDeployException ex){
//在这里处理异常
抛出新的RuntimeException(ex);
}
作为部署的一部分,运行时会验证所有模块依赖项(例如事件类型)确实存在。在部署期间,运行时添加条目以过滤索引,以确保在PersonEvent进入时它将被快速处理。
您的应用程序可以将回调附加EPStatement到接收语句结果。以下示例回调只打印名称和年龄:
EPStatement statement = runtime.getDeploymentService()。getStatement(deployment.getDeploymentId(),“my-statement”);
statement.addListener((newData,oldData,statement,runtime) - > {
String name =(String)newData [0] .get(“name”);
int age =(int)newData [0] .get(“age”);
System.out.println(String.format(“Name:%s,Age:%d”,name,age));
});
您的应用程序可以提供不同类型的回调,请参见表16.2“接收语句结果的选择”。
1.5.4。运行时 - 第四步:发送事件
您的应用程序可以使用作为运行时接口一部分的sendEventBean方法(或sendEvent与您选择的事件匹配的其他方法)将事件发送到运行时:
runtime.getEventService()。sendEventBean(new PersonEvent(“Peter”,10),“PersonEvent”);
您应该看到的输出是:
姓名:彼得,年龄:10岁
在将PersonEvent事件对象发送到运行时后,运行时会查询内部维护的共享过滤器索引树结构,以确定是否有任何语句对PersonEvent事件感兴趣。作为此示例的一部分部署的语句PersonEvent位于from-clause中,因此运行时将此类事件的处理委托给该语句。编译后的字节码通过调用getName和getAge方法获取名称和年龄属性。
1.6。必需的第三方库
1.6.1。常见的第三方库
编译器和运行时都需要以下第三方库:
- SLF4J是一个日志API,可以与LOG4J和其他日志API一起使用。虽然需要SLF4J,但LOG4J日志组件不是必需的,可以用其他记录器替换。SLF4J根据Apache 2.0许可证获得许可lib/esper_3rdparties.license。
1.6.2。编译器 - 必需的第三方库
编译器只需要以下第三方库进行编译(而不是在运行时):
- ANTLR是解析器生成器,用于解析和解析模式和EPL语法的树行走。归功于Terence Parr,网址为http://www.antlr.org。ANTLR许可证是BSD许可证,随附于lib/esper_3rdparties.license。在antlr-runtime运行时库所需的运行时间。
- Janino是一个小而快的Java编译器。编译器使用Janino生成代码并编译生成的代码。Janino根据3条款新BSD许可证获得许可lib/esper_3rdparties.license。
1.6.3。运行时 - 必需的第三方库
运行时不需要任何其他第三方库。