
摘要:本文整理自翼支付高级大数据工程师尹春光,在 Flink Forward Asia 2022 行业案例专场的分享。本篇内容主要分为五个部分:
公司实时业务场景
平台介绍
架构实践
应用场景
未来展望
一、公司实时业务场景

天翼电子商务有限公司(以下简称“翼支付”)是中国电信集团有限公司的成员企业,是国资委双百改革和发改委第四批混改“双试点”企业,也是“双试点”企业中唯一的金融科技公司。公司以翼支付 APP 为载体,面向 7000 万月活用户,提供民生缴费、消费购物、金融理财等服务内容,依托区块链、云计算、大数据、人工智能等技术,赋能超 1000 万家线下商户门店及 170 余家线上知名电商。

目前平台主要面临海量数据处理、高并发的数据服务请求,既有计算的低延迟时效,又具有业务多样性,场景复杂性的特点。业务场景多样化包括金融支付、消费购物、民生缴费等业务。
二、平台介绍

如上图所示,翼支付流计算的发展历程主要分为四个阶段。
第一阶段,我们通过烟囱式的方式开发 Spark Streaming 计算任务,来解决实时数据场景需求。在这个阶段随着需求不断增多,主要面临开发效率低、人工管理困难、运维任务复杂、难以释放人力等问题。
第二阶段,基于 Spark Struct Streaming 构建实时计算平台,用户可以通过网页配置方式来创建实时计算任务,解决大部分开发效率低的问题。除此之外,通过平台自动化管理任务提高了运维效率。但随着业务发展也面临的一些问题:
-
基于 Spark 实时计算任务的延迟性比较大的问题。
-
新增了实时数据集成的诉求。
-
分析人员需要通过 SQL 来实时分析业务数据。
-
开发人员需要提交复杂的计算任务进行托管。
为了实现支持多场景的实时计算任务,我们引入 Flink 作为实时计算引擎,对计算引擎做统一的升级。
第三阶段,基于 Flink SQL 作为实时计算引擎,搭建了支持多场景实时计算任务的平台。
目前第四阶段,正在探索湖仓一体的建设。为了解决实时业务数据入仓延时、业务表变更频繁、在线上抽取业务库导致存库压力大等问题。经过对比主流的数据湖,如 Iceberg、Hudi、DeltaLake,基于功能的完整性、业务场景以及社区活跃度等方面考虑,我们最终选择基于 Flink CDC+Hudi 建设湖仓。

数据开发平台满足多场景的实时任务开发功能,包括如下几种:
-
SQL 任务,支持在线 SQL 调试分析,这种场景适合分析人员和数仓人员。
-
实时特征任务,基于网页进行配置指标,最终转化成 Flink 计算任务,这种功能适合业务人员配置任务。
-
实时数据集成任务,这种功能能够满足数仓的实时抽数需求和技术人员做数据同步要求功能。
-
Jar 包任务,能够解决复杂的定制化开发需求场景,适合所有的大数据开发人员。

上图是实时指标开发的流程图。
-
第一步,新建实时指标。
-
第二步,配置数据源。在配置数据源的时候,支持多层嵌套数据,以及支持配置多个数据源。
-
第三步,对数据源进行预处理,包括数据转换、新增字段、补齐外部数据等。
-
第四步,定义结果存储的类型。
-
第五步,开发指标,在一个实时任务中支持配置多个指标。
-
第六步,对指标的计算逻辑进行配置,包括加工算法、过滤条件、聚合维度等。配置新的指标支持复用原有的指标配置。
-
第七步,完成所有的指标配置后,进行调试运行。
-
第八步,逻辑通过后就可以正常上线了。

上图是实时数据开发平台任务开发的介绍,选择了实时指标任务开发的流程和大家分享一下。四张图分别是新建的实时指标任务、进行数据源配置预处理工作、新增指标的配置、一个任务中配置了多个指标。
三、架构实践

最初的实时数据开发平台架构如图,核心的计算是基于 Spark Structure Streaming+自定义 State 实现。将计算的中间结果缓存到对应的中间存储,这里的中间结果存储类似于 Flink 的 Checkpoint State,计算结果数据输出到中间件和 Hbase 中,为实时看板、智能信贷、实时推荐等服务提供了实时的数据分析和决策。

