蚂蚁实时计算团队的Flink提交攻坚之路

简介: flink作业提交主要分为两个阶段client段和服务端,本文主要从客户端优化出发,讲述蚂蚁技术演讲方案

背景说明

  • Blink提交采用进程模型(包装flink info/run命令)进行作业执行计划的生成和作业的提交,这个基本是大数据计算引擎jstorm/spark/flink的共识,采用该方式的优点在于:
  • 简单:
  • 用户只需在自己的jar包中进行逻辑处理
  • 引擎client负责以方法调用形式调用用户main方法,然后编译、提交
  • 干净
  • 进程模型用户包用完销毁,引擎版本包通过目录隔离,不用考虑多版本问题。
  • 但这也到来了缺点,每次都得走一遍大量class 加载、校验等jvm启动全流程。同时,大多数作业的的执行计划生成耗时是在20秒以内,也就是说此时瓶颈不在编译阶段,此时jvm启动开销就成为了瓶颈。尤其当这些操作极其高频时,带来的开销不容小视。
  • 下图是blink作业模式下的plan生成和提交的耗时情况。


技术演进

JVM 进程冷启动层面优化

  • 下图展示的是一个典型的Java应用各模块执行时间的分布情况。从JVM启动到应用程序开始执行需要经历:VM加载,字节码文件加载,以及JIT(just in time)编译技术对解释执行的字节码进行优化,生成本地执行代码的过程,还需加上JVM内部垃圾回收所耗费的时间。 典型的Java应用加载时间通常是秒级起步,如果遇到比较大的应用初始花费几分钟都是正常的。

CDS

  • CDS全称是 Class-Data Sharing,其可以让类可以被预处理放到一个归档文件中,后续 Java 程序启动时将该归档文件映射到内存中,类加载器复用上一次进程启动时曾经加载、验证过的类信息,以节约应用启动的时间。换句话就是避免每次进程冷启动。

缺点:

通过CDS方案改进提交,虽然有些效果,但是总体上差强人意,核心缺点却不容忽视:

  • 仅能做到作业级别class复用
  • 原因:由于不同用户作业很大可能依赖不同版本的包,做class缓存时就会存在冲突
  • 间接带来的问题:
  • 每个作业需要缓存大量class,对brs服务磁盘带来巨大压力(单个作业缓存数据100MB以上)。
  • 引入CDS清理机制:由于blink作业操作并不复合近期操作原则,此时后续作业操作cds命中不了

AOT

  • AOT提前编译也不是一个新鲜的概念,在Java之前、很多其他的语言已经提出了AOT。DK9 引入了AOT(Ahead of Time) 编译技术,核心思想是在程序运行之前将class编译成native的code,程序运行阶段从原先的解释执行变成执行native code,从而减少冷启动问题。


缺点:

  • 有较多限制,同时业界并没有大规模使用:
  • 1、暂不支持自定义ClassLoader
  • 2、不支持CMS和ZGC
  • 在我们场景测试下来,效果并不好

常驻服务生成

  • 常驻服务生成方案:
  • 可以在blink rest server机器上,部署额外服务,负责生成plan/jobgraph(该服务和blink rest server同属一个进程)
  • 面临核心痛点问题:
  • 引擎版本间的兼容性没有保证,如何支持引擎多版本?
  • 方案1:多进程方案(每个版本额外启动一个常驻进程)
  • 为了更快速地支持业务需求、快速迭代,我们团队基本保持着半个月内发版的频率。
  • 为了尽快fix用户问题,也会发布临时版本
  • 基于此,该方案不可行,否则会有大量进场。
  • 方案2:多classloader方案
  • 每个版本一个classloader,通过classloader做jar包隔离。

引擎多版本classloader方案实现思路

  • 先做下简要背景说明,作业包可分为下面4种情况
  • flink/lib依赖包
  • launcher包:包涵和引擎交互的包。如plan/jobgraph的生成、资源plan apply到jobgraph中、热更新等
  • user jar用户jar包: 作业级别
  • connnector/backend插件包
  • 引擎当前为了支持平台包优先级高于引擎端而设计
  • 可以当做用户jar包来看

