因为我不知道大数据相关的东西,Flink也仅仅看了点皮毛,所以不敢把标题叫做 “指北”、“指南”之类的...
今天,我们不谈大数据其他相关的东西,只说说 什么是 Flink。
在Apache Flink的官方网站上我们可以看到这样的项目理念:
Apache Flink is a framework and distributed processing engine for stateful computations over unbounded and bounded data streams.
相信能看到这篇文章的都是文化人,我就不翻译了(因为我看不懂)。
然而,我们好像并不能通过这简单的一句话对Flink有个大体的认识,所以请看下面这个图
这个图是我从 云邪的分享ppt里偷的,我想他这么推广Flink,大概不会介意我偷他的图。
因为我没听过云邪的分享,所以即便拿到了这个图我也不知道Flink是什么。
为了写这篇文章,我去了解了下流处理技术的发展史,那么问题又来了,什么是流处理?,简单来说 流 表示无限数据,流处理就是处理无限的源源不断的数据的意思。
为了实现流处理,人们采用 分开处理连续的实时数据和有限批次的数据 的方式,这种方式可以使系统构建工作变得简单,然而却增加了系统的维护成本,这就是传说中的Lambda架构,它通过批量MapReduce作业提供准确的计算结果,同时使用Storm将最新数据的计算结果初步展示出来。
Lambda 架构是构建大数据应用程序的一种很有效的框架,但它还不够好。举例来说,基于MapReduce 和 HDFS 的 Lambda 系统有一个长达数小时的时间窗口,在这个窗口内,由于实时任务失败而产生的不准确的结果会一直存在。Lambda 架构需要在两个不同的 API(applicationprogramming interface,应用程序编程接口)中对同样的业务逻辑进行两次编程:一次为批量计算的系统,一次为流式计算的系统。针对同一个业务问题产生了两个代码库,各有不同的漏洞。这种系统实际上非常难维护。------摘自《Flink基础教程》
难以维护只是Lambda架构的一个局限,实际上在低延迟和高吞吐的流处理系统中维持良好的容错是相当困难的,为了得到有保障的准确状态,人们又想出了一个鬼主意:将连续事件中的流数据分割成一系列微小的批量作业,有点像用积分求面积的感觉,当你分割的足够小它就是一条线。这就是传说中的Spark Streaming所使用的方法。然而
在 Spark Streaming 中,处理数据的单位是一批而不是单条,而数据采集却是逐条进行的,因此 Spark Streaming 系统需要设置间隔使得数据汇总到一定的量后再一并操作,这个间隔就是批处理间隔。批处理间隔是 Spark Streaming 的核心概念和关键参数,它决定了 Spark Streaming 提交作业的频率和数据处理的延迟,同时也影响着数据处理的吞吐量和性能。 ------- 摘自IBM《Spark Streaming 新手指南》
于是乎,又有人想出了一些鬼主意,综合了之前所有大数据处理引擎的优势,完善了各种缺点,造就了一款能够同时支持高吞吐和Exactly-Once语义(有点像事务隔离级别,刚好一次的意思,就是很准确,刚好一次就完成。)的实时计算,还能提供批量数据处理(在Flink看来,批处理就是对有限数据进行流处理)。
上述内容基本解释了What is Flink 的那张图,但还有些原理上的疑点。
所以我要祭出 云邪 的另一张图,大佬的图整的就是好吖。
从后往前说哈
Window(窗口)
窗口是一种机制,我记得TCP好像还有什么滑动窗口的概念,感兴趣可以自行搜索。
时间窗口(以下摘自《Flink基础教程》)
时间窗口是最简单和最有用的一种窗口。它支持滚动和滑动。举一个例子,假设要对传感器输出的数值求和。一分钟滚动窗口收集最近一分钟的数值,并在一分钟结束时输出总和,如
一分钟滑动窗口计算最近一分钟的数值总和,但每半分钟滑动一次并输出结果,如
第一个滑动窗口对 9、6、8 和 4 求和,得到 27。半分钟后,窗口滑动,然后对 8、4、7 和 3 求和,得到 22,照此类推。在 Flink 中,一分钟滚动窗口的定义如下。
stream.timeWindow(Time.minutes(1));
每半分钟(即 30 秒)滑动一次的一分钟滑动窗口如下所示。
lstream.timeWindow(Time.minutes(1), Time.seconds(30))
计数窗口
Flink支持的另一种常见窗口叫做计数窗口。采用计数窗口时,分组依据不再是时间戳,而是元素的数量。例如在上图中每半分钟滑动一次的滑动窗口也可解释为由4个元素组成的计数窗口,并且每两个元素滑动一次。滚动和滑动的计数窗口分别定义如下:
stream.countWindow(4);
stream.countWindow(4, 2);
虽然计数窗口有用,但其定义不如时间窗口严谨,因此要谨慎使用。时间不会停止,而且时间窗口总会“关闭”。但就计数窗口而言,假设其定义的元素数量为100,而某个key对应的元素永远达不到100个,那么窗口就永远不会关闭,该窗口占用的内存也就浪费了。(当然这个是有解的)
会话窗口
Flink支持的另一种很有用的窗口是会话窗口。会话指的是活动阶段,其前后都是非活动阶段(做Web开发的应该很容易理解这种概念,就是Session)。
在Flink中,会话窗口由超时时间设定,即希望等待多久才认为会话已经结束。举例来说,以下代码表示,如果用户处于非活动状态长达5分钟,则认为会话结束。
stream.window(SessionWindows.withGap(Time.minutes(5)));
Time(时间)
在流处理中,主要有两个时间概念。
- 事件时间,即事件实际发生时间。
处理时间,即事件被处理的时间。
以《星球大战》系列电影为例。首先上映的3部电影是该系列中的第4、5、6 部(这是事件时间),它们的上映年份分别是 1977 年、1980 年和 1983 年(这是处理时间)。之后按事件时间上映的第 1、2、3、7 部,对应的处理时间分别是 1999 年、2002 年、2005 年和 2015 年。由此可见,事件流的顺序可能是乱的(尽管年份顺序一般不会乱)。
通常还有第 3 个时间概念,即摄取时间,也叫作进入时间。它指的是事件进入流处理框架的时间。缺乏真实事件时间的数据会被流处理器附上时间戳,即流处理器第一次看到它的时间(这个操作由 source 函数完成,它是程序的第一个处理节点)。在现实世界中,许多因素(如连接暂时中断,不同原因导致的网络延迟,分布式系统中的时钟不同步,数据速率陡增,物理原因,或者运气差)使得事件时间和处理时间存在偏差(即事件时间偏差)。事件时间顺序和处理时间顺序通常不一致,这意味着事件以乱序到达流处理器。根据应用程序的不同,两个时间概念都很有用。有些应用程序(如一些预警应用程序)需要尽可能快地得到结果,即使有小的误差也没关系。它们不必等待迟到的事件,因此适合采用处理时间语义。其他一些应用程序(如欺诈检测系统或者账单系统)则对准确性有要求:只有在时间窗口内发生的事件才能被算进来。对于这些应用程序来说,事件时间语义才是正确的
选择。也有两者都采用的情况,比如既要准确地计数,又要提供异常预警。
关于时间和窗口机制还有好几个点的概念没有抄过来,感兴趣可自行搜索。
State(状态)
流式计算分为无状态和有状态两种情况。
其实旧的流处理系统并不支持有状态,下图说明了有状态和无状态的区别
书上说在分布式系统中引入状态时自然就引入了一致性问题,就是前文中提到的Exactly-Once 的概念。在流处理中,一致性分为3个级别:
- at-most-once: 这其实是没有正确性保障的委婉说法——故障发生后,计数结果可能丢失。
- at-least-once: 这表示计数结果可能大于正确值,但绝不会小于正确值。也就是说,技术程序在发生故障后可能多算,但是绝对不会少算。
- exactly-once: 这表示系统发生故障后得到的计算结果与正确结果一致。
Checkpoint(检查点)
Flink保证exactly-once语义是靠检查点这一特性来实现的,它可以在出现故障时将系统重置回正确的状态。
Flink 检查点的核心作用是确保状态正确,即使遇到程序中断,也要正确。
Flink 检查点算法的正式名称是异步屏障快照(asynchronous barrier snapshotting)。该算法大致基于 Chandy-Lamport 分布式快照算法。
检查点由 Flink 自动生成,用来在故障发生时重新处理记录,从而修正状态。
累了,我不想写了,咋这么多概念吖,好烦哦~
参考:
Apache Flink官网
《Flink基础教程》
Spark Streaming 新手指南 IBM