本文共 7032 字,大约阅读时间需要 23 分钟。
下面是目前饿了么平台现状架构图:
来源于多个数据源的数据写到kafka里,计算引擎主要是Storm,Spark和Flink,计算引擎出来的结果数据再落地到各种存储上。
目前Storm任务大概有100多个,Spark任务有50个左右,Flink暂时还比较少。
目前我们集群规模每天数据量有60TB,计算次数有1000000000,节点有400个。这里要提一下,Spark和Flink都是on yarn的,其中Flink onyarn主要是用作任务间jobmanager隔离, Storm是standalone模式。
在讲述我们应用场景之前,先强调实时计算一个重要概念, 一致性语义:
饿了么早期都是使用Storm,16年之前还是Storm,17年才开始有Sparkstreaming, Structed-streaming。Storm用的比较早,主要有下面几个概念:
有一天有个业务方过来提需求说 我们能不能写个sql,几分钟内就可以发布一个实时计算任务。 于是我们开始做Sparkstreaming。它的主要概念如下:
下面给大家大致展示一下我们平台用户快速发布一个实时任务的操作页面,它需要哪些步骤。我们这里不是写DDL和DML语句,而是ui展示页面的方式。
页面里面会让用户选一些必要的参数, 首先会选哪一个kafka集群,每个分区消费多少,反压也是默认开启的。消费位置需要让用户每次去指定,有可能用户下一次重写实时任务的时候,可以根据业务需求去选择offset消费点。
中间就是让用户描述pipeline。 SQL就是kafka的多个topic,输出选择一个输出表,SQL把上面消费的kafka DStream注册成表,然后写一串pipeline,最后我们帮用户封装了一些对外sink(刚刚提到的各种存储都支持,如果存储能实现upsert语义的话,我们都是支持了的)。
虽然刚刚满足一般无状态批次内的计算要求,但就有用户想说, 我想做流的join怎么办, 早期的Spark1.5可以参考Spark-streamingsql这个开源项目把 DStream注册为一个表,然后对这个表做join的操作,但这只支持1.5之前的版本,Spark2.0推出structured streaming之后项目就废弃了。我们有一个tricky的方式:
让Sparkstreaming去消费多个topic,但是我根据一些条件把消费的DStream里面的每个批次RDD转化为DataFrame,这样就可以注册为一张表,根据特定的条件,切分为两张表,就可以简单的做个join,这个join的问题完全依赖于本次消费的数据,它们join的条件是不可控的,是比较tricky的方式。比如说下面这个例子,消费两个topic,然后简单通过filer条件,拆成两个表,然后就可以做个两张表的join,但它本质是一个流。
exactly-once需要特别注意一个点:
我们必须要求数据sink到外部存储后,offset才能commit,不管是到zk,还是mysql里面,你最好保证它在一个transaction里面,而且必须在输出到外部存储(这里最好保证一个upsert语义,根据unique key来实现upset语义)之后,然后这边源头driver再根据存储的offeset去产生kafka RDD,executor再根据kafka每个分区的offset去消费数据。如果满足这些条件,就可以实现端到端的exactly-once. 这是一个大前提。
###4. STRUCTUREDSTREAMING
我们调研然后并去使用了Spark2.X之后带状态的增量计算。下面这个图是官方网站的:
所有的流计算都参照了Google的 data flow,里面有个重要的概念:数据的processing time和event time,即数据的处理时间和真正的发生时间有个gap。于是流计算领域还有个watermark,当前进来的事件水位需要watermark来维持,watermark可以指定时间delay的范围,在延迟窗口之外的数据是可以丢弃的,在业务上晚到的数据也是没有意义的。
下面是structuredstreaming的架构图:
这里面就是把刚才Sparkstreaming讲exactly-once的步骤1,2,3都实现了,它本质上还是分批的batch方式,offset自己维护,状态存储用的hdfs,对外的sink没有做类似的幂等操作,也没有写完之后再去commit offset,它只是再保证容错的同时去实现内部引擎的exactly-once。
特别说一下,Structuredstreaming和原生的streaming的api有一点区别,它create表的Dataframe的时候,是需要指定表的schema的,意味着你需要提前指定schema。另外它的watermark是不支持SQL的,于是我们加了一个扩展,实现完全写sql,可以从左边到右边的转换(下图),我们希望用户不止是程序员,也希望不会写程序的数据分析师等同学也能用到。
于是基于以上几个Spark structuredstreaming的特点和缺点,我们考虑使用Flink来做这些事情。
Flink目标是对标Spark,流这块是领先比较多,它野心也比较大,图计算,机器学习等它都有,底层也是支持yarn,tez等。对于社区用的比较多的存储,Flink社区官方都支持比较好,相对来说。
Flink的框架图:
Flink中的JobManager,相当于Spark的driver角色,taskManger相当于executor,里面的task也有点类似Spark的那些task。 不过Flink用的rpc是akka,同时Flink core自定义了内存序列化框架,另外task无需像Spark每个stage的task必须相互等待而是处理完后即往下游发送数据。
Flink binary data处理operator:
Spark的序列化用户一般会使用kryo或者java默认的序列化,同时也有Tungsten项目对Spark程序做一jvm层面以及代码生成方面的优化。相对于Spark,Flink自己实现了基于内存的序列化框架,里面维护着key和pointer的概念,它的key是连续存储,在cpu层面会做一些优化,cache miss概率极低。比较和排序的时候不需要比较真正的数据,先通过这个key比较,只有当它相等的时候,才会从内存中把这个数据反序列化出来,再去对比具体的数据,这是个不错的性能优化点。
Flink task chain:
Task中operatorchain,是比较好的概念。如果上下游数据分布不需要重新shuffle的话,比如图中source是kafka source,后面跟的map只是一个简单的数据filter,我们把它放在一个线程里面,就可以减少线程上下文切换的代价。
比如说这里面会有5个task,就会有几个并发线程去跑,chain起来的话放在一个线程去跑就可以提升数据传输性能。Spark是黑盒的,每个operator无法设并发度,而Flink可以对每个operator设并发度,这样可以更灵活一点,作业运行起来对资源利用率也更高一点。
Spark 一般通过Spark.default.parallelism来调整并行度,有shuffle操作的话,并行度一般是通Spark.sql.shuffle.partitions参数来调整,实时计算的话其实应该调小一点,比如我们生产中和kafka的partition数调的差不多,batch在生产上会调得大一点,我们设为1000,左边的图我们设并发度为2,最大是10,这样首先分2个并发去跑,另外根据key做一个分组的概念,最大分为10组,就可以做到把数据尽量的打散。
因为Flink的数据是一条条过来处理,所以Flink中的每条数据处理完了立马发给下游,而不像spark,需要等该operator所在的stage所有的task都完成了再往下发。
Flink有粗粒度的checkpoint机制,以非常小的代价为每个元素赋予一个snapshot概念,只有当属于本次snapshot的所有数据都进来后才会触发计算,计算完后,才把buffer数据往下发,目前Flink sql没有提供控制buffer timeout的接口,即我的数据要buffer多久才往下发。可以在构建Flink context时,指定buffer timeout为0,处理完的数据才会立马发下去,不需要等达到一定阈值后再往下发。
Backend默认是维护在jobmanager内存,我们更多使用的的是写到hdfs上,每个operator的状态写到rocksdb上,然后异步周期增量同步到外部存储。
图中左半部分的红色节点发生了failover,如果是at-least-once,则其最上游把数据重发一次就好;但如果是exactly-once,则需要每个计算节点从上一次失败的时机重放。
Flink1.4之后有两阶段提交来支持exactly-once.它的概念是从上游kafka消费数据后,每一步都会发起一次投票,来记录状态,通过checkpoint的屏障来处理标记,只有最后再写到kafka(0.11之后的版本),只有最后完成之后,才会把每一步的状态让jobmanager中的cordinator去通知可以固化下来,这样实现exactly-once。
还有一点Flink比较好的就是,基于它的checkpoint来实现savepoint功能。业务方需要每个应用恢复节点不一样,希望恢复到的版本也是可以指定的,这是比较好的。这个savepoint不只是数据的恢复,也有计算状态的恢复。
我们期待和你一起,把Flink建设得更好,帮助更多开发者。
本文转自Apache Flink China博客,作者:易伟平,原文链接:
转载地址:http://suuta.baihongyu.com/