引入version classloader

  • 可以简单使用该classloader层级关系做隔离
  • 由于每个作业的user jar包不同,则version classloader没法复用
  • version classloader用完及释放,此时和进程模型相比也就没有太大区别,即性能会不好。


进一步引入reuqest level classloader

  • 思路:
  • 由于version级别的classloader,很少或者不变动,可复用。
  • request级别的classloader每次用完立即释放
  • 由于每个作业的用户jar不同,没法复用
  • launcher包的功能如何暴露给spring boot server(即blink rest server)使用呢?
  • spring boot server通过反射调用launcher包中的方法即可
  • 但是遵循一下规则即可:
  • 由于该spring boot server和flink打交道通过launcher包,暴露的方法参数务必注意只能是jdk的类
  • 假如暴露的参数使用的是开源库的类,哪怕version classloader和spring boot的app都有该jar包,但是此时类是不同的classloader加载了,会导致LinkageError问题。
  • 优点:
  1. version classloader和spring boot的app classloader没有继承关系,做到了干净隔离,因此该spring boot可以随便依赖flink、甚至blink或者其他依赖,并不影响该服务
  2. 将version classloader cache起来,复用率非常高
  • 当同版本更新发布或者测试环境希望仅仅更新某变更jar包做验证时,通过监听版本包目录jar包变更,让classloader缓存失效,重新构建即可。



思考

  • 为啥hive/spark/flink计算引擎都是通过自定义classloader方案,不采用类似上面的方案,如下图1所示呢?
  • 自定义classloader本质上是想解决用户jar和引擎包冲突的问题
  • 但是用户包和引擎包的交互:
  • 1) 不可能像上面方案交互是单向的
  • 上面方案:spring boot server仅单向访问引擎launcher提供的接口
  • 而对于计算引擎来说,user code访问引擎代码,引擎代码依赖user code的返回值是不可避免的。
  • 2) 不可能像上面方案约束暴露的方法参数必须为jdk的类
  • 否则用户用起来一定很不爽
  • 基于上面两点,计算引擎自然不可能使用下面图1方案,而是图2方案。


图1:flink classloader方案改造



图2:flink 自定义classloader 引擎当前方案


  • 那么计算引擎使用图2的方案存在什么问题呢?
  • LinkageError无法避免
  • 由于相互交互,同一个类被不同的classloader加载然后相互引用,细节见笔者分析的文档链接
  • 虽然flink对此做了很多改进,但是该问题无法根本解决
  • 比如引擎已经约束好哪些包是必须交给app classloader加载,防止被user classloader加载,那么相互引用就不会有问题
  • 但不可能放进去很多,否则不同版本三方包冲突问题不就随之而来。所以又暴露了用户级别配置,用户作业运行时报LinkageError问题,用户把对应的包路径塞入配置即可。但如果两个classloader比如需要且引用,则没有办法解
  • ClassNotFoundExceotion报错诡异,让人困惑
  • 一般地,用户插件包该错,很简单,user jar打上依赖即可
  • 但是有些情况,就比较绕。
  • 先铺垫下基础知识, classloader类加载机制3原则:
  1. 全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入,如class.forName(, classloader)。
  2. 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。
  3. 缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。


  • 举例:A, B, C三个类依赖关系如下图,但是类B对应的jar在两个classloader中都有。
  • 此时B在进程启动时,已经被父classloader加载。然后调用user code时,调用了A -> B -> C。由于B已经被父classloader加载,根据全盘负责原则此时C将交给父classloader加载,而父classloader没有该C的jar包,则报ClassNotFoundExceotion。
  • 但是用户就很困惑,调用链明明是我的代码,而且我的包中已经有该class,为什么会报这个错呢
  • 解决办法
  • 将B从父classpath去除。不可行,这样父classloader在进程启动前,就报ClassNotFoundExceotion了
  • 对user code中B 做shade改包名,一般该解法可行。但是比较trick的是用户代码依赖的B不是依赖形式使用,而是以hard code编码方式。如果让用户改动依赖代码,就很麻烦。
  • 最终临时是将该依赖打入到父classpath。但是对于引擎来说,就会有较大改动。如果是广泛使用的包,又会很容易和其他用户作业冲突。


