文章目录
0、flink是什么
Apache Flink是一个面向数据流处理和批量数据处理的可分布式的开源计算框架,用于在无界数据流和有界数据流上进行有状态的计算。
Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。
- 处理无界和有界数据
- 部署应用到任意地方
- 运行任意规模应用
- 利用内存性能,非常低的处理延迟
- 任务的状态始终保留在内存中,如果状态大小超过可用内存,则会保存在能高效访问的磁盘数据结构中
离线: hive 、 Spark(离线)
实时:Storm 、SparkStreaming、flink(实时
SparkStreaming:Scala
Flink: java(实际上,公司里面还是用Java的多)
1、Flink特性
- 1.有状态计算。
- 状态是指flink能够维护数据在时序上的聚类和聚合,同时它的checkpoint机制
- 2.带有事件时间的流处理和窗口处理。
- 事件时间的语义使流计算的结果更加精确,尤其在事件到达无序或者延迟的情况下。
- 3.支持高度灵活的窗口操作。
- 支持基于time、count、session,以及data-driven的窗口操作,能很好的对现实环境中的创建的数据进行建模。
- 4.利用内存性能,非常低的处理延迟
- 任务的状态始终保留在内存中,如果状态大小超过可用内存,则会保存在能高效访问的磁盘数据结构中
- 5.支持高吞吐、低延迟、高性能的流处理
- 6.支持savepoints 机制(一般手动触发)。
- 可以将应用的运行状态保存下来;在升级应用或者处理历史数据是能够做到无状态丢失和最小停机时间。
- 7.支持大规模的集群模式,
- 支持yarn、Mesos。可运行在成千上万的节点上
- 8.支持程序自动优化
- :避免特定情况下Shuffle、排序等昂贵操作,中间结果进行缓存
2、flink部署运行模式
-
Local模式 用于测试调试
- 本地模式的安装唯一需要的只是Java 1.7.x或更高版本,本地运行会启动Single JVM,主要用于测试调试代码。一台服务器即可运行
-
Standalone模式 适用于flink自主管理资源
- 资源调度管理交给flink集群自己来处理,standAlone是一种集群模式,可以有一个或者多个主节点JobManager(HA模式),用于资源管理调度,任务管理,任务划分等工作,多个从节点taskManager,主要用于执行JobManager分解出来的任务
-
Yarn模式 适用于使用yarn来统一调度管理资源
- 单个Yarn Session模式 在YARN里面启动一个flink集群
- 先启动集群,然后在提交作业,接着会向yarn申请一块资源空间后,资源永远保持不变。如果资源满了,下一个作业就无法提交,只能等到yarn中的其中一个作业执行完成后,释放了资源,那下一个作业才会正常提交,实际工作当中一般不会使用这种模式
- 不需要做任何配置,直接将任务提交到yarn集群上面去,我们需要提前启动hdfs以及yarn集群即可
- 多个yarn session模式 每提交一个任务就在yarn上面启动一个flink小集群
- 一个任务会对应一个job,即每提交一个作业会根据自身的情况,向yarn申请资源,直到作业执行完成,并不会影响下一个作业的正常运行,除非是yarn上面没有任何资源的情况下。
- 不需要在yarn当中启动任何集群,直接提交任务即可
- 单个Yarn Session模式 在YARN里面启动一个flink集群
3、获取source的方式
DataStream
-
(1)基于文件,readTextFile(path)
-
读取文本文件,文件遵循TextInputFormat 读取规则,逐行读取并返回。
-
(2)基于socket, socketTextStream
- 从socker中读取数据,元素可以通过一个分隔符切开。
-
(3)基于集合, fromCollection(Collection)
- 通过java 的collection集合创建一个数据流,集合中的所有元素必须是相同类型的。
-
(4)自定义输入,addSource ,
- 实现读取第三方数据源的数据
-
系统内置提供了一批connectors,连接器会提供对应的source支持【kafka】
DataSet(有界,批处理,离线)
- 基于文件:readTextFile(path)
- 基于集合:fromCollection(Collection)
public static void main(String[] args) throws Exception {
//步骤一:获取环境变量
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//步骤二:模拟数据
ArrayList<String> data = new ArrayList<String>();
data.add("hadoop");
data.add("spark");
data.add("flink");
//步骤三:获取数据源
DataStreamSource<String> dataStream = env.fromCollection(data);
//步骤四:transformation操作
SingleOutputStreamOperator<String> addPreStream = dataStream.map(new MapFunction<String, String>() {
@Override
public String map(String word) throws Exception {
return "kaikeba_" + word;
}
});
//步骤五:对结果进行处理(打印)
addPreStream.print().setParallelism(1);
//步骤六:启动程序
env.execute("StreamingSourceFromCollection");
}
4、 常见Transformation操作
DataStream
- map:输入一个元素,然后返回一个元素,中间可以做一些清洗转换等操作
- flatmap:输入一个元素,可以返回零个,一个或者多个元素
- filter:过滤函数,对传入的数据进行判断,符合条件的数据会被留下
- keyBy:根据指定的key进行分组,相同key的数据会进入同一个分区【典型用法见备注】
- reduce:对数据进行聚合操作,结合当前元素和上一次reduce返回的值进行聚合操作,然后返回一个新的值
- aggregations:sum(),min(),max()等
- window:在后面单独详解
- Union:合并多个流,新的流会包含所有流中的数据,但是union是一个限制,就是所有合并的流类型必须是一致的。
- Connect:和union类似,但是只能连接两个流,两个流的数据类型可以不同,会对两个流中的数据应用不同的处理方法。
- CoMap, CoFlatMap:在ConnectedStreams中需要使用这种函数,类似于map和flatmap
- Split:根据规则把一个数据流切分为多个流
- Select:和split配合使用,选择切分后的流
public static void main(String[] args) throws Exception {
int port;
try{
ParameterTool parameterTool = ParameterTool.fromArgs(args);
port = parameterTool.getInt("port");
}catch (Exception e){
System.err.println("no port set,user default port 9988");
port=9988;
}
//步骤一:获取flink运行环境(stream)
StreamExecutionEnvironment env= StreamExecutionEnvironment.getExecutionEnvironment();
String hostname="10.126.88.226";
String delimiter="\n";
//步骤二:获取数据源
DataStreamSource<String> textStream = env.socketTextStream(hostname, port, delimiter);
//步骤三:执行transformation操作
SingleOutputStreamOperator<WordCount> wordCountStream = textStream.flatMap(new FlatMapFunction<String, WordCount>() {
public void flatMap(String line, Collector<WordCount> out) throws Exception {
String[] fields = line.split("\t");
for (String word : fields) {
out.collect(new WordCount(word, 1L));
}
}
}).keyBy("word")
.timeWindow(Time.seconds(2), Time.seconds(1))//每隔1秒计算最近2秒
.sum("count");
wordCountStream.print().setParallelism(1);//打印并设置并行度
//步骤四:运行程序
env.execute("socket word count");
}
public static class WordCount{
public String word;
public long count;
public WordCount(){ }
public WordCount(String word,long count){
this.word=word;
this.count=count;
}
@Override
public String toString() {
return "WordCount{" +
"word='" + word + '\'' +
", count=" + count +
'}';
}
}
DataSet
-
Map:输入一个元素,然后返回一个元素,中间可以做一些清洗转换等操作
-
FlatMap:输入一个元素,可以返回零个,一个或者多个元素
-
MapPartition:类似map,一次处理一个分区的数据【如果在进行map处理的时候需要获取第三方资源链接,建议使用MapPartition】
-
Filter:过滤函数,对传入的数据进行判断,符合条件的数据会被留下
-
Reduce:对数据进行聚合操作,结合当前元素和上一次reduce返回的值进行聚合操作,然后返回一个新的值
-
Aggregate:sum、max、min等
-
Distinct:返回一个数据集中去重之后的元素,data.distinct()
-
Join:内连接
-
OuterJoin:外链接
-
Cross:获取两个数据集的笛卡尔积
-
Union:返回两个数据集的总和,数据类型需要一致
-
First-n:获取集合中的前N个元素
-
Sort Partition:在本地对数据集的所有分区进行排序,通过sortPartition()的链接调用来完成对多个字段的排序
public static void main(String[] args) throws Exception{ //获取运行环境 ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment(); ArrayList<String> data = new ArrayList<>(); data.add("you jump"); data.add("i jump"); DataSource<String> text = env.fromCollection(data); FlatMapOperator<String, String> flatMapData = text.flatMap(new FlatMapFunction<String, String>() { @Override public void flatMap(String value, Collector<String> out) throws Exception { String[] split = value.toLowerCase().split("\\W+"); for (String word : split) { System.out.println("单词:"+word); out.collect(word); } } }); flatMapData.distinct()// 对数据进行整体去重 .print(); }
5、常见sink操作
-
1、print()/printToErr()
- 打印每个元素的toString()方法的值到标准输出或者标准错误输出流中
-
2、writeAsText()
-
3、Flink提供的sink
- Apache Kafka (source/sink)
- Hadoop FileSystem (sink)
- Elasticsearch (sink)
-
4、自定义sink
- writeAsText():将元素以字符串形式逐行写入,这些字符串通过调用每个元素的toString()方法来获取
- writeAsCsv():将元组以逗号分隔写入文件中,行及字段之间的分隔是可配置的。每个字段的值来自对象的toString()方法
- print():打印每个元素的toString()方法的值到标准输出或者标准错误输出流中
6、dataSet当中的广播变量
广播变量,可以理解为是一个公共的共享变量,我们可以把一个dataset 数据集广播出去,然后不同的task在节点上都能够获取到,这个数据在每个节点上只会存在一份。
如果不使用broadcast,则在每个节点中的每个task中都需要拷贝一份dataset数据集,比较浪费内存(也就是一个节点中可能会存在多份dataset数据)。
//用法
//1:初始化数据
DataSet<Integer> toBroadcast = env.fromElements(1, 2, 3)
//2:广播数据
.withBroadcastSet(toBroadcast, "broadcastSetName");
//3:获取数据
Collection<Integer> broadcastSet = getRuntimeContext().getBroadcastVariable("broadcastSetName");
注意:
1:广播出去的变量存在于每个节点的内存中,所以这个数据集不能太大。因为广播出去的数据,会常驻内存,除非程序执行结束
2:广播变量在初始化广播出去以后不支持修改,这样才能保证每个节点的数据都是一致的。
7、累加器
与Mapreduce counter的应用场景差不多,都能很好地观察task在运行期间的数据变化
可以在Flink job任务中的算子函数中操作累加器,但是只能在任务执行结束之后才能获得累加器的最终结果。
最简单的累加器是counter(计数器)
Counter是一个具体的累加器(Accumulator)实现
IntCounter, LongCounter 和 DoubleCounter
//1:创建累加器
private IntCounter numLines = new IntCounter();
//2:注册累加器
getRuntimeContext().addAccumulator("num-lines", this.numLines);
//3:使用累加器
this.numLines.add(1);
//4:获取累加器的结果
myJobExecutionResult.getAccumulatorResult("num-lines")
8、Flink的Table以及SQL
TableAPI 是一种关系型API,类 SQL 的API,用户可以像操作表一样地操作数据, 非常的直观和方便。
Flink的tableAPI允许我们,对流式处理以及批量处理,都使用sql语句的方式来进行开发。
只要我们知道了dataStream或者dataSet可以转换成为Table,那么我们就可以方便的从各个地方获取数据,然后转换成为Table,通过TableAPI或者SQL来实现我们的数据的处理等
Flink的表API和SQL程序,可以连接到其他外部系统,来读写批处理表和流表。
Table source提供对存储在外部 系统(如数据库、键值存储、消息队列或文件系统)中的数据的访问。Table Sink将表发送到外部存储系统。
9、Flink架构概述
JobManager 为 Master 节点,TaskManager 为 Worker (Slave)节点。
所有组件之间的通信都是借助于 Akka Framework,包括任务的状态以及 Checkpoint 触发等信息。
-
1、Client 客户端
-
客户端负责将任务提交到集群,与 JobManager 构建 Akka 连接,然后将任务提交到 JobManager,通过和 JobManager 之间进行交互获取任务执行状态。
-
客户端提交任务可以采用 CLI 方式或者通过使用 Flink WebUI 提交,也可以在应用程序中指定 JobManager 的 RPC 网络端口构建 ExecutionEnvironment 提交 Flink 应用。
-
2、JobManager
- JobManager 负责整个 Flink 集群任务的调度以及资源的管理,从客户端中获取提交的应用,然后根据集群中 TaskManager 上 TaskSlot 的使用情况,为提交的应用分配相应的 TaskSlot 资源并命令 TaskManager 启动从客户端中获取的应用。
- JobManager 相当于整个集群的 Master 节点,且整个集群有且只有一个活跃的 JobManager ,负责整个集群的任务管理和资源管理。
- JobManager 和 TaskManager 之间通过 Actor System 进行通信,获取任务执行的情况并通过 Actor System 将应用的任务执行情况发送给客户端。
- 同时在任务执行的过程中,Flink JobManager 会触发 Checkpoint 操作,每个 TaskManager 节点 收到 Checkpoint 触发指令后,完成 Checkpoint 操作,所有的 Checkpoint 协调过程都是在 Fink JobManager 中完成。
- 当任务完成后,Flink 会将任务执行的信息反馈给客户端,并且释放掉 TaskManager 中的资源以供下一次提交任务使用。
-
TaskManager
-
TaskManager 相当于整个集群的 Slave 节点,负责具体的任务执行和对应任务在每个节点上的资源申请和管理。
-
客户端通过将编写好的 Flink 应用编译打包,提交到 JobManager,然后 JobManager 会根据已注册在 JobManager 中 TaskManager 的资源情况,将任务分配给有资源的 TaskManager节点,然后启动并运行任务。
-
TaskManager 从 JobManager 接收需要部署的任务,然后使用 Slot 资源启动 Task,建立数据接入的网络连接,接收数据并开始数据处理。同时 TaskManager 之间的数据交互都是通过数据流的方式进行的。
-
可以看出,Flink 的任务运行其实是采用多线程的方式,这和 MapReduce 多 JVM 进行的方式有很大的区别,Flink 能够极大提高 CPU 使用效率,在多个任务和 Task 之间通过 TaskSlot 方式共享系统资源,每个 TaskManager 中通过管理多个 TaskSlot 资源池进行对资源进行有效管理。
把任务提交到集群上的语句
StandAlnoe模式
flink run -c streaming.slot.lesson01.WordCount -p 2 flinklesson-1.0-SNAPSHOT.jar
Flink on Yarn(1)
flink run -m yarn-cluster -p 2 -yn 2 -yjm 1024 -ytm 1024 -c streaming.slot.lesson01.WordCount flinklesson-1.0-SNAPSHOT.jar 特点: 一个任务一个集群
掌握 Flink on Yarn(2)
yarn-session.sh -n 2 -jm 1024 -tm 1024
flink run ./examples/batch/WordCount.jar -input hdfs://hadoop100:9000/LICENSE -output hdfs://hadoop100:9000/wordcount-result.txt
特点:先建立一个集群,然后再往集群里面提交任务
10、并行度、Slot、Task
-
1、并行度
- 一个Flink程序由多个任务组成(source、transformation和 sink)。
- 一个任务由多个并行的实例(线程)来执行, 一个任务的并行实例(线程)数目就被称为该任务的并行度。
- 没有设置并行度,默认电脑有几个cpu core就会有几个并行度
- 一个任务的并行度设置可以从多个层次指定
- Operator Level(算子层次)
- Execution Environment Level(执行环境层次)
- Client Level(客户端层次) 提交flink的时候,有参数据可以让我们制定该任务的并行度
- System Level(系统层次)我们在flink的配置文件里面配置好。
-
2、Slot
-
flink 中的计算资源通过 Task Slot 来定义。
-
每个 task slot 代表了 TaskManager 的一个固定大小的资源子集。
-
例如,一个拥有3个slot的 TaskManager,会将其管理的内存平均分成三分分给各个 slot。将资源 slot 化意味着来自不同job的task不会为了内存而竞争,而是每个task都拥有一定数量的内存储备。需要注意的是,这里不会涉及到CPU的隔离,slot目前仅仅用来隔离task的内存。
-
solt的数量通常与每个TaskManager节点的可用CPU内核数成比例。一般情况下你的slot数是你每个节点的cpu的核数。
11、数据传输的方式
- 1 forward strategy
- 一个 task 的输出只发送给一个 task 作为输入
- 如果两个 task 都在一个 JVM 中的话,那么就可以避免网络开销
- 2 key based strategy (key by)
- 数据需要按照某个属性(我们称为 key),进行分组(或者说分区)
- 相同 key 的数据需要传输给同一个 task,在一个 task 中进行处理
- 3 broadcast strategy
- 4 random strategy
- 数据随机的从一个 task 中传输给下一个 operator 所有的 subtask
- 保证数据能均匀的传输给所有的 subtask
12、Operator Chain的条件
- 数据传输策略是 forward strategy
- 在同一个 TaskManager 中运行
- 并行度设置为1:
将operators链接成task是非常有效的优化:它能减少线程之间的切换,减少消息的序列化/反序列化,减少数据在缓冲区的交换,减少了延迟的同时提高整体的吞吐量。
13、state状态
-
一般指一个具体的task/operator的状态。State可以被记录,在失败的情况下数据还可以恢复。
- 单词出现的次数有累计的效果。如果没有状态的管理,是不会有累计的效果的,所以Flink里面还有state的概念。
-
Flink中有两种基本类型的State:Keyed State,Operator State,
-
他们两种都可以以两种形式存在:原始状态(raw state)和托管状态(managed state)
-
托管状态:由Flink框架管理的状态,我们通常使用的就是这种。
-
原始状态:由用户自行管理状态具体的数据结构,框架在做checkpoint的时候,使用byte[]来读写状态内容,对其内部数据结构一无所知。通常在DataStream上的状态推荐使用托管的状态,当实现一个用户自定义的operator时,会使用到原始状态。但是我们工作中一般不常用,所以我们不考虑他。
-
Flink中的两种State:
-
operator state
-
task级别的state,即每个task对应一个state
-
Kafka Connector source中的每个分区(task)都需要记录消费的topic的partition和offset等信息。
-
operator state 只有一种托管状态:ValueState
-
-
keyed state
- 记录的是每个key的状态,每个key对应一个state
- Keyed state托管状态有六种类型:
- ValueState
- ListState
- MapState
- ReducingState
- AggregatingState
- FoldingState
/**
* ValueState<T> :这个状态为每一个 key 保存一个值
* value() 获取状态值
* update() 更新状态值
* clear() 清除状态
* ValueState 保存的是对应的一个 key 的一个状态值
*/
/**
* ListState<T> :这个状态为每一个 key 保存集合的值
* get() 获取状态值
* add() / addAll() 更新状态值,将数据放到状态中
* clear() 清除状态
*/
/**
* MapState<K, V> :这个状态为每一个 key 保存一个 Map 集合
* put() 将对应的 key 的键值对放到状态中
* values() 拿到 MapState 中所有的 value
* clear() 清除状态
*/
/**
* ReducingState<T> :这个状态为每一个 key 保存一个聚合之后的值
* get() 获取状态值
* add() 更新状态值,将数据放到状态中
* clear() 清除状态
*/
Flink支持的StateBackend
- MemoryStateBackend 默认情况
- 状态信息,存储在 TaskManager 的堆内存中的,c heckpoint 的时候将状态保存到 JobManager 的堆内存中。
- 缺点:只能保存数据量小的状态; 状态数据有可能会丢失
- 优点:开发测试很方便
- FsStateBackend
- 状态信息存储在 TaskManager 的堆内存中的,checkpoint 的时候将状态保存到指定的文件中 (HDFS 等文件系统)
- 缺点:状态大小受TaskManager内存限制(默认支持5M)
- 优点:状态访问速度很快状态信息不会丢失
用于: 生产,也可存储状态数据量大的情况
- RocksDBStateBackend
- 状态信息存储在 RocksDB 数据库 (key-value 的数据存储服务), 最终保存在本地文件中,checkpoint 的时候将状态保存到指定的文件中 (HDFS 等文件系统)
- 缺点:状态访问速度有所下降
- 优点:可以存储超大量的状态信息 ;状态信息不会丢失
用于: 生产,可以存储超大量的状态信息
StateBackend配置方式
(1)单任务调整 修改当前任务代码 env.setStateBackend()
(2)全局调整 修改flink-conf.yaml
14、checkpoint概述
-
为了保证state的容错性,Flink需要对state进行checkpoint。
-
根据配置,周期性地基于Stream中各个Operator/task的状态来生成快照,从而将这些状态数据定期持久化存储下来;
-
当Flink程序一旦意外崩溃时,重新运行程序时可以有选择地从这些快照进行恢复,从而修正因为故障带来的程序数据异常
checkpoint配置:
-
默认checkpoint功能是disabled的,想要使用的时候需要先启用,
-
checkpoint开启之后,checkPointMode有两种,Exactly-once和At-least-once,
- Exactly-once 默认,对大多数应用来说是最合适的。
- At-least-once可能用在某些延迟超低的应用程序(始终延迟为几毫秒)。
// 每隔1000 ms进行启动一个检查点【设置checkpoint的周期】
env.enableCheckpointing(1000);
// 高级选项:
// 设置模式为exactly-once (这是默认值)
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ONCE);
// 确保检查点之间有至少500 ms的间隔【checkpoint最小间隔】
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
// 检查点必须在一分钟内完成,或者被丢弃【checkpoint的超时时间】
env.getCheckpointConfig().setCheckpointTimeout(60000);
16、恢复数据(容错)
重启策略概述
-
Flink支持不同的重启策略,以在故障发生时控制作业如何重启,集群在启动时会伴随一个默认的重启策略,在没有定义具体重启策略时会使用该默认策略。 如果在工作提交时指定了一个重启策略,该策略会覆盖集群的默认策略
-
默认的重启策略可以通过 Flink 的配置文件 flink-conf.yaml 指定。配置参数 restart-strategy 定义了哪个策略被使用。常用的重启策略
- (1)固定间隔 (Fixed delay)
- (2)失败率 (Failure rate)
- (3)无重启 (No restart)
-
若未启用 checkpoint,则使用无重启 (no restart) 策略。
-
若启用 checkpoint,重启策略可以在flink-conf.yaml中配置,表示全局的配置;也可以在应用**代码中动态指定,**会覆盖全局配置。
-
未配置重启策略,默认使用固定间隔 (fixed-delay) 策略, 尝试重启次数默认值是:Integer.MAX_VALUE,
-
第一种:全局配置 flink-conf.yaml restart-strategy: fixed-delay restart-strategy.fixed-delay.attempts: 3 restart-strategy.fixed-delay.delay: 10 s 第二种:应用代码设置 env.setRestartStrategy(RestartStrategies.fixedDelayRestart( 3, // 尝试重启的次数 Time.of(10, TimeUnit.SECONDS) // 间隔 ));
-
多checkpoint
-
默认情况下,如果设置了Checkpoint选项,则Flink只保留最近成功生成的1个Checkpoint,Flink程序失败时,可从最近的这个Checkpoint来进行恢复。
-
如果我们希望保留多个Checkpoint,并能够根据实际需要选择其中一个进行恢复,
-
需要在Flink的配置文件conf/flink-conf.yaml中,添加如下配置,指定最多需要保存Checkpoint的个数:
-
state.checkpoints.num-retained: 20
-
如果Flink程序异常失败,或者最近一段时间内数据处理错误,我们可以将程序从某一个Checkpoint点进行恢复
-
恢复数据,可以在代码里面指定checkpoint目录,这样下一次启动的时候,即使代码发生了改变就自动恢复数据了。
-
17、checkPoint与savePoint
- checkPoint
- 应用定时触发,用于保存状态,会过期,内部应用失败重启的时候使用。
- savePoint
- 用户手动执行,是指向Checkpoint的指针,不会过期,在升级的情况下使用。
- savepoint的使用
- 1:在flink-conf.yaml中配置Savepoint存储位置;
- 2:触发一个savepoint【直接触发或者在cancel的时候触发】
- 3:从指定的savepoint启动job
18、Window窗口
聚合事件(比如计数、求和)在流上的工作方式与批处理不同。比如,对流中的所有元素进行计数是不可能的,因为通常流是无限的(无界的)。
所以,流上的聚合需要由 window 来划定范围,比如 “计算过去的5分钟” ,或者 “最后100个元素的和” 。window是一种可以把无限数据切割为有限数据块的手段。
窗口可以是 时间驱动的 【Time Window】(比如:每30秒)
或者 数据驱动的【Count Window】 (比如:每100个元素)。
Keyed Window 和 Non Keyed Window
Window类型
- tumbling windows:滚动窗口 【没有重叠】
- sliding windows:滑动窗口 【有重叠】
- session windows:会话窗口
- global windows: 没有窗口global window + trigger 一起配合才能使用
/**
* 每隔5秒计算最近10秒单词出现的次数
*/
public class TimeWindowWordCount {
public static void main(String[] args) throws Exception{
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> dataStream = env.socketTextStream("localhost", 8888);
SingleOutputStreamOperator<Tuple2<String, Integer>> result = dataStream.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Integer>> out) throws Exception {
String[] fields = line.split(",");
for (String word : fields) {
out.collect(new Tuple2<>(word, 1));
}
}
}).keyBy(0)
.timeWindow(Time.seconds(10), Time.seconds(5))
.sum(1);
result.print().setParallelism(1);
env.execute("TimeWindowWordCount");
}
}
-
window增量聚合
-
窗口中每进入一条数据,就进行一次计算,等时间到了展示最后的结果
-
常用的聚合算子
-
reduce(reduceFunction) aggregate(aggregateFunction) sum(),min(),max()
-
-
window全量聚合
-
等属于窗口的数据到齐,才开始进行聚合计算【可以实现对窗口内的数据进行排序等需求】
-
apply(windowFunction) process(processWindowFunction) processWindowFunction比windowFunction提供了更多的上下文信息。类似于map和RichMap的关系
-
window join
两个window之间可以进行join,join操作只支持三种类型的window:滚动窗口,滑动窗口,会话窗口
19、Time时间
针对stream数据中的时间,可以分为以下三种:
-
Event Time:事件产生的时间,它通常由事件中的时间戳描述。
-
Ingestion time:事件进入Flink的时间
-
Processing Time:事件被处理时当前系统的时间
EventTime
- 1.事件生成时的时间,在进入Flink之前就已经存在,可以从event的字段中抽取。
- 2.必须指定watermarks(水位线)的生成方式。
- 3.优势:确定性,乱序、延时、或者数据重放等情况,都能给出正确的结果
- 4.弱点:处理无序事件时性能和延迟受到影响
2、IngestTime
- 1.事件进入flink的时间,即在source里获取的当前系统的时间,后续操作统一使用该时间。
- 2.不需要指定watermarks的生成方式(自动生成)
- 3.弱点:不能处理无序事件和延迟数据
3、ProcessingTime
- 1.执行操作的机器的当前系统时间(每个算子都不一样)
- 2.不需要流和机器之间的协调
- 3.优势:最佳的性能和最低的延迟
- 4.弱点:不确定性 ,容易受到各种因素影像(event产生的速度、到达flink的速度、在算子之间传输速度等),压根就不管顺序和延迟
20、WaterMark水位
使用eventTime的时候如何处理乱序数据?
watermark是用于处理乱序事件的,通常用watermark机制结合window来实现。
流处理从事件产生,到流经source,再到operator,中间是有一个过程和时间的。虽然大部分情况下,流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、背压等原因,导致乱序的产生(out-of-order或者说late element)。
但是对于late element,我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。这个特别的机制,就是watermark。
总结:window触发的时间
- watermark 时间 >= window_end_time
- 在 [window_start_time, window_end_time) 区间中有数据存在,注意是左闭右开的区间,而且是以 event time 来计算的
21、迟到太多的事件处理方式
-
丢弃,这个是默认的处理方式,重启程序,做测试
-
allowedLateness 指定允许数据延迟的时间
-
当我们设置允许迟到 2 秒的事件,第一次 window 触发的条件是 watermark >= window_end_time
-
第二次(或者多次)触发的条件是 watermark < window_end_time + allowedLateness
-
-
sideOutputLateData 收集迟到的数据
位
使用eventTime的时候如何处理乱序数据?
watermark是用于处理乱序事件的,通常用watermark机制结合window来实现。
流处理从事件产生,到流经source,再到operator,中间是有一个过程和时间的。虽然大部分情况下,流到operator的数据都是按照事件产生的时间顺序来的,但是也不排除由于网络、背压等原因,导致乱序的产生(out-of-order或者说late element)。
但是对于late element,我们又不能无限期的等下去,必须要有个机制来保证一个特定的时间后,必须触发window去进行计算了。这个特别的机制,就是watermark。
总结:window触发的时间
- watermark 时间 >= window_end_time
- 在 [window_start_time, window_end_time) 区间中有数据存在,注意是左闭右开的区间,而且是以 event time 来计算的
21、迟到太多的事件处理方式
-
丢弃,这个是默认的处理方式,重启程序,做测试
-
allowedLateness 指定允许数据延迟的时间
-
当我们设置允许迟到 2 秒的事件,第一次 window 触发的条件是 watermark >= window_end_time
-
第二次(或者多次)触发的条件是 watermark < window_end_time + allowedLateness
-
-
sideOutputLateData 收集迟到的数据