写点什么

Flink 高级应用模式第一辑:欺诈检测系统案例研究

  • 2020-02-05
  • 本文字数:4172 字

    阅读完需:约 14 分钟

Flink高级应用模式第一辑:欺诈检测系统案例研究

在这个博客文章系列中,你将学习到三种用来构建流应用程序的强大的 Flink 模式:


  • 动态更新应用程序逻辑

  • 动态数据分区(混排),在运行时控制

  • 基于自定义窗口逻辑的低延迟警报(不使用窗口 API)


这些模式带来了更多使用静态定义的数据流实现的功能,并提供了满足复杂业务需求的构建块。


应用程序逻辑的动态更新允许 Flink 作业在运行时更改,而不会因停止和代码重新提交而导致停机。


动态数据分区提供了在运行时更改 Flink 分配事件和分组方式的能力。当使用可动态重新配置的应用程序逻辑构建作业时,往往会自然而然需求这样的能力。


自定义窗口管理展示了当原生窗口API与你的需求不完全匹配时,如何使用底层进程函数API。具体来说,你将学习如何在 Windows 上实现低延迟警报以及如何使用计时器限制状态增长。


这些模式都是建立在 Flink 核心功能的基础上的,但框架的文档可能不够一目了然,因为不用具体的场景举例的话,往往很难解释和展示这些模式背后的机制。所以我们将通过一个实际示例来展示这些模式,这个示例为 Apache Flink 提供了一个真实的使用场景,那就是一个欺诈检测引擎。我们希望本系列文章能帮助你将这些功能强大的方法放到自己的工具箱中,从而执行一些激动人心的新任务。


在该系列的第一篇博文中,我们将介绍这个演示应用程序的高级架构,描述它的各个组件及其交互。然后,我们将深入研究该系列中第一个模式的实现细节——动态数据分区


你可以在本地完整运行这个欺诈检测演示应用程序,并通过随附的 GitHub 存储库查看实现的细节。

欺诈检测演示

我们的欺诈检测演示的完整源代码都是开源的,可以在线获取。要在本地运行它,请访问以下存储库并按照自述文件中的步骤操作:


https://github.com/afedulov/fraud-detection-demo


