CEP 的英文全称是 Complex Event Processing,翻译成中文为复杂事件处理。它可以用于处理实时数据并在事件流到达时从事件流中提取信息,并根据定义的规则来判断事件是否匹配,如果匹配则会触发新的事件做出响应。除了支持单个事件的简单无状态的模式匹配(例如基于事件中的某个字段进行筛选过滤),也可以支持基于关联/聚合/时间窗口等多个事件的复杂有状态模式的匹配(例如判断用户下单事件后 30 分钟内是否有支付事件)。

CEP 是什么?

CEP 的英文全称是 Complex Event Processing,翻译成中文为复杂事件处理。

CEP 可以用于处理实时数据并在事件流到达时从事件流中提取信息,根据定义的规则来判断事件是否匹配,如果匹配则会触发新的事件做出响应。除了支持单个事件的简单无状态的模式匹配(例如基于事件中的某个字段进行筛选过滤),也可以支持基于关联/聚合/时间窗口等多个事件的复杂有状态模式的匹配(例如判断用户下单事件后 30 分钟内是否有支付事件)。

因为这种事件匹配通常是根据提前制定好的规则去匹配的,而这些规则一般来说不仅多,而且复杂,所以就会引入一些规则引擎来处理这种复杂事件匹配。市面上常用的规则引擎有如下这些。

规则引擎对比

Drools

Drools 是一款使用 Java 编写的开源规则引擎,通常用来解决业务代码与业务规则的分离,它内置的 Drools Fusion 模块也提供 CEP 的功能。

优势:

  • 功能较为完善,具有如系统监控、操作平台等功能。
  • 规则支持动态更新。

劣势:

  • 以内存实现时间窗功能,无法支持较长跨度的时间窗。
  • 无法有效支持定时触达(如用户在浏览发生一段时间后触达条件判断)。

Aviator

Aviator 是一个高性能、轻量级的 Java 语言实现的表达式求值引擎,主要用于各种表达式的动态求值。

优势:

  • 支持大部分运算操作符。
  • 支持函数调用和自定义函数。
  • 支持正则表达式匹配。
  • 支持传入变量并且性能优秀。

劣势:

  • 没有 if else、do while 等语句,没有赋值语句,没有位运算符。

EasyRules

EasyRules 集成了 MVEL 和 SpEL 表达式的一款轻量级规则引擎。

优势:

  • 轻量级框架,学习成本低。
  • 基于 POJO。
  • 为定义业务引擎提供有用的抽象和简便的应用
  • 支持从简单的规则组建成复杂规则

Esper

Esper 设计目标为 CEP 的轻量级解决方案,可以方便的嵌入服务中,提供 CEP 功能。

优势:

  • 轻量级可嵌入开发,常用的 CEP 功能简单好用。
  • EPL 语法与 SQL 类似,学习成本较低。

劣势:

  • 单机全内存方案,需要整合其他分布式和存储。
  • 以内存实现时间窗功能,无法支持较长跨度的时间窗。
  • 无法有效支持定时触达(如用户在浏览发生一段时间后触达条件判断)。

Flink 是一个流式系统,具有高吞吐低延迟的特点,Flink CEP 是一套极具通用性、易于使用的实时流式事件处理方案。

优势:

  • 继承了 Flink 高吞吐的特点
  • 事件支持存储到外部,可以支持较长跨度的时间窗。
  • 可以支持定时触达(用 followedBy + PartternTimeoutFunction 实现)

劣势:

  • 无法动态更新规则(痛点)

Flink CEP是在 Flink 之上实现的复杂事件处理(CEP)库,它允许我们在事件流中检测事件的模式。

因为搭配了 Flink 实时处理的能力,所以 Flink CEP 能够在流处理的场景去做一些实时的复杂事件匹配,它与传统的数据库查询是不一致的,比如,传统的数据库的数据是静态的,但是查询却是动态的,所以传统的数据库查询做不到实时的反馈查询结果,而 Flink CEP 则是查询规则是静态的,数据是动态实时的,如果它作用于一个无限的数据流上,这就意味着它可以将某种规则的数据匹配一直保持下去(除非作业停止);另外 Flink CEP 不需要去存储那些与匹配不相关联的数据,遇到这种数据它会立即丢弃。

