使用具体的例子来讲解如何使用Esper

这篇文章使用了一个通俗的具体的例子来讲解Esper。在文末有两个版本的代码链接。通过这篇文章,你将会了解如何使用Esper,了解如何集成Esper到Spring框架中,了解如何使用Apache Active MQ和JMS来为Esper提供事件数据。

以下是一个非常简单的事件流处理示例(使用ESPER引擎)。在GitHub上可以看到一个完整的代码, ~ 链接在文末 ~。

什么是复杂事件处理(CEP)?

复杂事件处理(CEP)或事件流流处理(ESP)是事件驱动系统中常用的技术。这些类型的系统实时响应事件数据流。通常情况下,常常应用于金融交易,欺诈识别和流程监控 - -需要识别、理解并快速响应数据事件流中的模式。

CEP系统的关键组件

CEP系统就像典型数据库模型颠倒过来一样。一个典型的数据库存储数据,并对数据运行查询,而CEP存储`查询语句,对实时流进来的数据进行匹配查询或者说过滤和匹配。

要做到这一点基本上需要:

  • 数据 - 以“事件”的形式,常见地以普通java类表示。
  • 查询 - 使用EPL(’事件处理语言’)
  • 监听器 - 如果有结果返回,那么它会被触发从而“执行某些操作”。(回调函数)

一个简单的例子 - 一座核电站

以核电站为例..
这里写图片描述

现在,这只是一个例子 - 假设你最关心的是核电站的临界温度等的信息。

监测核心温度

假设我们的发电站有一个发电站,而且如果太热的话 - 非常糟糕的事情会发生。所以检测核电站核心地温度并在异常情况下发出警告信息是很重要地。
这里写图片描述

假设我们有温度计,每秒钟读取核心温度 - 并将数据发送到中央监控系统。

有什么要求?

当发现3种类型的事件时,我们需要警告:

  • 监控
    只需告诉我们每10秒钟的平均温度 - 供参考

  • 警告
    警告我们,如果我们有2个连续的温度超过一定的阈值

  • 危急
    提醒我们,如果我们有4个连续事件,第一个事件超过一定的阈值,并且每个后续事件都大于最后一个 - 最后一个比第一个大1.5倍。这意味着有一个突然的不断升高的温度峰值 - 有点像下图。让我们假设这是一件非常糟糕的事情。
    这里写图片描述

使用Esper

  • 我们可以通过多种方式构建系统来处理这些需求。这篇文章将使用Esper来解决这个问题。

  • Esper大体这样处理:

  • 使用Esper创建3个查询(使用EPL - Esper查询语言)来对这些事件模式进行建模。
  • 然后,我们为每个查询附加一个侦听器(listener) - 当EPL检测到事件的匹配模式时,会触发此侦听器。
  • 创建一个Esper服务,把上面的listener和它绑定,完成注册。
  • 当有匹配的数据的时候,Esper服务会向相关的listener发送消息。

我们简单的ESPER解决方案

系统的核心是用于检测事件的3个查询。

查询1 - MONITOR(只监视平均温度)

select avg(value) as avg_val 
from TemperatureEvent.win:time_batch(10 sec)

查询2 - 警告(告诉我们,如果我们有2个连续事件违反阈值)

select * from TemperatureEvent "
 match_recognize ( 
   measures A as temp1, B as temp2 
   pattern (A B) 
   define  
     A as A.temperature > 400, 
     B as B.temperature > 400)

查询3 - 关键 - 连续4个上升值超过100,第四个值比第一个大1.5倍

select * from TemperatureEvent 
match_recognize ( 
measures A as temp1, B as temp2, C as temp3, D as temp4 
pattern (A B C D) 
define
A as A.temperature < 100, 
B as (A.temperature < B.value), 
C as (B.temperature < C.value), 
D as (C.temperature < D.value) and D.value > (A.value * 1.5))

一些代码片段

TemperatureEvent

  • 假设我们的传入数据以TemperatureEvent POJO的形式到达
  • 可以通过JMS队列传入esper消息数据,侦听器再将其转换为POJO。这样使用JMS可以将我们从传入的数据结构中分离出来,提高项目地灵活性。这里只使用了普通地Java类。下面是我们的POJO的一个例子,表示事件类型。
package com.cor.cep.event;

import java.util.Date;

/**
 * Immutable Temperature Event class. 
 * The process control system creates these events. 
 * The TemperatureEventHandler picks these up 
 * and processes them.
 */
public class TemperatureEvent {

    /** Temperature in Celcius. */
    private int temperature;

    /** Time temerature reading was taken. */
    private Date timeOfReading;

    /**
     * Single value constructor.
     * @param value Temperature in Celsius.
     */
    /**
     * Temerature constructor.
     * @param temperature Temperature in Celsius
     * @param timeOfReading Time of Reading
     */
    public TemperatureEvent(int temperature, 
            Date timeOfReading) {
        this.temperature = temperature;
        this.timeOfReading = timeOfReading;
    }

    /**
     * Get the Temperature.
     * @return Temperature in Celsius
     */
    public int getTemperature() {
        return temperature;
    }

    /**
     * Get time Temperature reading was taken.
     * @return Time of Reading
     */
    public Date getTimeOfReading() {
        return timeOfReading;
    }

    @Override
    public String toString() {
        return "TemperatureEvent [" + temperature + "C]";
    }

}

处理这个事件

  • 主要处理程序类在TemperatureEventHandler.java中,初始化了Esper服务。
  • 创建了3个语句并为每个语句添加了一个侦听器
/**
 * Auto initialise our service after Spring bean wiring is complete.
 */
@Override
public void afterPropertiesSet() throws Exception {
    initService();
}


/**
 * Configure Esper Statement(s).
 */
public void initService() {

    Configuration config = new Configuration();

    // Recognise domain objects in this package in Esper.
    config.addEventTypeAutoName("com.cor.cep.event");
    epService = EPServiceProviderManager.getDefaultProvider(config);

    createCriticalTemperatureCheckExpression();
    createWarningTemperatureCheckExpression();
    createTemperatureMonitorExpression();
}
  • 创建临界温度警告并注册侦听器。
/**
  * EPL to check for a sudden critical rise across 4 events, 
  * where the last event is 1.5x greater than the first. 
  * This is checking for a sudden, sustained escalating 
  * rise in the temperature
  */
 private void createCriticalTemperatureCheckExpression() {

     LOG.debug("create Critical Temperature Check Expression");
     EPAdministrator epAdmin = epService.getEPAdministrator();
     criticalEventStatement = 
             epAdmin.createEPL(criticalEventSubscriber.getStatement());
     criticalEventStatement.setSubscriber(criticalEventSubscriber);
 }
  • 最后 - 一个关键事件监听器的例子。
package com.cor.cep.subscriber;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.cor.cep.event.TemperatureEvent;

/**
 * Wraps Esper Statement and Listener. No dependency on Esper libraries.
 */
@Component
public class CriticalEventSubscriber implements StatementSubscriber {

    /** Logger */
    private static Logger LOG = 
            LoggerFactory.getLogger(CriticalEventSubscriber.class);

    /** Minimum starting threshold for a critical event. */
    private static final String CRITICAL_EVENT_THRESHOLD = "100";

    /**
     * If the last event in a critical sequence is this much greater 
     * than the first - issue a critical alert.
     */
    private static final String CRITICAL_EVENT_MULTIPLIER = "1.5";

    /**
     * {@inheritDoc}
     */
    public String getStatement() {

        // Example using 'Match Recognise' syntax.
        String criticalEventExpression = "select * from TemperatureEvent "
                + "match_recognize ( "
                + "measures A as temp1, B as temp2, C as temp3, D as temp4 "
                + "pattern (A B C D) "
                + "define "
                + "       A as A.temperature > " + CRITICAL_EVENT_THRESHOLD + ", "
                + "       B as (A.temperature < B.temperature), "
                + "       C as (B.temperature < C.temperature), "
                + "       D as (C.temperature < D.temperature) "
                + "and D.temperature > "
                + "(A.temperature * " + CRITICAL_EVENT_MULTIPLIER + ")" + ")";

        return criticalEventExpression;
    }

    /**
     * Listener method called when Esper has detected a pattern match.
     */
    public void update(Map<String, TemperatureEvent> eventMap) {

        // 1st Temperature in the Critical Sequence
        TemperatureEvent temp1 = 
                (TemperatureEvent) eventMap.get("temp1");
        // 2nd Temperature in the Critical Sequence
        TemperatureEvent temp2 = 
                (TemperatureEvent) eventMap.get("temp2");
        // 3rd Temperature in the Critical Sequence
        TemperatureEvent temp3 = 
                (TemperatureEvent) eventMap.get("temp3");
        // 4th Temperature in the Critical Sequence
        TemperatureEvent temp4 = 
                (TemperatureEvent) eventMap.get("temp4");

        StringBuilder sb = new StringBuilder();
        sb.append("***************************************");
        sb.append("n* [ALERT] : CRITICAL EVENT DETECTED! ");
        sb.append("n* " + temp1 + " > " + temp2 + 
                " > " + temp3 + " > " + temp4);
        sb.append("n***************************************");

        LOG.debug(sb.toString());
    }


}

代码运行

运行演示的完整说明可以在这里找到:

这里有两个GitHub代码,它们在Esper的部分是一样的,区别在于,第一个使用一个POJO类产生Esper消息,比较简单。第二个在第一个地基础之上做了拓展,增加了JMS和Apache ActiveMQ。也就是使用JMS来产生Esper消息。如果要顺利执行第二个代码,需要额外安装Apache ActiveMQ。具体情况请阅读每一个项目地README文件。

1. github.com/corsoft/esper-demo-nuclear

2. https://github.com/liangyihuai/Esper4SpingJMS

运行演示的一个例子如下所示 - 它生成随机温度事件并通过Esper处理器发送它们。

当我们的3个查询中的任何一个检测到匹配时 - 调试信息被显示到控制台。在现实世界的解决方案中,这三个监听器中的每一个都会以不同的方式处理这些事件 - 也许可以通过发送消息来提醒队列/端点来获取系统的其他部分来获取处理。
这里写图片描述

结论

使用像Esper这样的系统是一种使用最少代码实时监控和识别数据模式的简便方法。

这篇文章只是一个简单的用例。查看 Esper网站获取更多信息。

这篇文章翻译自:complex-event-processing-made

猜你喜欢

转载自blog.csdn.net/liangyihuai/article/details/79705357