你会看到该演示是一个自包含的应用程序——它只需要从源构建 docker 和 docker-compose,并且包含以下组件:


  • 带有 ZooKeeper 的 Apache Kafka(消息代理)

  • Apache Flink(应用程序集群

  • 欺诈检测 Web 应用


这款欺诈检测引擎的高级目标是消费一个金融交易流,并根据一组规则对其进行评估。这些规则会经常更改和调整。在实际的生产系统中,我们需要在运行时添加和删除它们,而不会因停止和重新启动作业而带来高昂的代价。


在浏览器中转到演示 URL 时,将显示以下 UI:



图 1:欺诈检测演示 UI


单击“Start”按钮后,你可以在左侧看到在系统中流动的财务交易的直观表示。可以使用顶部的滑块控制每秒生成的交易数。中间部分用来管理 Flink 评估的规则。在这里,你可以创建新规则以及发出控制命令,例如清除 Flink 的状态。


演示自带一组预定义的示例规则。你可以单击 Start 按钮,一段时间后就能观察到 UI 右侧部分中显示的警报。这些警报是 Flink 根据预定义规则针对生成交易流的评估结果。


我们的欺诈检测示例系统包含三大组件:


  • 前端(React)

  • 后端(SpringBoot)

  • 欺诈检测应用程序(Apache Flink)


主要元素之间的交互如图 2 所示。



图 2:欺诈检测演示组件


后端向前端公开了一个 REST API,用于创建/删除规则以及发布控制命令来管理演示应用的执行。然后通过一个“Control”Kafka 主题将这些前端动作转发给 Flink。后端还包括一个 Transaction Generator(交易生成器)组件,该组件通过单独的“Transactions”主题将模拟的汇款事件流发送到 Flink。由 Flink 生成的警报由后端的“Alerts”主题的消费,并通过 WebSockets 转发到 UI。


现在你已经熟悉了我们这款欺诈检测引擎的总体布局和目标,现在我们来详细介绍实现这种系统所需的条件。

动态数据分区

我们要研究的第一个模式是动态数据分区。


如果你过去曾经使用过 Flink 的 DataStream API,那么你无疑会熟悉 keyBy 方法。Keying 一个流会重排所有记录,以便将具有相同 key 的元素分配给同一分区。这意味着所有具有相同 key 的记录将由下一个运算符的同一个物理实例处理。


在典型的流应用程序中,key 的选择是固定的,由元素内的某些静态字段确定。例如,当构建一个简单的基于窗口的交易流聚合时,我们可能总是按交易账户 ID 进行分组。


DataStream<Transaction> input = // [...]DataStream<...> windowed = input  .keyBy(Transaction::getAccountId)  .window(/*window specification*/);
复制代码


这种方法是在众多用例中实现水平可扩展性的主要构建块。但如果应用程序试图在运行时提供业务逻辑的灵活性,这种方法还不够用。为了理解为什么会发生这种情况,我们首先以一个功能需求的形式为这款欺诈检测系统制定一个现实的示例规则定义:


“只要在一周内从同一付款人向同一收款人累计付款总额超过 1,000,000 美元,就会发出警报。”


在这个公式中,我们可以发现许多能够在新提交的规则中指定的参数,甚至可能稍后在运行时修改或调整它们:


  • 汇总字段(付款金额)

  • 分组字段(付款人+收款人)

  • 汇总函数(总和)

  • 窗口持续时间(1 周)

  • 限制(1000000)

  • 限制运算符(更大)


因此,我们将使用下面这样简单的 JSON 格式来定义上述参数:


{  "ruleId": 1,  "ruleState": "ACTIVE",  "groupingKeyNames": ["beneficiaryId", "payeeId"],  "aggregateFieldName": "paymentAmount",  "aggregatorFunctionType": "SUM",  "limitOperatorType": "GREATER",  "limit": 1000000,  "windowMinutes": 10080}
复制代码


在这里,重要的是要了解 groupingKeyNames 确定的是事件的实际物理分组——必须将具有相同指定参数值(例如,25 号付款人->12 号收款人)的所有交易汇总到评估运算符的同一个物理实例中。自然,在 Flink API 中以这种方式分发数据的过程是通过一个 keyBy()函数实现的。


Flink 的keyBy()文档)中的大多数示例都使用硬编码的 KeySelector,其会提取特定固定事件的字段。但是,为了支持所需的灵活性,我们必须根据规则的定义以更加动态的方式提取它们。为此,我们将不得不使用一个额外的运算符,该运算符为每个事件做准备以将其分发到正确的聚合实例。


在高级层面上,我们的主要处理管道如下所示:


DataStream<Alert> alerts =    transactions        .process(new DynamicKeyFunction())        .keyBy(/* some key selector */);        .process(/* actual calculations and alerting */)
复制代码


先前我们已经确定,每个规则都定义一个 groupingKeyNames 参数,该参数用来指定将哪些字段组合用于传入事件的分组。每个规则都可以使用这些字段的任意组合。同时,每个传入事件都可能需要根据多个规则进行评估。这意味着这些事件可能需要同时出现在与不同规则相对应的评估运算符的多个并行实例上,因此需要进行分叉。用 DynamicKeyFunction()来确保此类事件的分派。



图 3:具有动态 Key 函数的分叉事件


DynamicKeyFunction 迭代一组已定义的规则,并提取所需的分组 key 来为每个要由 keyBy()函数处理的事件作准备:


public class DynamicKeyFunction    extends ProcessFunction<Transaction, Keyed<Transaction, String, Integer>> {   ...  /* Simplified */  List<Rule> rules = /* 初始化的规则.                        细节会在未来的文章中讨论. */;
@Override public void processElement( Transaction event, Context ctx, Collector<Keyed<Transaction, String, Integer>> out) {
for (Rule rule :rules) { out.collect( new Keyed<>( event, KeysExtractor.getKey(rule.getGroupingKeyNames(), event), rule.getRuleId())); } } ...}
复制代码


KeysExtractor.getKey()使用反射来从事件中提取 groupingKeyNames 字段的必需值,并将它们组合为单个串联的字符串 key,例如“ {beneficiaryId = 25; payeeId = 12}”。Flink 将计算该 key 的哈希值,并将此特定组合的处理分配给集群中的特定服务器。这将跟踪 25 号付款人和 12 号收款人之间的所有交易,并在期望的时间窗口内评估定义的规则。


注意,这里引入了具有以下签名的包装类 Keyed,作为 DynamicKeyFunction 的输出类型


public class Keyed<IN, KEY, ID> {  private IN wrapped;  private KEY key;  private ID id;
... public KEY getKey(){ return key; }}
复制代码


该 POJO 的字段包含以下信息:wrapped 是原始交易事件,key 是使用 KeysExtractor 的结果,id 是导致事件分配的 Rule 的 ID(根据特定于规则的分组逻辑)。


这种类型的事件将成为主处理管道中 keyBy()函数的输入,并允许在实现动态数据混排的最后步骤中使用一个简单的 lambda 表达式作为一个(KeySelector)。


DataStream<Alert> alerts =    transactions        .process(new DynamicKeyFunction())        .keyBy((keyed) -> keyed.getKey());        .process(new DynamicAlertFunction())
复制代码


有了 DynamicKeyFunction,我们可以隐式复制事件,以便在 Flink 集群中并行执行各条规则评估。这样一来,我们获得了一个重要的属性——规则处理的水平可扩展性。我们的系统将能够通过向集群添加更多服务器来处理更多规则,也就是提高并行度。实现此属性的代价是重复数据,这可能会成为一个问题,具体取决于特定的参数集,例如传入数据速率、可用网络带宽和事件负载大小等。在实际场景中可以应用其他优化,例如合并具有相同 groupingKeyNames 的规则评估,或添加一个过滤层,以在处理特定规则时剥离所有字段中不需要的事件。

结束语

在这篇博文中,我们用一个示例用例(欺诈检测引擎)讨论了对 Flink 应用程序提供动态运行时更改能力的原因。我们描述了整体架构及其组件之间的交互,并提供了在 dockerized 设置中构建和运行示例欺诈检测应用程序的指引。然后,我们展示了将动态数据分区模式实现为第一个基础构建块以实现灵活的运行时配置的细节操作。


为了将重心放在描述模式的核心机制上,我们将 DSL 和基础规则引擎的复杂性降到了最低。走下去的话,不难想象我们会添加一些扩展,例如允许使用更复杂的规则定义,包括某些事件的过滤、逻辑规则链接以及其他更高级的功能。


在本系列的第二部分中,我们将描述规则如何进入正在运行的欺诈检测引擎。此外,我们将详细介绍管道的主要处理函数——DynamicAlertFunction()的实现细节。



图 4:端到端管道


在下一篇文章中,我们将看到如何在运行时利用 Flink 的广播流来帮助指导欺诈检测引擎中的处理(动态应用程序更新模式)。


原文链接


https://flink.apache.org/news/2020/01/15/demo-fraud-detection.html


2020-02-05 16:315917
用户头像
赵钰莹 InfoQ 主编

发布了 882 篇内容, 共 641.3 次阅读, 收获喜欢 2679 次。

关注

评论

发布
暂无评论
发现更多内容

钉钉协同引擎与应用场景技术探索

阿里巴巴终端技术

分布式 跨平台 客户端 开发框架

流动性挖矿开发模式,方案设计

Geek_232be3