虽然 Flink CEP 拥有 Flink 的本身优点和支持复杂场景的规则处理,但是它本身其实也有非常严重的缺点,那就是不能够动态的更新规则。通常引入规则引擎比较友好的一点是可以将一些业务规则抽象出来成为配置,然后更改这些配置后其实是能够自动生效的,但是在 Flink 中却无法做到这点,甚至规则通常还是要写死在代码里面。

举个例子,你在一个 Flink CEP 的作业中定义了一条规则:机器的 CPU 使用率连续 30 秒超过 90% 则发出告警,然后将这个作业上线,上线后发现告警很频繁,你可能会觉得可能规则之前定义的不合适,那么接下来你要做的就是将作业取消,然后重新修改代码并进行编译打包成一个 fat jar,接着上传该 jar 并运行。整个流程下来,你有没有想过会消耗多长的时间?五分钟?或者更长?但是你的目的就是要修改一个配置,如果你在作业中将上面的 30 秒和 90% 做成了配置,可能这样所需要的时间会减少,你只需要重启作业,然后通过传入新的参数将作业重新启动,但是重启作业这步是不是不能少,然而对于流作业来说,重启作业带来的代价很大。

国内的 Flink 技术分享会却看到有几家公司对这块做了优化,让 Flink CEP 支持动态的更新规则,下面分享一下他们几家公司的思路。

  • A 公司:用户更新规则后,新规则会被翻译成 Java 代码,并编译打包成可执行 jar,停止作业并使用 Savepoint 将状态保存下来,启动新的作业并读取之前保存的状态,会根据规则文件中的数量和复杂度对作业的数量做一个规划,防止单作业负载过高,架构如下图所示。

img

  • B 公司:规则中心存储规则,规则里面直接存储了 Java 代码,加载这些规则后然后再用 Groovy 做动态编译解析,其架构如下图所示。

img

  • C 公司:增加函数,在函数方法中监听规则的变化,如果需要更新则通过 Groovy 加载 Pattern 类进行动态注入,采用 Zookeeper 和 MySQL 管理规则,如果规则发生变化,则从数据库中获取到新的规则,然后更新 Flink CEP 中的 NFA 逻辑,注意状态要根据业务需要选择是否重置,其架构设计如下图所示。

img

第一种方法,笔者不推荐,因为它这样的做法还是要将作业重启,无非就是做了一个自动化的操作,不是人为的手动重启,从 B 公司和 C 公司两种方法可以发现要实现 Flink CEP 动态的更新规则无非要做的就是:

  • 监听规则的变化
  • 将规则变成 Java 代码
  • 通过 Groovy 动态编译解析
  • 更改 NFA 的内部逻辑
  • 状态是否保留

上面虽然提到了一个 Flink CEP 的痛点,但是并不能就此把它的优势给抹去,它可以运用的场景其实还有很多,这里笔者拿某些场景来做个分析。

实时反作弊和风控

对于电商来说,羊毛党是必不可少的,国内拼多多曾爆出 100 元的无门槛券随便领,当晚被人褥几百亿,对于这种情况肯定是没有做好及时的风控。另外还有就是商家上架商品时通过频繁修改商品的名称和滥用标题来提高搜索关键字的排名、批量注册一批机器账号快速刷单来提高商品的销售量等作弊行为,各种各样的作弊手法也是需要不断的去制定规则去匹配这种行为。

实时营销

分析用户在手机 APP 的实时行为,统计用户的活动周期,通过为用户画像来给用户进行推荐。比如用户在登录 APP 后 1 分钟内只浏览了商品没有下单;用户在浏览一个商品后,3 分钟内又去查看其他同类的商品,进行比价行为;用户商品下单后 1 分钟内是否支付了该订单。如果这些数据都可以很好的利用起来,那么就可以给用户推荐浏览过的类似商品,这样可以大大提高购买率。

实时网络攻击检测

当下互联网安全形势仍然严峻,网络攻击屡见不鲜且花样众多,这里我们以 DDOS(分布式拒绝服务攻击)产生的流入流量来作为遭受攻击的判断依据。对网络遭受的潜在攻击进行实时检测并给出预警,云服务厂商的多个数据中心会定时向监控中心上报其瞬时流量,如果流量在预设的正常范围内则认为是正常现象,不做任何操作;如果某数据中心在 10 秒内连续 5 次上报的流量超过正常范围的阈值,则触发一条警告的事件;如果某数据中心 30 秒内连续出现 30 次上报的流量超过正常范围的阈值,则触发严重的告警。