![](https://img.haomeiwen.com/i17302790/1e0324afd9ca74b1.jpg)
摘要:本文整理自腾讯专家级工程师李天旺,在 Flink Forward Asia 2022 AI 特征工程专场的分享。本篇内容主要分为四个部分:
- 背景介绍
- 平台架构的实践
- 线上运营的挑战
- 质量保障
一、背景介绍
![](https://img.haomeiwen.com/i17302790/5b330f72087e01b0.jpg)
微信是国内较庞大而复杂的业务,平台上包括通讯、社交、短视频、支付、小程序、企业微信等等大的业务,小的业务也有上千个。逐利的黑灰产哪里流量多、能赚钱,他们就跑到哪里去,所以微信会被业务黑灰产给盯上,这时如果业务安全、风控没有做好,会让公司和用户蒙受很大的损失。
而我们风控团队的职责就是与这些黑灰产进行对抗。在对抗过程中,我们面对了很多挑战,主要有以下三点:
-
需要面对百万规模从业人员的黑产,而我们的人员远远低于这样数量级。
-
需要覆盖各个业务场景,如果哪个业务场景没有覆盖了,就会被利用。
-
业务安全还有特点就是对抗激烈。当我们做出打击后,坏人就会变招,绕过安全打击。这就需要我们快速调整策略,重新进行打击,它的要求非常快。此外,业务调整、环境变化等也需要快速调整策略来保证我们的业务,这一点比较关注效率。
![](https://img.haomeiwen.com/i17302790/a981987e7d21f20f.jpg)
了解了我们的挑战之后,再来看一看我们业务安全风控的流程,这里列举了四个重要的步骤。
- 第一步,分析,它分为事前风险点分析和事后分析。事前风险点分析指提前判断业务的风险点,做好防范。事后分析指发现问题的时候做 case 的一些分析行为。
- 第二步,特征开发,即开发需要使用的各个特征。
- 第三步,策略,包括规则或者模型。
- 第四步,运营,包括有处罚、客诉、预警、垄断、监控等。
从这个流程可以看出,特征开发在整个风控里是非常重要的一环。
![](https://img.haomeiwen.com/i17302790/97a7e8acc80aab1b.jpg)
策略和特征是唇齿相依的,没有好的数据特征,我们的策略无法达到预期效果。举一个大家感触都比较深的例子,就是银行卡连续输错三次密码就会被锁卡,或者今天就不能再使用了。这就是一个简单规则型的策略,即特征值达到了某个阈值后,然后进行某一种处理。从这个例子可以看出,特征是整个策略的基石,是非常重要的。
![](https://img.haomeiwen.com/i17302790/974da609ff51ea9d.jpg)
我们的特征平台建设主要经历了三个阶段。
-
第一阶段,我们写代码在 KV 上进行累计的服务,没有使用一些框架,所有的功能都是自己写代码实现的。这个阶段存在的问题就是,开发效率低,质量参差不齐。
-
第二阶段,我们使用了 Flink。Flink 有很多开箱即用的算子函数,比如窗口、双流 Join 等,它的功能会更加强大一点,但它有一定的学习门槛。我们在内部推广培训的过程中发现,它的效果不是特别理想。因为我们的安全策略人员不是 Java 栈,而是 C++和 Python 技术栈的,他们对写 SQL 还比较熟悉,但对 Flink 这种 Java 技术栈技术还不能很好的掌握。
所以我们找了几个熟悉 Flink 的人,来专门做实时特征开发。这样也会存在一个问题,就是当突然有很多紧急需求过来的时候,所有的需求都压到这几个人的身上,这几个人就会成为团队效率的瓶颈。为了解决团队效率的问题,我们开始建立了第三阶段。
- 第三阶段,我们建立了一站式的实时特征开发平台。采用画布+组件化的模式进行开发来降低门槛,提升效率。让那些安全策略人员即使没有 Flink 技术栈,也能够做实时特征开发。
![](https://img.haomeiwen.com/i17302790/ee4639931d2f1e6a.jpg)
从业务安全风控的特点出发,平台的特点包括以下四点:
-
业务场景多:平台接入的成本就要低。
-
强对抗:策略就会频繁变更,数据特征就要做到实时。
-
海量的行为数据:需要高性能的计算。
-
直接影响用户体验:数据质量要有保障。如果数据质量有问题,比如特征算的不准,风控的策略也有可能不准,就会导致误伤或者漏过了一些坏人作恶的行为。
总结起来,整个特征开发平台建设需要关注以下三个要点:
-
高效率。
-
高性能,我认为风控类系统对特征质量要求会比推荐类系统要求高一些。
-
高质量,我认为特征质量会比推荐类的系统要求要高一些。
二、平台架构的实践
![](https://img.haomeiwen.com/i17302790/e9f74093b7676005.jpg)
我们以平台化的方式降低业务使用成本,利用开箱即用的能力组件解决场景问题。
首先我们拥有一站式的 WEB IDE,安全策略人员只需要在我们的 WEB IDE 上就能开发它的特征。同时,我们提供了很多能力组件,比如输入组件有实时输入、维表输入等等。计算组件有窗口计算、维表关联、排行榜、双流 Join、去重等等。通过输出组件可以快速对接到它们的服务里,比如对接到规则引擎、KV 的实时数仓、消息队列、Svrkit(RPC 的一个服务)。
那么这些能力组件又是怎么工作的呢?是由我们下层的服务引擎来支撑的。服务引擎主要分为两部分,分别是 SQL 适配引擎和统一计算 Pipeline 框架。
-
SQL 适配引擎是将用户计算的配置转换成 Flink SQL 的过程。
-
统一计算 Pipeline 框架是将输入、计算、输出等组件串起来变成 Flink Job。
最后是基础组件,它由我们的数据平台团队提供,包括 Flink 集群、Pulsar、Kafka 等等云上的组件。
![](https://img.haomeiwen.com/i17302790/40f7df873647eb33.jpg)
上图展示的是我们画布的一站式开发 IDE。左边是我们的组件,右边是我们的画布。组件包括数据输入、数据处理、数据输出等,用户通过拖拽就能复用这些能力,然后通过简单配置就完成它业务逻辑了。
这里有一个关键要点,通过连线来完成数据流的 Pipeline,从输入到计算到最后的输出,这样简单组装起来就可以完成它的业务逻辑过程。它的使用门槛非常低,即使是新人也能很快上手。
![](https://img.haomeiwen.com/i17302790/587d9cace46aff05.jpg)
接下来我们来看一下数据源节点,用户只需要再画布上拖入一个数据源节点,然后配置一下就可以了。这里的配置非常简单,只需选择一个数据源表即可。没有繁琐的 DDL 的配置,由统一的元数据进行管理。
![](https://img.haomeiwen.com/i17302790/4747f4ebe7b7e54f.jpg)
我们的计算节点有很多都是 SQL 节点,但我们的 SQL 节点是采用 SQL+配置化的方式来实现各种功能的,比如窗口、Join 等等。同时,我们还是采用 MySQL 的方式来写 SQL,降低业务的使用门槛,比如用户要写一个窗口统计,它只要按照 MySQL 写一个 group by,然后在右侧设置一下它的窗口就可以了。用户不需要学习流式 SQL 的窗口怎么写,以及窗口函数是怎样的。
同时,它还支持在线的 SQL 调试,自动提取 SQL 结果的 Schema 给下游使用。这样通过 SQL+配置的方式简化之后,用户的使用门槛就变得很低,同时又能满足业务需求的功能。
![](https://img.haomeiwen.com/i17302790/0d55052757a4ee28.jpg)
我们有一个支持 Python 代码片段的节点。
为什么要支持代码片段呢?因为有些数据的结构比较复杂,当我们要去对这些数据做解析的时候,用 SQL 很难实现,这时候只能通过写代码或是写函数的方式来实现。但我们的用户大多都是不熟悉 Flink 的人,所以写代码和写函数对于他们来说门槛就比较高了,因此我们做了 Python 代码片段的节点。
用户只需要拖拽 Python 代码片段节点,我们就能生成一个框架代码,且我们生成的框架代码没有引入除 Python 基础库以外的任何依赖,用户只需实现函数里的内容即可。这个函数有一个输入是 Dict,返回是 List。用户甚至也可以把代码复制到本地进行调试,也支持在线测试代码。配置完节点后,只要指定输出字段以及字段类型即可。然后注册 Schema,这个门槛很低。这个代码片段可以简单理解成 Flatmap 的过程。
那么我们的后台是怎么做的呢?我们将用户代码片段嵌入到 Python 的一个 UDF 里执行,然后再把 UDF 注册到 Flink 里,同时执行 UDF 的函数实现代码片段 Flatmap 的过程。
![](https://img.haomeiwen.com/i17302790/dba37a453b4d6fd2.jpg)
我们的输出节点设置的也比较简单,用户按照他们应用场景选择需要的输出节点,然后拖拽节点进行简单的配置就可以了。我们帮助用户做协议的转换、自动类型的转换、校验等等功能。同时还会提供一些自动化设置的能力,比如用户要输出一个数据到一个 MySQL 表,就会有很多字段需要做映射,如果字段名一样,我们会做一些自动映射,提升他们的效率。
![](https://img.haomeiwen.com/i17302790/53a7b6d198af2447.jpg)
接下来,看一下画布与纯 SQL 比较。我们在使用的时候,觉得拖拽式开发更加直观、易开发、易维护。
如果业务逻辑比较复杂,后期维护也会非常麻烦。比如像上图左侧这样密密麻麻的很多节点,如果要写成纯 Flink SQL,它的 SQL 脚本会非常长,甚至达到一两千行,后期维护 SQL 的时候就会非常麻烦。像上图左侧这样,每个节点都知道它要计算什么功能,这就会比较清晰。
![](https://img.haomeiwen.com/i17302790/be6f0cdcb9d16938.jpg)
因为 Flink SQL 与大家熟悉的 MySQL 语法还是有很多不同的,概念上有一些差别。为此我们做一个 SQL 适配引擎可以将 MySQL 的语法转换成 MySQL,让用户更快的上手,同时还可以分析用户 SQL,提供一些优化选项供用户选择。让不熟悉 Flink 的人也可以开发 Flink 计算,并达到一些优化的效果。此外,SQL 适配引擎也支持 SQL 调试,可以快速验证业务逻辑的正确性,提升开发效率,降低数据质量的风险。
我们看下上图中的例子。如果用户要写一个维表关联,就是 Lookup 计算,那么他只需要按 MySQL 语法写维表关联就行了,即一个 Join 语句。之后我们分析用户的 SQL,哪个是流式表,哪个是为表,然后把 SQL 解析之后再转换成 Flink SQL 到 Flink 里执行。这样用户不需要学习 Flink SQL,门槛会更低。
![](https://img.haomeiwen.com/i17302790/b1d5aa96d6bf3939.jpg)
我们的 Pipeline 框架是将输入、处理、输出等组件转换为 Flink 算子,并对业务算子进行一些优化。例如输入节点,用户只需在页面选择即可,框架会帮助用户完成数据源的对接,设置 Schema、Watermark 水位、自动添加数据源的质量监控等等。处理节点也是类似,会帮助用户做很多复杂的事情。
还有一个非常重要的特点是,可以自动适配实现上下游的对接,这个在 Flink 里较难实现。
![](https://img.haomeiwen.com/i17302790/debf6de3e154da3b.jpg)
Pipeline 组件化设计带来好处包括以下三点:
提供常用组件,方便重用能力与快速开发,不同组件可以满足不同业务场景的需求。比如去重、窗口、排行榜,用户直接用这些组件即可完成配置,不用完整的学习 Flink 各个组件的能力。
开发可以方便的组装业务功能,它可以自由进行上下有连接。同时,我们也可以按组件做专业的优化,获得最大收益。自由组装的方式与其他平台的方式可能会由一些不同,比如 Flink 有一个 Interval Join 之后,它的 Watermark 会产生一个 delay,delay 的时间取决于 Interval Join 等待的时间。这样的场景,如果要经过一个 Interval Join 后,下游再做一个窗口计算,下一个的窗口的触发时间就会延迟。
延迟在我们的风控场景里是不可以接受的,因为风控对实时性要求比较高。如果数据不能及时到达,坏人有可能已经开始作恶了。通过我们的组件,Interval Join 之后我们可以重新设置 Watermark 生成一种策略。这种在纯 SQL 里是不可能实现的,只能通过写代码方式来实现。
![](https://img.haomeiwen.com/i17302790/a1ebce7c42a13c01.jpg)
最后我们来总结一下整个方案架构。
- 拖拽式页面开发:全流程数据处理 Pipeline 方式。
- 组件化:提供常用组件,方便重用能力与快速开发。
- 低门槛化:采用 MySQL 语法作为业务逻辑开发语言,并且提供代码片段节点和不同应用场景的输出节点等功能,降低学习门槛。
这样,即使是没有太多编程经验的用户也可以在平台上进行数据处理,享受高效便捷的开发体验。整个架构设计,我们非常注重效率和使用门槛,这就是我们整个的方案设计。
三、线上运营的挑战
![](https://img.haomeiwen.com/i17302790/c35560afe1cf7746.jpg)
我们在使用封装组件的时候,也针对很多应用场景做了优化。下面介绍几个组件的优化,包括窗口计算、Join 计算、实时数据源等方面。
![](https://img.haomeiwen.com/i17302790/1452167481c82739.jpg)
Flink 的滑动窗口,每条记录过来之后都会注册这条记录所属的所有窗口中。比如用户一小时的登录测速,窗口长度是一小时,滑动步长是一分钟。用户只要登录一次,就会输出 60 条记录,且这 60 条记录的统计值都是 1。这个时候会重复的输出,所以在我们的安全风控里也会重复执行我们的策略。因为执行状态没有改变,所以执行策略结果是一样的;或是重复更新下游的存储 DB 等等。这就会对下游服务造成一定的压力,这是状态没有变化,重复输出的问题。
为了这个解决问题,我们做了一个滑动窗口增量输出的窗口,即一条记录过来,只注册这条记录进入和退出的两个窗口,其他窗口都不注册,这样就可以大大减少我们的输出,常常可达到减少 20 倍左右。
在风控场景中,我们常常需要统计用户维度的特征(按用户 key 进行统计),这种用户数据是不连续的,因此滑动窗口的长度越长、滑动步长越短,效果越明显。滑动窗口增量输出在风控场景里应用是非常多的。
![](https://img.haomeiwen.com/i17302790/3a93810c391d018a.jpg)
Flink 的滚动和滑动窗口,都是在时间到了窗口结束的时候,将窗口内的所有结果统计并在一瞬间输出。比如窗口统计的 key 值达到几百万、上千万,他要瞬间把几百万、上千万的数据输出的话,输出到下游是 RPC 的服务或者是 KV 的存储。这样一瞬间产生大量的写入量写入到下游,就会对下游的系统稳定性造成一定的冲击。
为了解决这个问题,我们一开始采用了消息队列的方式,但这个方式有一个不好的地方,就是下游处理的速度不能很好的设置限制大小,如果限制大了,下游的负载比较高;如果限制小了,数据会积压,导致实时性不够。
为此我们做了一个平滑窗口,即按照窗口 key 的不同做时间偏移,使窗口的结束时间是打散的,这样输出就不会集中到某个时间点。比如用户 1 的窗口时间是 0~10,用户 2 就是 1~11,用户 3 就是 2~12,这样的话每个用户的窗口结束时间都不一样,输出就会比较平滑,但它的窗口长度是一样的。因为在风控里对窗口长度要求比较严格,对窗口开始时间要求不是那么严格,这就能很好的解决业务场景的问题。
通过优化后,输出曲线就从上面的脉冲式输出变成下面平滑的曲线了。一个场景输出瞬间峰值 80+万/s, 按 Uin 偏移后只有 5 万+/s 的输出,下游系统处理起来就会非常平顺了。
![](https://img.haomeiwen.com/i17302790/3969cfcbdcba88cb.jpg)
有一些业务场景它需要每秒关联几十万的数据,即每秒几十万查询 KV 的存储,它的查询量比较大。而 Flink Lookup Join 算子依赖上游的分区数,比如当上游是消息队列的时候,我们就无法直接扩容 Flink 作业来提升它的吞吐。因为如果上游有五个分区,下游只有五个 Join 算子才能拿到数据,同时缓存命中率也不高。
常见的解决方案是扩容上游的消息队列。但这个方法会对系统产生一定的影响,首先分期数增加的同时会增加它的连接数,其次会产生很多比较小的文件。为了解决这个问题,我们开始支持 hash Join 的优化算子,同时支持异步和批量查询。通过这种方法支撑我们这种业务场景需要每秒几十万的查询关联。
优化之后,因为是通过 hash 分发了,就可以扩容我们的 Flink 作业来提升它的吞吐能力,同时也提升了缓存的命中率,因为 hash 之后的缓存命中率会更高。对于用户来说,他只需在选择维表数据的时候开启 hash Join 策略即可,目前我们已默认打开了这种策略。
![](https://img.haomeiwen.com/i17302790/aae33df12b56af5d.jpg)
Flink 的双流 Join 只考虑多的多对多的场景,它会存储期间内的所有数据。但有一些应用场景,只需关心一对一或者一对多的场景。比如像一对一的场景,关联上的数据是不需要保存的,而一对多的场景,关联上多的那一侧数据也不需要保存,对此做了一些优化策略。
优化后,可以大幅的减小计算状态的存储。对用户来说,我们提供了一个组件化的优化策略供他们选择。默认是多对多,也可以选择一对多、一对一等等。
![](https://img.haomeiwen.com/i17302790/2adfacc8579ec8ee.jpg)
Flink 基于事件时间时,它的 Watermark 是根据时间时间增长的,没数据时不增长,这就会导特征不能及时计算输出,进而还会导致下游的风控策略也无法很好的执行,因为拿不到实时特征。对我们这边的影响还是比较大。
为此我们进行了一些优化,当数据源 idle 自动生成 Watermark 进行下发。即 idle 的时候,我们会生成一个系统层的 Watermark,Watermark 等于系统时间减去等待时间,然后 Watermark,推动整个工作流的 Watermark 增长。从而使得下游的窗口能够比较及时的输出。这样就算数据源不连续,我们也能得到实时特征。
四、质量保障
![](https://img.haomeiwen.com/i17302790/95f04e547dceed23.jpg)
质量保障方面,我们主要做了两件事情,分别是 DO 分离和监控告警。
DO 分离是以开发生命周期为基础,制定严格流程规范。开发和测试阶段由业务开发团队负责,我们这边的安全策略人员主要负责需求、业务开发和逻辑自测等功能。测试完后提交上线,上线之后的过程由平台维护团队负责,主要做上线和服务阶段的一些监控等等。
为什么这么做呢?因为我们的业务团队对 Flink 不熟悉,对 Flink 很多运营指标也不能很好的把控,比如 Watermark 的延迟监控、内存指标监控等等。而 Flink 团队他对 Flink 很熟悉,可以快速判断系统的瓶颈在哪里,优化调整资源等等。所以我们做了这样的职责分离,更好的保障我们服务的稳定性。
![](https://img.haomeiwen.com/i17302790/2340a9c6963ac019.jpg)
监控方面,我们做了两个监控,分别是系统监控和数据监控。
- 系统监控,它由我们的数据平台技术团队提供。它支持集群基础组件监控和负载监控,比如内存、IO、网络等等;支持任务状态,比如重启、CK 等等。
- 数据监控,数据还是要通过数据监控来才能更好的把握。比如数据延迟,输入延迟是多少,输出延迟是多少等等;数据丢弃,迟到丢弃多少,脏数据丢弃多少等等;数据波动,突增突降,突然掉 0 等等。
![](https://img.haomeiwen.com/i17302790/1583209c74dc37a5.jpg)
上图展示的是部分监控指标。包括数据输入延迟是多少、数据输出延迟是多少、每天处理计算量等等。
![](https://img.haomeiwen.com/i17302790/be0b5cc1b4b5c755.jpg)
我们未来的规划主要分为两个方面:
- 流批一体化,我们已经开始使用 Hudi 技术进行实践。这将有助于将流数据和批数据进行整合,实现更高效、更灵活的数据处理。
- 智能化运维,由于平台上将维护大量任务,我们希望能够通过智能化工具来判断任务扩缩容的容量大小。传统的人工判断方式效率较低,因此我们希望能够利用智能化工具来提高工作效率,实现更智能化的运维管理。
网友评论