其实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. marquee滚动标签

    marquee语法    <marquee></marquee> 实例一<marquee>Hello, World</marquee> marquee常 ...

  2. PHP 类中使用全局变量和全局常量

    <?php $global_var = "var"; define('global_const', 'const'); class Test { public $_var; ...

  3. 全方面讲解TensorFlow

    任何曾经试图在 Python 中只利用 NumPy 编写神经网络代码的人都知道那是多么麻烦.编写一个简单的一层前馈网络的代码尚且需要 40 多行代码,当增加层数时,编写代码将会更加困难,执行时间也会更 ...

  4. 【IntelliJ IDEA】 常用快捷键列表

    1.常用Shortcut F2 或Shift+F2 高亮错误或警告快速定位 Ctrl+Up/Down 光标跳转到第一行或最后一行下 Ctrl+B 快速打开光标处的类或方法  CTRL+ALT+B  找 ...

  5. [翻译]——MySQL 8.0 Histograms

    前言: 本文是对这篇博客MySQL 8.0 Histograms的翻译,翻译如有不当的地方,敬请谅解,请尊重原创和翻译劳动成果,转载的时候请注明出处.谢谢! 英文原文地址:https://lefred ...

  6. 基于xposed Hook框架实现个人免签支付方案

    我的个人网站如何实现支付功能? 想必很多程序员都有过想开发一个自己的网站来获得一些额外的收入,但做这件事会遇到支付这个问题.目前个人网站是无法实现支付功能的. 今天我就给大家分享一下我的实现方案:&l ...

  7. NOIP模拟赛18 皇帝的烦恼O(∩_∩)O 二分+DP

    题目描述 经过多年的杀戮,秦皇终于统一了中国.为了抵御外来的侵略,他准备在国土边境安置n名将军.不幸的是这n名将军羽翼渐丰,开始展露他们的狼子野心了.他们拒绝述职.拒绝接受皇帝的圣旨. 秦皇已经准备好 ...

  8. Kubernetes3-kubectl管理Kubernetes容器平台-1

    一.简介 1.什么是kubectl kubectl前面其实已经用到了一些,它其实就是用于操作kubernetes集群的命令行接口,通过kubectl的各种命令实现各种功能 2.环境还是用上一偏文章 K ...

  9. 《JS高程》-教你如何写出可维护的代码

    1.前言   在平时工作开发中,大部分开发人员都花费大量的时间在维护其他人员的代码.很难从头开始开发新代码,很多情况下都是以他人成果为基础的,或者新增修改需求,自己写的代码也会被其他开发人员调用,所以 ...

  10. php判断是不是https的方法

    php判断是不是https的方法<pre> public function is_https() { if (!empty($_SERVER['HTTPS']) && st ...