上图左侧是数据源、维表,中间是计算引擎核心,右侧是中间结果存储和结果数据存储。
中间的计算引擎模块上面是一组指标,下面是 DSL 解析器,解析指标的计算逻辑最终转化成自定义的算法模板,之后就可以通过 Spark Structure Streaming 任务提交,在集群上正常运行。

V1 版本的开发引擎架构存在如下痛点:
-
需要不断开发新的 UDF 函数。所有的算法都是用户自定义的,需要开发较多的、常用的 SQL 函数,如方差、去重、99 线、用户自定义的加解密函数等。但这些函数都无法在 Streaming SQL 中的函数兼容,Streaming SQL 引擎模块也需要重复开发 UDF。
-
中间结果存储在 Redis 中,缓存数据量大。在计算聚合指标的时候需要从缓存中读取数据实现聚合计算,整体计算性能较低。
-
Redis 集群不能兼具效率和数据安全性问题。在大量的读写数据时,如果要保证数据不丢失,开启 AOF 就需要损耗较多的性能。计算任务之间缺少状态的隔离性,一个任务的缓存数据量大会影响其他数据的安全。
-
基于自定义的 DSL 开发指标模板,如果需要继续扩展支持完善的 SQL 以及数据集成等相关任务,开发难度较大。如果需要独立开发 Server 引擎和数据集成任务,需要维护多套代码,开发维护成本较高。
因此在面对不同的实时计算任务场景需求,我们对平台的开发引擎模块进行架构升级。

升级后的架构直接基于 Flink 作为实时计算引擎,统一了实时计算架构。实时数据开发模块分为实时指标、StreamingSQL、实时数据集成,在这里依托于 Flink State,我们将中间结果模块移除,提高了计算任务的稳定性和性能。

我们将实时数据集成、实时 SQL 分析、实时指标计算统一转化成 Flink SQL。基于 Flink SQL 实现计算任务,降低了开发引擎模块维护难度,实现了模块复用。通过 SQL 解析加载 UDF 最终生成 Flink Streaming Graph,将 Job Pipeline 提交到计算集群。经过升级之后,平台的代码维护更加方便,实时计算任务管理、监控都做到了统一。

围绕实时计算引擎模块,我们做了如下工作:
-
元数据管理:通过 Calcite 解析 Flink SQL,最终得到血缘关系。
-
任务的沙箱测试:方便用户在线调试任务,看到 SQL 每一步运行的中间结果。
-
UDF 管理:包括 UDF 的权限管理,用户提交 SQL,通过 SQL 解析得到 UDF 进行权限校验。主要解决一些加密的 UDF 函数权限管理。
-
细密度资源配置:通过 Flink SQL 解析得到 Streaming Graph,然后针对算子设置并行度,解决 Flink SQL 只能统一设置并行度的问题。
-
任务状态监控:通过 Flink Metric 监控任务的状态,例如数据处理的速度低于阈值、Kafka 堆积超过阈值,我们就会判断这个任务处理数据存在问题。
-
自动恢复:通过应用层进行任务自动管理。

第一,多 SQL 任务无法隔离。在一个实时计算任务中通常需要新建多个指标,用户希望多个指标之间可以一起上下线,且指标之间可以隔离。
目前,Flink SQL 无法实现多 SQL 状态隔离,导致一个 SQL 任务变更会影响其他 SQL 从 savepint 进行恢复。任务可以正常启动,但 State 数据会丢失。
针对这个问题,我们的解决思路是对 Flink SQL 任务的多 SQL State 进行隔离,实现多 SQL 正常恢复。

上图是基于 Flink 多 SQL 隔离设计,通过 SQL Parser 解析 Streaming Graph,然后对整个 Streaming Graph 中的每一个算子的 UID 设置计算好的 Hash 值。基于 SQL 的 Hash 摘要+如何定位整个 Graph 图的算子的位置方式,也就是算子类型,上游依赖等信息确定某个节点在图中的位置,从而实现不同的 Graph 的 State 隔离。

首先,上图是简化之后的示意代码,SQL Parser 解析多个 SQL 得到 DML 语句,然后通过 Flink 的执行计划转化成 Streaming Graph,最后对整个图设置节点实现隔离。