效果

  • 通过多版本classloader方案优化后,经测试简单作业plan耗时从10秒降低到1秒以内,有数量级级别的提升
  • 同时,从背景说明章节的图中可看到绝大多数作业都为简单作业

作业提交和jobgraph生成解耦

  • blink/flink核心在于jobgraph,而session/perjob/application模式核心仅仅在于生成job graph的位置不同、是否支持多作业而已。具体细节见笔者之前写的文档链接
  • blink 采用single job的session模式,提交作业时先拉起JobManager,然后同步方式等pod拉起之后(拉起需要申请pod比较耗时),之后在编译作业生成jobgraph。如果发现不兼容再退出JM作业,则前面耗时的工作白做了
  • 基于此,我们实现flink支持k8s per job模式,解耦作业提交和jobgraph生成。在客户端提前生成jobgraph,如果不兼容直接报错了,无需拉起JobManager。
  • 解耦后,可以做很多优化。运维态不变更作业。可以直接复用已经生成的jobgraph,无需再重复生成等。
  • 同时,为了统一代码栈,降低开发成本,也扩展datastream作业支持per job模式提交


结语

  • 提交主要分为两个阶段client段和服务端,本文主要从客户端优化出发,对于服务端提交优化,蚂蚁实时计算团队还做了其他大量工作,如镜像加速(镜像拉取的优化)、集群模式(申请TaskManager资源时,不走sigma镜像方式,直接起进程方式)、热更新(对用户作业不修改情况下,不走整个提交流程,复用k8s的flink集群)。


参考文档链接:

[1]https://www.yuque.com/g/jackylau-sc7w6/bve18l/rgy8y7e47abmw17c/collaborator/join?token=dGXoLPcmNkj0ILEP#%20《包冲突常见解法》

[2]https://www.yuque.com/g/jackylau-sc7w6/bve18l/tui3w27uq7rush3o/collaborator/join?token=oVoqrCtZYs3152jY#%20《Flink%20JobGraph核心信息》

