共计 5520 个字符,预计需要花费 14 分钟才能阅读完成。
摘要:本文整顿自作业帮实时计算负责人张迎在 Flink Forward Asia 2021 的分享。在作业帮实时计算演进过程中,Flink 起到了重要的作用,特地是借助于 FlinkSQL 极大的进步了实时工作的开发效率。这篇文章次要分享 FlinkSQL 在作业帮的应用状况、实践经验,以及随着工作规模增长,在从 0 到 1 搭建实时计算平台的过程中遇到的问题及解决方案。内容包含:
- 倒退历程
- Flink SQL 利用实际
- 平台建设
- 总结瞻望
FFA 2021 直播回放 & 演讲 PDF 下载
一、倒退历程
作业帮次要使用人工智能、大数据等技术,为学生提供更高效的学习解决方案。因而业务上的数据,次要是学生的到课状况、知识点把握的状况这些。整体架构上,无论是 binlog 还是一般日志,通过采集后写入 Kafka,别离由实时和离线计算写入存储层,基于 OLAP 再对外提供对应的产品化服务,比方工作台、BI 剖析工具。
作业帮的实时计算目前根本以 Flink 为主,倒退历程大略有三个阶段:
- 19 年,实时计算蕴含大量的 SparkStreaming 作业,提供到辅导老师、主讲侧。在解决实时需要的过程中,就会发现开发效率很低,数据简直无奈复用;
- 之后惯例的做法,是在生产实践中逐渐利用 Flink JAR,积攒教训后开始搭建平台以及利用 Flink SQL。不过在 20 年,业务提出了十分多的实时计算需要,而咱们开发人力储备有余。过后 Flink SQL 1.9 公布不久,SQL 性能变动较大,所以咱们的做法是间接在实时数仓方向利用 Flink SQL,目前整个实时数仓超过 90% 的工作都是应用 Flink SQL 实现的;
- 到了 20 年 11 月份,Flink 作业很快减少到几百条,咱们开始从 0 到 1 搭建实时计算平台,曾经反对了公司全副重要的业务线,计算部署在多个云的多个集群上。
接下来介绍两个方面:
- FlinkSQL 实际遇到的典型问题以及解决方案;
- 实时计算平台建设过程中的一些思考。
二、Flink SQL 利用实际
这是基于 Flink SQL 的残缺数据流架构:
binlog/log 采集写入 Kafka 后,topic 会主动注册成为元数据的一张表,这是后续所有实时 SQL 作业的终点。用户能够在 SQL 作业里应用这个表,而不必定义简单的 DDL。
同时,思考理论利用时,也须要在元数据表的根底上,可能对表属性进行新增或者替换:
- 新增:元数据记录的是表级别的属性,然而 SQL 作业里可能须要减少工作级别的属性。比方对于 Kafka 源表,减少作业的 group.id 来记录 offset;
- 替换:线下测试时,在援用元数据表的根底上,只须要定义 broker topic 等属性笼罩源表,这样能够疾速的构建一个线下测试表。
框架也须要反对用户的 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/jir… https://issues.apache.org/jira/browse/FLINK-18840
从上图下方的理论 DAG 看不太合乎预期:
- DAG 被分成了高低不相干的两局部,Kafka 源表也就是 DataSource 局部,读取了两次;
- foo 办法调用了三次。
数据源压力以及计算性能都须要优化。
解决这个问题须要从几个角度别离优化,这里次要介绍下 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 实现,首先有几点益处:
- 高 qps、低提早:这个应该是所有实时计算都关注的;
- TTL:用户不必关怀数据如何登场,给定一个正当的 TTL 就能够了;
- 通过应用 protobuf 等高性能且紧凑的序列化形式,以及应用 TTL,存储上整体不到 200G,redis 的内存压力能够承受;
- 贴合计算模型:计算自身为了确保时序性,会进行 keyBy 的操作,把须要同时解决的数据 shuffle 到同一并发上,因而也不依赖存储过多思考锁的优化。
接下来咱们的场景,次要是解决多索引以及触发音讯的问题。
上图举了一个学生在某个章节是否到课的表的例子:
- 多索引:数据首先依照 string 格局存储,比方 key=(uid, lesson_id), value=serialize(is_attend, …),这样咱们就能够在 SQL 里 JOIN ON uid AND lesson_id 了。如果 JOIN ON 其余字段,比方 lesson_id 怎么办?咱们的做法,是会同时写入一个 lesson_id 为 key 的 set,set 里的元素是对应的 (uid, lesson_id)。接下来查找 lesson_id = 123 时,先取出该 set 下所有元素,而后再通过 pipeline 的形式查找到所有的 VALUE 返回;
- 触发音讯:写入 redis 后,会同时写入一条更新音讯到 Kafka. 两个存储之间的一致性、程序性、不丢数据都在 Redis Connector 的实现里保障。
这些性能都封装在 Redis Connector 里,业务上能够简略的通过 DDL 定义这么一个 Table 进去。
DDL 里几个比拟重要的属性:
- primary 定义了主键,对应 string 的数据结构,例如例子里的 uid + lesson_id;
- index.fields 定义了辅助查找的索引字段,例如例子里的 lesson_id;索引也能够定义多个;
- poster.kafka 定义接管触发音讯的 kafka 表,这个表同样定义在了元数据,用户能够在后续的 SQL 作业里无需定义间接读取该表。
因而整个开发模式的复用性很强,用户能够很不便的开发进去端到端的 N 个 SQL 作业,也不必放心 case 如何追究的问题。
三、平台建设
下面的数据流架构搭建实现后,实时作业数在 2020.11 很快减少到了几百条,相比 19 年快了很多。这个时候咱们开始从 0 到 1 搭建实时计算平台,接下来分享在搭建过程中的一些思考。
平台反对的性能,出发点次要有 3 个:
- 对立:对立不同云厂商不同的集群环境、Flink 版本、提交形式等;之前 hadoop 客户端散落在用户的提交机上,对集群数据、工作平安都有隐患,同时减少了集群后续的降级、迁徙老本。咱们心愿通过平台对立工作的提交入口以及提交形式;
- 易用:通过平台交互可能提供更多易用性能,比方调试、语义检测,这些都能进步工作测试的人效,以及记录工作的版本历史反对不便的上线及回滚操作;
- 标准:权限管制、流程审批等,相似于在线服务的上线流程,通过平台,可能把实时工作的研发流程标准起来。
3.1 标准 – 实时工作流程治理
FlinkSQL 使得开发非常简单高效,然而越简略越难以标准,因为可能写一段 SQL 只用两个小时,然而走一遍标准下来得半天。
然而标准还是要执行,有些问题相似在线服务,实时计算里也会遇到:
- 记不清:工作在线上跑了一年,最后的需要可能是口口相传,好一点记了 wiki 或者邮件,然而都容易在工作交接中记不清楚;
- 不标准:UDF 也好,DataStream 的代码也好,都没有恪守标准,可读性差,导致前面接手的同学降级改不动、或者不敢改,没法短暂的保护上来。包含实时工作的 SQL 怎么写也应该有标准;
- 找不到:线上运行中的工作,依赖了某个 jar,对应的是哪个 git 模块的哪个 commitId,出了问题怎么第一工夫找到对应的代码实现;
- 瞎批改:始终失常的工作,周末忽然报警了,起因是擅自批改了线上工作的 SQL。
标准次要分为三局部:
- 开发:RD 能够从 UDF archetype 我的项目上疾速创立一个 UDF 模块,这个是参考了 flink quickstart。创立进去的 UDF 模块能够失常编译,蕴含了相似 WordCount 这种 udf 示例,也有默认的 ReadMe、VersionHelper 这些辅助办法。依照业务需要批改后,通过 CR 上传到 Git;
- 需要治理、编译:提交的代码会关联到需要卡片上,通过集群编译、QA 测试,能力发单上线;
- 上线:依据模块及编译产出,抉择更新 / 创立哪些作业,通过作业 owner 或者 leader 审批后,重新部署。
整个研发流程,是不能从线下擅自批改的,比方更换 jar 包或者失效到哪个工作上。一个实时工作,即便运行上几年,也可能从当前任务找到谁上的线、谁审批的,过后的测试记录、对应 Git 代码,以及最最开始谁提出来的实时指标的需要,这样能力将工作短暂的保护起来。
3.2 易用 – 监控
咱们目前的 Flink 作业都运行在 Yarn 上。作业启动后,预期是 Prometheus 来抓取 Yarn 调配的 Container,而后对接报警零碎,用户就能够基于报警系统配置 Kafka 提早、Checkpoint 失败这些报警。在搭建这条通路时次要遇到了两个问题:
- PrometheusReporter 启动 HTTPServer 后,Prometheus 怎么能动静感知;也须要可能管制 metric 的大小,防止采集大量无用数据;
- 咱们 SQL 的源表,根本是以 Kafka 为主。相比第三方的工具,在计算平台上配置 Kafka 提早报警会更加不便。因为可能人造的拿到工作读取的 topic、group.id,同时也能够跟工作失败应用同一个报警组。再配合上报警模板,配置报警十分简便。
解决方案上:
- 在官网 PrometheusReporter 的根底上减少了 discovery 的性能。Container 的 HTTPServer 启动后,把对应的 ip:port 以长期节点的模式注册到 zk 上,而后利用 Prometheus 的 discover targets 监听 zk 节点的变动。因为是长期节点,Container 销毁时节点隐没,Prometheus 也可能感知不再抓取。这样就很简便的搭建起来 Prometheus 抓取的通路。
- KafkaConsumer.records-lag 是比拟实用、重要的提早指标,次要做了两个工作。批改 KafkaConnector,在 KafkaConsumer.poll 之后再 expose 进去,确保 records-lag 指标可见。另外在做这个的过程中,发现不同 Kafka 版本的这个指标格局不同(https://cwiki.apache.org/conf…),咱们的做法是都打平为一种格局,注册到 flink 的 metrics 里。这样不同版本裸露进去的指标是统一的。
四、总结瞻望
上一个阶段次要是在利用 Flink SQL 反对疾速开发实时作业,以及搭建了实时计算平台,反对了上千条的 Flink 作业。
其中一个比拟大的感悟是,SQL 的确简化了开发,然而同时也屏蔽了更多的技术细节。实时作业运维工具的需要比方 Trace,或者工作的标准这些并没有发生变化,甚至对这些的要求反而更加严格。因为屏蔽细节的同时,一旦出了问题,用户越不晓得如何解决。就如同冰山一角,漏出来的越少,沉在水底的越多,你就越须要做好周边体系的建设。
另外一个就是适配现状,先能尽快满足以后需要,比方咱们就是进步人效、升高开发门槛。同时也要一直摸索更多业务场景,比方应用 HBase、RPC 服务替换 Redis Connector,当初的益处是批改底层存储,用户 SQL 作业感知很小,因为 SQL 作业里根本都是业务逻辑,而 DDL 定义到了元数据。
下一步布局次要分为三局部:
- 反对资源弹性伸缩,均衡实时作业的老本以及时效性;
- 咱们是从 1.9 开始大规模利用 Flink SQL 的,当初版本升级变化很大,须要思考如何让业务可能低成本的降级应用新版本里 feature;
- 摸索流批一体在理论业务场景上的落地。
FFA 2021 直播回放 & 演讲 PDF 下载
更多 Flink 相干技术问题,可扫码退出社区钉钉交换群
第一工夫获取最新技术文章和社区动静,请关注公众号~