其实CEP复杂事件处理,简单来说你可以用通过类似正则表达式的方式去表示你的逻辑,表现能力非常的强,用过的人都知道

开篇先偷一张图,整体了解Flink中的CEP中的  一种重要的图  NFA非确定有限状态机

FlinkCEP在运行时会将用户的逻辑转化成这样的一个NFA Graph (nfa对象)

graph 中包含状态(Flink中State对象),以及连接状态的边(Flink中StateTransition对象)

当从一个State跳变到另一个State时需要通过一条边StateTransition,这条边中包含一个Condition对象包含了用户的逻辑就是我们用户代码中.where()中返回Boolean的方法

也就是说Condition对象中包含是否可以完成状态跳变的条件,A状态要跳变到B状态就必须满足连接AB的边中的条件(边StateTransition对象属于B state)

其中边StateTransition分为三种

  take: 状态满足跳变条件后直接跳变到B状态

  ignore: 状态满足跳变条件以后又回到原来状态,状态保持不变

  process: 这条边可以忽略也可以不忽略

后面源码分析的时候可以看到他们之间的区别

接着从源码来看一下如何用这个NFA图实现Flink中的CEP复杂事件处理的

因为CEP在Flink中被设计成算子的一种而不是单独的计算引擎,所以直接找到CepOperator.java中

来看一下它的初始化Open()

这里看到有一个NFAFactory的工厂创建了一个NFA,这里的这个工厂是在Driver端通过用户编写的代码返回的Patten对象转换得到的,也就是用户env.exection()的时候解析的,工厂对象还包含了用户所有的State集合

继续,在createNFA()方法中

将工厂中的所有顶点也就是状态States放到了NFA对象的一个Map中

Key为这个States的Name(其实就是用户代码中的.next("Name"))

接着看CepOperator.java中接收到数据processElement()方法做了什么

这里是处理时间的,这里其实就是直接执行了,这里就不看了,直接看事件时间是如何处理的

先是取出数据的事件时间,判断是不是小于当前水印了,小于这条数据就证明迟到太久了,如果有侧输出丢给侧输出处理,没有就直接丢弃了,和WindowOperater一样

然后看saveRegisterWatermarkTimer()方法

将 (当前水印+1) 注册成了一个定时器timer用于触发计算,和window原理一样(不知道的可以看看前面的文章)

这里主要是因为窗口是一批一批触发而CEP需要逐个触发,所以用(当前水印+1)当做定时器,也就是说只要水印往前推进了就触发推进这段时间的所有计算

然后bufferEvent()将这条数据加入到了一个Queue中

现在来看触发计算的具体逻辑

来到onEventTime()方法中

先是拿到一个用时间排序的优先队列PriorityQueue里面就是排序的事件时间

getNFAState()这里比较重要,这里通过nfa得到了一个nfaState具体来看一下

这里这个NFAstate会初始化,NFAstate里面包含了一个ComputationState的queue,主要目的是用于每条数据来的时候都会去遍历这个queue,看这条数据是否能匹配上里面的state如果匹配上了就更新下一个准备匹配的状态

这里就知道他为什么NFAstate初始化的时候会把用户所有的State中可以作为开始start的状态放queue了吧

因为一开始没数据,当来数据的时候我要判断这条数据是不是属于我CEP的Begin头,这个state也就是我们用户的begin()方法,所以才把所有的可以作为开始的状态都放到这个PartialMatches这个queue中去,这个PartialMatches后面计算的时候会用到,注意

NFAState的初始化就讲完了

继续,回到处理逻辑

然后根据事件时间作为key拉取前面将数据放入的那个queue中数据,返回的是一个List包含这个事件时间的所有数据

然后排序,这里是二次排序,第一次排序是用的事件时间,二次排序排的是同一时间的数据按什么顺序处理

然后这里ProcessEvent()方法就是具体执行的逻辑了,这里同时会把刚刚初始化好的NFAState传递进去

一开始会获取一个共享的缓冲区主要是为了减小CEP重复数据存储的内存占用,这里不讲了因为CEP论文里面有,比较复杂

这里process()方法就是具体逻辑了,返回了一个map这个map包含了process()方法这条数据匹配成功结束的数据也就是结果,而processMatchedSequences(patterns, timestamp)就是执行用户的.select()逻辑了

既然这里就得到了CEP匹配的结果,来看下具体计算逻辑nfa.process()

这里又初始化两个优先队列

分别用于

  newPartialMatches  装nfa匹配到一半没有结束数据,也就是半匹配,

  potentialMatches     装成功匹配完成的数据,用于返回,调用用户的方法去处理结果

接着

这里就直接去初始化好的NFAState中拿刚刚的那个PartialMatches,并且遍历它,通过传入这个computeNextStates()方法,用于判断这条数据是否可以满足这个ComputationState完成匹配

注意! 一开始时初始化里面只有所有可作为CEP匹配头的ComputationState,可想而知当后面匹配上了以后肯定会更新这个用于看数据是否匹配的queue

   这里就可以知道了整个CEP的处理方式了:  

        一开始会把所有可以作为CEP匹配头的状态State先放入queue,每来一条数据就会遍历queue中所有state,看这条数据是否能能匹配上,能匹配上就在queue中加入下一个用于匹配的状态,用于看下一条数据能否继续匹配上

        比如一个正则"abc"用于CEP匹配 当来了一条a数据,就匹配上CEP头了,会把b state加入queue中,接着来了一条b 数据,又继续匹配上了,又把c state加入queue 直到来了一条c数据整个就匹配完成,返回结果

   总结处理过程就是两步

        1.来一条数据,遍历queue中所有state,看哪些state能匹配上就匹配

        2.根据1的结果更新queue,用于下一条数据的匹配 

    