相关实践学习
基于Hologres+Flink搭建GitHub实时数据大屏
通过使用Flink、Hologres构建实时数仓,并通过Hologres对接BI分析工具(以DataV为例),实现海量数据实时分析.
实时计算 Flink 实战课程
如何使用实时计算 Flink 搞定数据处理难题?实时计算 Flink 极客训练营产品、技术专家齐上阵,从开源 Flink功能介绍到实时计算 Flink 优势详解,现场实操,5天即可上手! 欢迎开通实时计算 Flink 版: https://cn.aliyun.com/product/bigdata/sc Flink Forward Asia 介绍: Flink Forward 是由 Apache 官方授权,Apache Flink Community China 支持的会议,通过参会不仅可以了解到 Flink 社区的最新动态和发展计划,还可以了解到国内外一线大厂围绕 Flink 生态的生产实践经验,是 Flink 开发者和使用者不可错过的盛会。 去年经过品牌升级后的 Flink Forward Asia 吸引了超过2000人线下参与,一举成为国内最大的 Apache 顶级项目会议。结合2020年的特殊情况,Flink Forward Asia 2020 将在12月26日以线上峰会的形式与大家见面。
相关文章
|
存储 分布式计算 数据处理
「48小时极速反馈」阿里云实时计算Flink广招天下英雄
阿里云实时计算Flink团队,全球领先的流计算引擎缔造者,支撑双11万亿级数据处理,推动Apache Flink技术发展。现招募Flink执行引擎、存储引擎、数据通道、平台管控及产品经理人才,地点覆盖北京、杭州、上海。技术深度参与开源核心,打造企业级实时计算解决方案,助力全球企业实现毫秒洞察。
1025 0
「48小时极速反馈」阿里云实时计算Flink广招天下英雄
|
SQL 运维 Java
蚂蚁 Flink 实时计算编译任务 Koupleless 架构改造
本文介绍了对Flink实时计算编译任务的Koupleless架构改造。为解决进程模型带来的响应慢、资源消耗大等问题,团队将进程模型改为线程模型,并借助Koupleless的类加载隔离能力实现版本和包的隔离。通过动态装配Plugin及其Classpath,以及Biz运行时仅对依赖Plugin可见的设计,大幅优化了编译任务的性能。结果表明,新架构使编译耗时降低50%,吞吐量提升5倍以上。
蚂蚁 Flink 实时计算编译任务 Koupleless 架构改造
zdl
|
消息中间件 运维 大数据
大数据实时计算产品的对比测评:实时计算Flink版 VS 自建Flink集群
本文介绍了实时计算Flink版与自建Flink集群的对比,涵盖部署成本、性能表现、易用性和企业级能力等方面。实时计算Flink版作为全托管服务,显著降低了运维成本,提供了强大的集成能力和弹性扩展,特别适合中小型团队和业务波动大的场景。文中还提出了改进建议,并探讨了与其他产品的联动可能性。总结指出,实时计算Flink版在简化运维、降低成本和提升易用性方面表现出色,是大数据实时计算的优选方案。
zdl
897 56
|
存储 关系型数据库 BI
实时计算UniFlow:Flink+Paimon构建流批一体实时湖仓
实时计算架构中,传统湖仓架构在数据流量管控和应用场景支持上表现良好,但在实际运营中常忽略细节,导致新问题。为解决这些问题,提出了流批一体的实时计算湖仓架构——UniFlow。该架构通过统一的流批计算引擎、存储格式(如Paimon)和Flink CDC工具,简化开发流程,降低成本,并确保数据一致性和实时性。UniFlow还引入了Flink Materialized Table,实现了声明式ETL,优化了调度和执行模式,使用户能灵活调整新鲜度与成本。最终,UniFlow不仅提高了开发和运维效率,还提供了更实时的数据支持,满足业务决策需求。
|
消息中间件 关系型数据库 MySQL
Flink CDC 在阿里云实时计算Flink版的云上实践
本文整理自阿里云高级开发工程师阮航在Flink Forward Asia 2024的分享,重点介绍了Flink CDC与实时计算Flink的集成、CDC YAML的核心功能及应用场景。主要内容包括:Flink CDC的发展及其在流批数据处理中的作用;CDC YAML支持的同步链路、Transform和Route功能、丰富的监控指标;典型应用场景如整库同步、Binlog原始数据同步、分库分表同步等;并通过两个Demo展示了MySQL整库同步到Paimon和Binlog同步到Kafka的过程。最后,介绍了未来规划,如脏数据处理、数据限流及扩展数据源支持。
1197 0
Flink CDC 在阿里云实时计算Flink版的云上实践
|
存储 监控 大数据
阿里云实时计算Flink在多行业的应用和实践
本文整理自 Flink Forward Asia 2023 中闭门会的分享。主要分享实时计算在各行业的应用实践,对回归实时计算的重点场景进行介绍以及企业如何使用实时计算技术,并且提供一些在技术架构上的参考建议。
1862 7
阿里云实时计算Flink在多行业的应用和实践
|
运维 数据处理 数据安全/隐私保护
阿里云实时计算Flink版测评报告
该测评报告详细介绍了阿里云实时计算Flink版在用户行为分析与标签画像中的应用实践,展示了其毫秒级的数据处理能力和高效的开发流程。报告还全面评测了该服务在稳定性、性能、开发运维及安全性方面的卓越表现,并对比自建Flink集群的优势。最后,报告评估了其成本效益,强调了其灵活扩展性和高投资回报率,适合各类实时数据处理需求。
|
存储 分布式计算 流计算
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
本文介绍了阿里云开源大数据团队在实时计算领域的最新成果——向量化流计算引擎Flash。文章主要内容包括:Apache Flink 成为业界流计算标准、Flash 核心技术解读、性能测试数据以及在阿里巴巴集团的落地效果。Flash 是一款完全兼容 Apache Flink 的新一代流计算引擎,通过向量化技术和 C++ 实现,大幅提升了性能和成本效益。
4825 74
实时计算 Flash – 兼容 Flink 的新一代向量化流计算引擎
|
SQL 消息中间件 Kafka
实时计算 Flink版产品使用问题之如何在EMR-Flink的Flink SOL中针对source表单独设置并行度
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。

热门文章

最新文章