stormtrooper_stormbringer_jstorm

背景说明

Blink提交采用进程模型(包装flink info/run命令)进行作业执行计划的生成和作业的提交,这个基本是大数据计算引擎jstorm/spark/flink的共识,采用该方式的优点在于:

但这也带来了缺点,每次都得走一遍大量class 加载、校验等jvm启动全流程。同时,大多数作业的的执行计划生成耗时是在20秒以内,也就是说此时瓶颈不在编译阶段,此时jvm启动开销就成为了瓶颈。尤其当这些操作极其高频时,带来的开销不容小视。

下图是blink作业模式下的plan生成和提交的耗时情况。

技术演进

JVM 进程冷启动层面优化

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

stormbringer_stormtrooper_jstorm

CDS

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

缺点:

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

仅能做到作业级别class复用:

AOT

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

stormtrooper_stormbringer_jstorm

缺点:

1、有较多限制,同时业内并没有大规模使用:

2、在我们场景测试下来,效果并不好

常驻服务生成

1、常驻服务生成方案:

2、面临核心痛点问题:

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

先做下简要背景说明,作业包可分为下面4种情况

引入version classloader

可以简单使用该classloader层级关系做隔离

stormtrooper_stormbringer_jstorm

进一步引入reuqest level classloader

思路:

launcher包的功能如何暴露给spring boot server(即blink rest server)使用呢?

优点:

version classloader和spring boot的app classloader没有继承关系,做到了干净隔离,因此该spring boot可以随便依赖flink、甚至blink或者其他依赖,并不影响该服务;

将version classloader cache起来,复用率非常高;

stormtrooper_jstorm_stormbringer

思考

为啥hive/spark/flink计算引擎都是通过自定义classloader方案,不采用类似上面的方案,如下图1所示呢?

jstorm_stormbringer_stormtrooper

图1:flink classloader方案改造

jstorm_stormtrooper_stormbringer

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

那么计算引擎使用图2的方案存在什么问题呢?

1、LinkageError无法避免

2、ClassNotFoundExceotion报错诡异,让人困惑

全盘负责:所谓全盘负责,就是当一个类加载器负责加载某个Class时,该Class所依赖和引用其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入,如class.forName(, classloader)。

双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。

缓存机制:缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。这就是为很么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

stormtrooper_stormbringer_jstorm

效果

通过多版本classloader方案优化后,经测试简单作业plan耗时从10秒降低到1秒以内,有数量级级别的提升。

作业提交和jobgraph生成解耦

blink/flink核心在于jobgraph,而session/perjob/application模式核心仅仅在于生成job graph的位置不同、是否支持多作业而已。具体细节见笔者之前写的文档链接[2]。

blink 采用single job的session模式,提交作业时先拉起JobManager,然后同步方式等pod拉起之后(拉起需要申请pod比较耗时),之后在编译作业生成jobgraph。如果发现不兼容再退出JM作业,则前面耗时的工作白做了。

stormtrooper_jstorm_stormbringer

结语

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

参考文档链接:

[1]#%20《包冲突常见解法》

[2]#%20《Flink%20JobGraph核心信息》

参与ImageSearch 图像搜索评测,赢取Kindle Paperwhite4、评测局定制卫衣、云小宝帆布包、图搜6个月免费试用等多重好礼

限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注