而判断是否能匹配上就是这个computerNextStates()方法中

先把这个状态state压栈

从栈中取state遍历它所有的边 StateTransitions

调用用户的方法看是否能满足边条件,也就是说是否能跳变到这个状态

当满足时,会根据边

  ignore: 啥都不做

  take:       加入结果集中

  process:  又把这个状态的下一个状态state压栈了,继续循环处理

结果返回这条数据匹配上的状态们,于是

遍历所有匹配上的状态得结果集,会把匹配上的状态的下一个(target)用于匹配的状态加进queue去

如果是结束,默认NFAstate中是有一个自带"&end"的结束state

遍历所有完成的状态,当匹配上最后一个状态时就是上面说的“&end”就证明完成了,丢到完成queue中

当匹配失败了就清空状态

当匹配上了但还没有结束就丢到半匹配queue

接着

会先执行跳过策略把结果筛选一遍

然后

就是用我们前面说的那个半匹配queue了,用它又继续更新了NFAState中的PartialMatches了

下一条数据来了以后就会用遍历这个新queue集合来判断是否可以继续匹配了

然后返回这次匹配成功的数据,调用用户select方法处理结果了

  

Flink中的CEP复杂事件处理 (源码分析)的更多相关文章

  1. Flink中Idle停滞流机制(源码分析)

    前几天在社区群上,有人问了一个问题 既然上游最小水印会决定窗口触发,那如果我上游其中一条流突然没有了数据,我的窗口还会继续触发吗? 看到这个问题,我蒙了???? 对哈,因为我是选择上游所有流中水印最小 ...

  2. Flink中异步AsyncIO的实现 (源码分析)

    先上张图整体了解Flink中的异步io 阿里贡献给flink的,优点就不说了嘛,官网上都有,就是写库不会柱塞性能更好 然后来看一下, Flink 中异步io主要分为两种 一种是有序Ordered 一种 ...

  3. Flink 中LatencyMarks延迟监控(源码分析)

    流式计算中处理延迟是一个非常重要的监控metric flink中通过开启配置   metrics.latency.interval  来开启latency后就可以在metric中看到askManage ...

  4. Flink的Job启动TaskManager端(源码分析)

    前面说到了  Flink的JobManager启动(源码分析)  启动了TaskManager 然后  Flink的Job启动JobManager端(源码分析)  说到JobManager会将转化得到 ...

  5. RocketMQ中Broker的HA策略源码分析

    Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...

  6. Redis学习——ae事件处理源码分析

    0. 前言 Redis在封装事件的处理采用了Reactor模式,添加了定时事件的处理.Redis处理事件是单进程单线程的,而经典Reator模式对事件是串行处理的.即如果有一个事件阻塞过久的话会导致整 ...

  7. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  8. RocketMQ中Broker的刷盘源码分析

    上一篇博客的最后简单提了下CommitLog的刷盘  [RocketMQ中Broker的消息存储源码分析] (这篇博客和上一篇有很大的联系) Broker的CommitLog刷盘会启动一个线程,不停地 ...

  9. List中的ArrayList和LinkedList源码分析

    ​ List是在面试中经常会问的一点,在我们面试中知道的仅仅是List是单列集合Collection下的一个实现类, List的实现接口又有几个,一个是ArrayList,还有一个是LinkedLis ...

随机推荐

  1. 数据库系统(六)---MySQL语句及存储过程

    一.DDL.DML.DCL常用语句 1.DDL(Data Definition Language)数据库定义语言 (1)数据库模式定义 #创建数据库 create database if exsite ...

  2. SpringBoot整合MybatisPlus3.X之逻辑删除(三)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  3. Jdk14都要出了,还不能使用 Optional优雅的处理空指针?

    1. 前言 如果你没有处理过空指针,那么你不是一位真正的 Java 程序员. 空指针确实会产生很多问题,我们经常遇到空的引用,然后又想从这个空的引用上去获取其他的值,接着理所当然的碰到了 NullPo ...

  4. 是true还是false呢?

    古来圣贤皆寂寞 惟有[努]者留其名 ---[努]原文:饮 先总结一个小知识点:0.null.NaN.undefined."" 转成布尔值为false 其他则一律返回true 1.首 ...

  5. Docker常见报错解决方法记录

    [问题一]OCI runtime exec failed......executable file not found in $PATH": unknown [root@localhost ...

  6. NOIP 模拟19

    考试状态一次不如一次,所以这次.......我经无言以对 考完试T1就A了,但不是考试时A的,所以屁用没有! 这次考试其实T1想的是正解但是自己傻逼了,感觉自己只能拿部分分,(而且我还把数据范围少看一 ...

  7. .netcore利用DI实现订阅者模式 - xms

    结合DI,实现发布者与订阅者的解耦,属于本次事务的对象主体不应定义为订阅者,因为订阅者不应与发布者产生任何关联 一.发布者订阅者模式 发布者发出一个事件主题,一个或多个订阅者接收这个事件,中间通过事件 ...

  8. javascript中判断数据类型

    编写javascript代码的时候常常要判断变量,字面量的类型,可以用typeof,instanceof,Array.isArray(),等方法,究竟哪一种最方便,最实用,最省心呢?本问探讨这个问题. ...

  9. mvn上传dubbo jar到nexus

    第一种方式: mvn deploy:deploy-file -DgroupId=com.alibaba -DartifactId=dubbo -Dversion=2.8.4 -Dpackaging=j ...

  10. 面试开挂:近百道Java面试题整理

    1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机执行的字节码文件. Java被 ...