作业帮基于 Flink 的实时计算平台实践
摘要:本文整理自作业帮实时计算负责人张迎在 Flink Forward Asia 2021 的分享。在作业帮实时计算演进过程中,Flink 起到了重要的作用,特别是借助于 FlinkSQL 极大的提高了实时任务的开发效率。这篇文章主要分享 FlinkSQL 在作业帮的使用情况、实践经验,以及随着任务规模增长,在从 0 到 1 搭建实时计算平台的过程中遇到的问题及解决方案。内容包括:
一、发展历程
作业帮主要运用人工智能、大数据等技术,为学生提供更高效的学习解决方案。因此业务上的数据,主要是学生的到课情况、知识点掌握的情况这些。整体架构上,无论是 binlog 还是普通日志,经过采集后写入 Kafka,分别由实时和离线计算写入存储层,基于 OLAP 再对外提供对应的产品化服务,比如工作台、BI 分析工具。

作业帮的实时计算目前基本以 Flink 为主,发展历程大概有三个阶段:

接下来介绍两个方面:
二、Flink SQL 应用实践
这是基于 Flink SQL 的完整数据流架构:

binlog/log 采集写入 Kafka 后,topic 会自动注册成为元数据的一张表,这是后续所有实时 SQL 作业的起点。用户可以在 SQL 作业里使用这个表,而不用定义复杂的 DDL。
同时,考虑实际应用时,也需要在元数据表的基础上,能够对表属性进行新增或者替换:
框架也需要支持用户的 SQL 作业方便的输出 metrics 以及日志,以做到全链路的监控以及Trace。
这里主要介绍下 SQL 增加 Trace 功能时 DAG 优化实践,以及我们在 Table 底层物理存储的选型和封装。
2.1 SQL 增加 Trace 功能
SQL 可以提高开发人效,但是业务逻辑的复杂度还在,复杂的业务逻辑写出来的 DML 会很长。这种情况下,会推荐使用视图来提高可读性。因为视图的 SQL 更简短,跟代码规范里单个函数不要太长很像。
下图左边是一个示例任务的部分 DAG,可以看到 SQL 节点很多。这种情况下出了 case 定位比较困难,因为如果是 DataStream API 实现的代码,还可以添加日志。但是 SQL 做不到,用户能够干预的入口很少,只能看到整个作业的输入输出。
类似于在函数里打印日志,我们希望能够支持给视图增加 Trace,方便 case 追查。

但是尝试给 SQL 增加 Trace 时遇到了一些问题,举一个简化后的例子:

右上角的 SQL 创建 source_table 作为源表, prepare_data 视图读取该表, sql 里调用了 foo udf,然后使用 StatementSet 分别 insert into 到两个下游,同时,将视图转为 DataStream 以调用 TraceSDK 写入 trace 系统。
注:我们当时是基于 1.9 开发的,这里为了讲述清楚,也使用了一些后来加入的 feature
https://issues.apache.org/jira/browse/FLINK-16361 https://issues.apache.org/jira/browse/FLINK-18840
从上图下方的实际 DAG 看不太符合预期:
数据源压力以及计算性能都需要优化。
解决这个问题需要从几个角度分别优化,这里主要介绍下 DAG 合并的思路,无论是 table 还是 stream 的 env,都会生成对应的 transformation。我们的做法是统一合并到 stream env 下,这样在 stream env 就能拿到一个完整的 transformation 列表,然后生成 StreamGraph 提交。
左下就是我们优化后的 DAG,读取源表以及调用 foo 方法都只有一次:

优化后的 DAG 效果跟我们写 SQL 时的逻辑图就非常像了,性能自然也都符合预期。
回到问题本身,业务上可以简单的用一条语句给视图的某些字段增加 trace,例如: prepare_data.trace.fields=f0,f1. 由于 SQL 天然包含了字段名,因此 trace 的数据可读性比普通日志还要高。
2.2 Table 的选型及设计
前面提到我们的首要需求是提高人效,因此需要 Table 有比较好的分层和复用的能力,支持模板化的开发,这样可以快速的串联起来端到端的 N 个 Flink 作业。
我们的解决方案是基于 Redis 实现,首先有几点好处:
接下来我们的场景,主要是解决多索引以及触发消息的问题。

上图举了一个学生在某个章节是否到课的表的例子:
这些功能都封装在 Redis Connector 里,业务上可以简单的通过 DDL 定义这么一个 Table 出来。

DDL 里几个比较重要的属性:
因此整个开发模式的复用性很强,用户可以很方便的开发出来端到端的 N 个 SQL 作业,也不用担心 case 如何追查的问题。
三、平台建设
上面的数据流架构搭建完成后,实时作业数在 2020.11 很快增加到了几百条,相比 19 年快了很多。这个时候我们开始从 0 到 1 搭建实时计算平台,接下来分享在搭建过程中的一些思考。

平台支持的功能,出发点主要有 3 个:
3.1 规范 - 实时任务流程管理
FlinkSQL 使得开发非常简单高效,但是越简单越难以规范,因为可能写一段 SQL 只用两个小时,但是走一遍规范下来得半天。

但是规范还是要执行,有些问题类似在线服务,实时计算里也会遇到:

规范主要分为三部分:
整个研发流程,是不能从线下私自修改的,比如更换 jar 包或者生效到哪个任务上。一个实时任务,即使运行上几年,也能够从当前任务找到谁上的线、谁审批的,当时的测试记录、对应 Git 代码,以及最最开始谁提出来的实时指标的需求,这样才能将任务长久的维护起来。
3.2 易用 - 监控
我们目前的 Flink 作业都运行在 Yarn 上。作业启动后,预期是 Prometheus 来抓取 Yarn 分配的 Container,然后对接报警系统,用户就可以基于报警系统配置 Kafka 延迟、Checkpoint 失败这些报警。在搭建这条通路时主要遇到了两个问题:

解决方案上:
四、总结展望
上一个阶段主要是在应用 Flink SQL 支持快速开发实时作业,以及搭建了实时计算平台,支持了上千条的 Flink 作业。
其中一个比较大的感悟是,SQL 确实简化了开发,但是同时也屏蔽了更多的技术细节。实时作业运维工具的需求比如 Trace,或者任务的规范这些并没有发生变化,甚至对这些的要求反而更加严格。因为屏蔽细节的同时,一旦出了问题,用户越不知道如何处理。就好像冰山一角,漏出来的越少,沉在水底的越多,你就越需要做好周边体系的建设。
另外一个就是适配现状,先能尽快满足当前需求,比如我们就是提高人效、降低开发门槛。同时也要不断探索更多业务场景,比如使用 HBase、RPC 服务替换 Redis Connector,现在的好处是修改底层存储,用户 SQL 作业感知很小,因为 SQL 作业里基本都是业务逻辑,而 DDL 定义到了元数据。

下一步规划主要分为三部分:
更多 Flink 相关技术问题,可扫码加入社区钉钉交流群第一时间获取最新技术文章和社区动态,请关注公众号~
