flink cep pattern动态加载

通常我们在提交一个flink cep任务,流程基本上是:开发,打包,部署;例如我们有一个任务:计算在60秒内,连续两次登陆失败的用户

begin("begin").where(_.status=='fail').next("next").where(_.status=="fail").within(Time.seconds(60))

然后又来一个任务:计算60秒内,用户登陆失败1次,然后第二次登陆成功的用户

begin("begin").where(_.status=='fail').next("next").where(_.status=="success").within(Time.seconds(60))

这两个任务,数据的输入,输出都是一样的,唯一的区别就是pattern不同;往常的话我们要重复之前的3个步骤才能完成任务的上线;如果我们能根据flink 任务传入参数,动态生成pattern对象,就能简化任务的上线流程,画个图

如何实现pattern动态加载?为了实现这个功能,可以拆分成两个步骤

1.根据pattern规则转换成pattern字符串
val patternStr="begin(\"begin\").where(_.status==\"fail\")
        .next(\"next\").where(_.status==\"fail\").within(Time.seconds(60))"
2.将pattern字符串转换成pattern对象
val pattern = transPattern(patternStr)

现在先看第2步,假定现在有了patternStr,如何转成pattern对象?

str->obj,第一个能想到的方案就是使用javax.script.ScriptEngine调用groovy脚本的方法生成pattern对象;

groovy 脚本如下:

import com.hhz.flink.cep.pojo.LoginEvent
import com.hhz.flink.cep.patterns.conditions.LogEventCondition
import org.apache.flink.cep.scala.pattern.Pattern
import org.apache.flink.streaming.api.windowing.time.Time
def getP(){
    return Pattern.<LoginEvent>begin("begin")
    .where(new LogEventCondition("getField(eventType)==\"fail\""))
    .next("next").where(new LogEventCondition("getField(eventType)==\"fail\""))
    .times(2).within(Time.seconds(3))
}

这个脚本可以以字符串的方式通过groovy脚本引擎加载到内存中,并使用invokeFunction调用getP()方法,就可以返回pattern对象,伪代码如下

String script="def getP(){return Pattern.<>....within(Time.seconds(3)))}";
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine =  factory.getEngineByName("groovy");
engine.eval(script);
Invocable inv = (Invocable) engine;
Pattern<LoginEvent, LoginEvent> pattern
    = (Pattern<LoginEvent, LoginEvent>) invocable.invokeFunction("getP");

现在回过头来看第一步:根据pattern规则转换成pattern字符串

在scala中pattern代码如下

begin("begin").where(_.status=='fail').next("next").where(_.status=="fail").within(Time.seconds(60))

where方法可以接受表达式,例如"_.status=='fail'",同时他也可以接受一个SimpleCondition对象,例如

where(new SimpleCondition<LoginEvent>() {
            @Override
            public boolean filter(LoginEvent event) {
                return event.eventType() == "fail";
            }
        })

但在groovy中不支持接受"_.status=='fail'"表达式作为函数的参数,所以在生成pattern串是必须将where中的表达式换成SimpleCondition对象;

那问题来了,换成SimpleCondition对象后,我们就得在filter方法中实现表达式的逻辑;这显然不是我们所需要的,这样做的话,patternStr的维护的成本就太高了;如果我们将表达式以字符串的形式传入到SimpleCondition对象中,然后在filter中自动计算表达式的值,就像

where(new LogEventCondition("getField(eventType)==\"fail\""))

filter方法根据表达式getField(eventType)==\"fail\"计算结果,返回ture或false,难点来了,如何根据表达式计算结果,这里就需要引入aviator包,关于aviator我们看几个样例

import com.googlecode.aviator.AviatorEvaluator;
public class TestAviator {
    public static void main(String[] args) {
        Long result = (Long) AviatorEvaluator.execute("1+2+3");
        System.out.println("-------"+result);
    }
}
结果输出:
-------6

具体看下LogEventCondition

public class LogEventCondition  extends SimpleCondition<LoginEvent> implements Serializable {
    private String script;

    static {
        AviatorEvaluator.addFunction(new GetFieldFunction());
    }

    //getField(eventType)==\"fail\"
    public LogEventCondition(String script){
        this.script = script;
    }

    @Override
    public boolean filter(LoginEvent value) throws Exception {
        Map<String, Object> stringObjectMap = Obj2Map.objectToMap(value);
        //计算表达式的值
        boolean result = (Boolean) AviatorEvaluator.execute(script, stringObjectMap);
        return result;
    }

}

到这里,cep pattern动态加载就介绍完了;起初我也是想像某些大厂一样,通过订制一套特有的DSL语法,然后将DSL语句解析转换成pattern,这样的话,非开发同学也就能够在公司的数据平台直接订制实时计算任务;但回想之前给非研发同学培训sql的悲惨经历,放弃了;觉得pattern订制的学习成本还是有点高,交给运营或者产品去搞这事,不靠谱,所以这事还是得研发来干;对于研发来讲pattern最原生的规则就是最好的~~~

------------20210917  更新------------------

补齐下之前遗漏的代码

package com.hhz.flink.cep.patterns.aviator;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorJavaType;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorString;

import java.util.HashMap;
import java.util.Map;

public class GetFieldFunction extends HhzFieldFunction {

    @Override
    public String getName() {
        return "getField";
    }

    @Override
    public AviatorString call(Map<String, Object> params, AviatorObject arg1) {

        AviatorJavaType field = (AviatorJavaType)arg1;
        String name = field.getName();

        if(name.contains(".")){
            return new AviatorString(jsonValue(name, params));
        }

        String stringValue = FunctionUtils.getStringValue(arg1, params);
        return new AviatorString(stringValue);
    }

    public static void main(String[] args) {
        AviatorEvaluator.addFunction(new GetInt());
        Map<String,Object> m = new HashMap<String, Object>();
        m.put("person","{age:12,name:\"zhangsan\"}");
        System.out.println(AviatorEvaluator.execute("getInt(person.age)<12", m));
    }
}






  
package com.hhz.flink.cep.patterns.aviator;

import com.alibaba.fastjson.JSONObject;
import com.googlecode.aviator.runtime.function.AbstractFunction;

import java.util.Map;

public abstract class HhzFieldFunction extends AbstractFunction {


    public String jsonValue(String fieldName, Map<String, Object> params){
        String[] arr = fieldName.split("\\.");
        String json = params.get(arr[0]).toString();
        JSONObject object = JSONObject.parseObject(json);
        return object.getString(arr[1]);
    }
}

  • 11
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
Flink CEP (Complex Event Processing) 是Apache Flink的一个组件,用于处理和查询数据流中的复杂事件模式。动态规则是指在运行时能够动态修改和更新事件模式的规则。具体而言,在使用Flink CEP进行事件处理时,动态规则允许我们根据实时需求对规则进行灵活调整。 动态规则的实现可以通过在Flink CEP中使用可扩展的API。Flink CEP提供了创建和管理事件规则的方法,这些规则可以根据特定的需求进行灵活的增删改。通过使用动态规则,我们可以在不中断系统运行的情况下动态地添加、删除或修改事件的匹配规则。这对于处理实时数据流的应用非常有用,因为业务需求往往会随着时间的推移而变化,需要根据新的规则及时适应数据的变化。 使用动态规则的一个实际场景是,当我们需要监控在线交易时的欺诈行为。我们可以配置一组初始规则来检测可能的欺诈模式。然而,在实际应用中,新的欺诈模式可能会不断出现,将新的规则手动添加到系统中效率低且不够实时。因此,我们可以使用Flink CEP动态规则功能,根据欺诈行为的新模式实时更新规则,确保系统始终能够检测到最新的欺诈模式,提高交易监控的准确性和效率。 总之,Flink CEP动态规则功能提供了一种灵活的方式来处理实时事件流中的复杂模式匹配需求。通过实时地更新和更改规则,我们可以及时适应业务需求的变化,提高系统的实时性和准确性。这在许多应用场景中都是非常有价值的,特别是那些需要随时适应新规则的实时数据处理任务中。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值