第二,针对 Flink SQL 任务的并行度调整。我们针对 Flink SQL 解析出 Streaming Graph 之后,就可以对 Flink 的算子设置并行度,根据 Flink 收集 Metric,如算子的处理速度、任务被压、输入 buffer 占比等组合规则,提示运维进行扩容该节点。
根据不同的任务类型出现繁忙的算子节点不同,需要根据不同的 SQL 场景组合定义规则。例如 Kafka 的 Source 节点通常吞吐量很高;Kafka 分区小的原因限制不用对 Kafka 的 Source 设置较大的并行度。比如 SQL 统计按省份每日新增月活的场景,在数据处理被压的时候经常会出现在 Group AGG 节点,那么就可以调整该节点的并行度,而不用调整 Source 节点和 Rank 节点的并行度。

第三,Flink SQL 任务的调试实践。场景是业务用户希望使用平台进行调试 SQL 逻辑,便于开发定位任务异常。以及在生产上能够查到数据为什么可以 join 关联上和 count 为什么是期望值等。平台的技术人员希望能够得到每一条 SQL 过滤分组运行到哪一个算子,以及运行时的 State 是否存在对应的数据等。面对这样的诉求,我们进行了三种方案的对比。
-
方案一,基于 Minicluster 实现,替换应用的结果表。这个方案的优点是实现流程简单,缺点是不能满足生产上的任务调试,以及在提交任务时会导致整个 Server 占用资源较高。
-
方案二,基于提交真实的计算任务到集群,通过替换每一个逻辑查询增加结果表,让用户能够看到中间的运行数据。这个方案的优点是可以满足开发环境调试以及部分生产场景的调试,它的缺点是暂时只能满足部分的场景任务调试。
-
方案三,通过镜像任务+底层 API ProcessFunction 等插装实现。这个方案的优点是能够满足细粒度的问题排查,缺点是它的实现很复杂,有一定资源消耗的问题。
对比以上三种方案,最终我们选择了方案二进行任务调试。

第四,任务监控告警的优化工作。
-
针对实时数据,我们加入了质量监控,能够及时发现数据异常和告警。
-
针对实时任务进行分级管理,例如风控、营销等口径的任务进行人工干预,类似于报表的任务行定时统一处理。
-
基于 Metric 组合进行任务的状态监控,例如 Flink 处理数据的速度低于阈值,Kafka 堆积超过阈值则进行告警。
-
基于 Metric 组合进行任务的并行度以及内存相关的优化。
四、应用场景

上图是实时看板场景。业务数据通过 Flink CDC 实时抽取,行为数据通过 Flink SQL 实时抽取,进行关联计算,通过 Hbase 的维表进行补齐。计算的最终的结果存储到 ClickHouse,提供给 BI 平台和看板进行实时分析。

在这个场景中日志和业务数据需要快速入仓。实时日志写入集群会有小文件问题,业务库表变更比较频繁,批量抽存库会导致存库压力较大。我们通过 Flink CDC 将 MySQL 直接接入 Kafka,通过 Flink 结合 Hudi 将数据写入到 ODS。这条链路中,MySQL 没有直接入仓,主要是为了实时业务数据的复用,以及目前只是将 Hudi 放到 ODS 层,后面会根据业务场景结合完善整体的湖仓建设。

在平台上通过获取库表元数据信息,配置实时同步的任务,针对业务库支持 OceanBase、MySQL、Oracle 等,通过 Flink CDC 进行增量数据采集。针对 Kafka 数据则通过 Flink SQL 进行增量抽取,实时同步到 Kafka、Hudi、ClickHouse 等组件,较大提升了实时数据集成任务开发和管理的效率。
五、未来展望

我们平台未来的规划如下:
-
第一,Flink 容器化实践,主要为了解决白天流量激增,夜间流量低峰时资源占用的问题,通过 K8s 结合 Flink 容器化实现动态扩缩容。
-
第二,批流融合,为了实现实时和离线的统一计算口径和元数据管理。
-
第三,完善湖仓一体建设,满足数仓分析和业务人员实时数据分析的诉求。
网友评论