一、背景
早在2015年的时候,为了满足业务数据在大数据组件和传统数据库之间的搬运需求,数据中心的前身部门开发了第一代的数据搬运工具,当时命名为XDATA。在随后的时间,XDATA经过几次改造,演变成一Master节点,多 Worker节点的架构。一定程度上具备了横向扩展的能力。
二、老系统的问题
第二代的XDATA比较好的支撑了业务数据搬运的需求。但同时有着如下的问题:
1、资源问题
随着业务接入数量越来越多,并发任务越来越多的情况下,Worker数量也会比较多。这部分资源只能被XDATA任务使用,资源不能得到有效利用。
2、任务并行能力差
每个Worker提供了15个线程来执行数据搬运的具体任务。对于二代XDATA的一个任务,读取数据的插件运行于一个线程上,写插件运行于另一个线程,通过一个有限的数据队列,形成一个生产者消费者模式的模型。对于一些数据量特别大的任务,无法通过并行度来提高任务运行效率。有一些任务的运行时间甚至达到了20个小时以上。
3、运维压力大
没有资源的动态伸缩机制支持。在集群资源不够或者过多的情况下,需要人工申请调整负载,再通过运维人员操作实现。同时项目发布更新流程复杂。集群需要更新的时候,为了避免对运行任务的影响,必须先关闭任务提交入口,然后等待运行任务结束后,才可以进行发布更新。
三、新系统介绍
基于以上问题,我们开始着手开发新的系统。
关于资源问题,我们选择了Yarn。首先是因为我们已经运行着多个Yarn集群,总共有着40000+cores,100T+内存的资源池。然后Yarn还提供了比较好的资源隔离和伸缩功能。通过Yarn管理资源,我们可以很灵活的给不同的任务申请不同的资源。而通过Yarn队列,可以提供一定的任务隔离功能,保证一些重要的任务不会因为资源问题不能按时的完成。
为了解决任务内部并行处理的问题,我们研究之后决定采用Flink作为我们任务执行的引擎。首先Flink提供了完整的并行,容错,监控机制,还原生支持在Yarn集群上运行。同时,我们对于Flink项目在推进的Flink SQL模块和批流统一的功能有一定的期待,后续我们也希望能够通过Flink SQL进行任务的配置。 最后我们团队的小伙伴对Flink的研究还是比较不错的,为了方便后续问题的处理和功能的扩展,就决定使用基于Flink进行数据搬运功能的开发。
按照我们搬运工具的命名传统,新的系统命名为XFlink。
上图是xflink任务的用户配置界面。以从hdfs搬运到redis的任务为例,一共分三步: 1、选择数据源:分别选择数据来源hdfs和数据目标redis,完成相应配置信息的填写(如图中的hdfs集群,redis组名等选项); 2、字段映射:每条结构化的数据会被分成多个字段,所以用户需要配置好hdfs和redis中一一对应的字段,避免字段错位等情况的发生; 3、保存任务,手动执行或者放入项目调度当中。
XFlink是一个单纯的任务执行框架。在完成上述的任务配置后,任务具体的维护和调度由Jarvis平台(数据中心自研的大数据任务调度系统)负责。
下图是系统中各个模块之间的关系,Manager是一个Rest服务,它向Jarvis提供调度所需的接口服务(运行/停止任务、任务生命周期轮训等),并将任务生命周期信息维护在Mysql数据库中。Deploy模块负责将任务配置信息通过我们的一个Parser模块解析成具体的Flink Job的JobGraph,并向提交到指定的Yarn集群运行。任务运行结束后的所有信息也可以通过Flink history server暴露给用户。
XFlink模块图
下面介绍一下系统中比较重要的模块和功能。
四、Core 模块
1、Plugin
(1)读写插件
org.apache.flink.api.common.io包中的InputFormat和OutputFormat是Flink API中定义如何读取源数据和如何存储数据的基础接口。分别对应我们系统中的读插件和写插件。我们基于这2个接口,开发了我们自己的读写插件基类,额外提供了额外的功能。这里需要关注2点,一个是写插件的幂等性和读插件的并发能力。
写插件的幂等性通过目标数据源的主键来支持。即在所搬运的数据字段中,需要包含在目标数据源中的主键字段,比如ElasticSearch的_id字段,Hbase的rowkey字段等。系统不提供额外的机制来实现数据插入的幂等性。
读插件的并行能力是数据搬运效率很重要的一环。Flink在机制上支持读插件的并行,但是在实现中却存在一些限制。比如1.x和2.x版本的ES,读取时可以根据某个用户设置的数值型字段,取最大最小值,然后按照并行度,切分成不同的查询语句实现并行读取数据。但是该机制存在问题,比如该数值字段的分布不均匀,导致一些分区的数据量特别大,数据产生的倾斜会严重影响并行的效率。而对于5.x以上版本的ES,则可以使用Slice scroll方式进行并行读取。
当前支持组件
(2)统计
在基类中,我们使用accumulator中的LongCounter来统计读插件成功读取到的数据条数,写插件写入成功的数据条数和失败条数。这些统计数据会在任务结束后呈现给业务使用方,帮助他们了解任务完成的情况。
(3)异常数据归档
在数据搬运过程中,会出现一些数据类型,大小上的一些异常,导致数据不能成功被搬运。我们会把这些数据统一写入到一个目录中,供业务使用方查询,帮助他们排查数据异常的具体情况。同时我们还支持在异常数据达到一定的数量后,停止搬运任务。
(4)写插件限流
在写插件中使用google guava中的RateLimiter组件,限制写插件向目标数据源写数据的速度,避免因为搬运任务影响到数据源的性能和稳定性。
2、Parser
Parser模块是生成Flink Job的主程序。所有用户配置的关于读写插件的参数都会由Parser模块解析并生成对应的InputFormat,OutputFormat的具体实现。并最终按照搬运的逻辑生成一个Flink任务数据处理逻辑。搬运逻辑的组装过程如下图:
五、Deploy模块
Flink官方包提供了使用脚本提交任务的方式。我们并没有使用这种方式,而是基于其逻辑,做了一些改造,使其成为一个单独的常驻的服务。主要功能是处理任务提交请求,并以Single Job集群和Detach模式将任务提交到指定的Yarn集群。
1、租户配置
Flink内部使用YarnClient处理和Yarn集群的RPC交互。而YarnClient默认会使用环境变量HADOOPUSERNAME指定的租户和Yarn交互(环境变量未配置则使用当前应用的启动用户),即Yarn Application的用户就是环境变量HADOOPUSERNAME配置的用户,而不是用户指定的租户。不方便我们后续关于租户的统计和分析。我们的解决办法是使用用户配置的代理用户,生成一个新的UserGroupInformation对象,并使用该UGI作为权限对象创建YarnClient对象和创建YarnApplication对象。
2、支持多Yarn集群
Flink使用YARNCONFDIR环境变量来加载Yarn集群的配置文件,无法动态的指定提交到的Yarn集群。公司按照业务和Hadoop版本划分了几个Yarn集群,每个Yarn集群会有一个唯一的标识付。用户可以通过标识符指定希望任务运行的Yarn集群。我们把多个集群的配置文件添加后缀,同样存储在YARNCONFDIR指定的目录内。系统初始化时加载这些配置文件,以YarnConfiguration对象形式缓存起来,并对应的生成多个YarnClient对象,支持向不同Yarn集群提交任务。
六、监控
目前系统的监控分为2个模块。一个是Flink任务的监控,一个是任务提交模块的监控。都是使用了Prometheus+PushGateway+Grafana的体系。
对于Flink任务的监控,使用了Flink系统自带的Metrcis系统。通过在Flink任务中配置的Prometheus Gateway Reporter,Flink定时地向Prometheus推送监控数据。最后通过Grafana制作图表展示。
Flink的metrcis系统指标还是比较齐全的,用户也可以通过其API自定义一些指标。下图展示了一个Flink任务中,某个TaskManager的GC和内存使用情况。
对于任务的提交,我们使用了dropwizard定义了一些指标,通过一个定时Job把监控数据推送到Prometheus。比如说提交任务延迟该指标统计了从接到任务提交请求到任务真正提交到Yarn平台上的时间,帮助我们监控任务提交至Yarn的过程是否正常。还有比如提交任务线程池队列的大小,用来帮助我们监控一段时间内提交任务的数量是否达到了系统的瓶颈。
七、后续功能
1、以Flink SQL进行任务配置
对于数据相关从业者,SQL应该是最熟悉的工具,使用SQL进行相关任务的配置也会极大方便他们的使用。目前Insert into和Select两个语义就可以满足当前搬运任务的功能,后续通过其它SQL语义和UDF,还可以在搬运数据功能之外,提供一定的数据清洗功能。Flink社区也一直在完善Flink SQL的相关功能,我们会持续保持关注。
2、模块化插件功能
目前系统的读取和写入插件都是由我们团队的小伙伴开发,这些插件满足了通用的,基本的数据搬运需求。而业务是多变的,时不时的业务方会有一些定制化的需求。我们会提供开发接口,提供给业务方实现自己的需求。通过我们的审核后,通过SPI机制加载到系统中。最终满足业务方灵活多变的需求。
浙公网安备 33010602011771号