流动性挖矿

全托管云原生 MQTT 消息服务 EMQX Cloud 版本更新,助力开展更加安全灵活的物联网业务

EMQ映云科技

物联网 IoT mqtt emq 3月月更

艾特网能全面参与数据中心蒸发冷却空调系列标准编制

科技热闻

多终端运行小程序,快速丰富智慧社区使用场景

Speedoooo

智慧社区 小程序管理平台 智慧家居 智能家居生态平台 生态平台

成本管理系统解决方案

低代码小观

企业管理 资产管理 成本优化 低成本 CRM系统

怎么搭建在线帮助页面

小炮

帮助中心

大咖说|网易数帆论道 PolarDB 数据库开源 & 存储生态

大咖说

数据库 阿里云 开源 网易

NFT元宇宙开发Defi模式NFT游戏开发DAPP

Geek_232be3

区块链 NFT生态链游

设计模式—代理模式以及动态代理的实现

Linux服务器开发

c++ 设计模式 后端开发 Linux服务器开发 Linux后台开发

共享24小时自助洗车加盟你看好吗

共享电单车厂家

自助洗车 共享洗车 24小时无人洗车 共享自助洗车机

共享洗车机设备多少钱一台?贵不贵

共享电单车厂家

共享洗车机设备 自助洗车机多少钱 自助洗车机价格

24小时自助洗车店加盟靠谱吗

共享电单车厂家

自助洗车机 自助洗车 24小时无人自助洗车 自助洗车加盟 24小时自助洗车店

USB DDK助你轻松实现HarmonyOS USB驱动开发

HarmonyOS开发者

HarmonyOS 驱动框架

王者荣耀商城异地多活架构设计

王大胖

多云管理需求分析与好用的工具介绍

行云管家

云计算 混合云 多云 云管理

流动性挖矿APP系统开发介绍模式

Geek_232be3

LIP流动性挖矿

fastposter v2.6.2 发布 程序员专属海报生成器

物有本末

Vue 海报 Pillow 海报生成器 电商海报

利用云效创建代码仓库,免费还方便

阿里云云效

git 云计算 阿里云 代码仓库 Codeup

如何实现一个支持分级数据统计的增强型透视图

明道云

架构实战营作业一

热猫

架构

95 后新生代 Committer 贺张俭:谈谈年轻人的开源观

Apache Pulsar

开源 云原生 物联网 Apache Pulsar Apache Pulsar 社区

共享自助洗车店加盟需要投资多少

共享电单车厂家

共享自助洗车 自助洗车机 自助洗车

加盟24小时共享自助洗车怎么样?

共享电单车厂家

自助洗车机 自助洗车 24小时共享自助洗车 24小时无人自助洗车 自助洗车加盟

NFT游戏NFT数字藏品交易系统搭建开发

薇電13242772558

NFT

【等保】二级等保是什么意思?哪些企业要做?多少钱?

行云管家

网络安全 等保 二级等保

活动报名|3DCAT实时渲染云行业生态合作系列沙龙之“云XR如何赋能虚拟仿真实验教学”线上活动邀您参会

3DCAT实时渲染

虚拟仿真 实时渲染

BookKeeper PMC 成员翟佳受邀参与DataFunSummit 大数据存储架构峰会

Apache Pulsar

开源 架构 云原生 bookKeeper Apache Pulsar

适创科技以云仿真平台,支持“中国智造”升级

阿里云弹性计算

仿真 高性能计算 EHPC 神龙架构 CAE

瞄准程序员招聘痛点,ShowMeBug让面试代码操作可“回放”

ShowMeBug

博文推荐|使用 Apache Pulsar 和 Scala 进行事件流处理

Apache Pulsar

开源 分布式 云原生 Apache Pulsar Apache Pulsar 社区

Flink高级应用模式第一辑:欺诈检测系统案例研究_AI&大模型_Alexander Fedulov_InfoQ精选文章