这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
常用API
DataStream 常用API
主要分成三块 DataSource Transformation Sink
DataSource
主要有四种
基于文件
env.readTextFile("fileName");
也可以用hdfs
env.readTextFile("hdfs://teacher1:9000/a.txt")
但要多设置几步, 在windows环境中安装hadoop, 并且新增依赖
System.setProperty("HADOOP_USER_NAME","");
依赖方面主要有:
flink-hadoop-compatibility_2.11, hadoop-common, hadoop-hdfs, hadoop-client
基于Socket
从Socket中读取数据, 元素可以通过一个分割符分开
基于集合
通过java的Collection集合创建一个数据流, 集合中的所有元素必须是相同类型
env.fromCollection(Collection);
env.fromElements("A", "B");
如果满足一下条件,Flink将数据类型识别为POJO类型(允许按名称字段引用)
- 公有且独立(没有非静态内部类)
- 有公有的无参构造方法
- 类(及父类)中所有的不被static,transient修饰的属性要么是公有的(且不被final修饰), 要么是包含公有的getter和setter方法,遵循Java Bean命名规范
自定义
addSource实现读取第三方数据源, Flink提供了一批内置的Connector(连接器)
| 连接器 | 是否提供Source支持 | 是否提供Sink支持 |
|---|---|---|
| Apache Kafka | 是 | 是 |
| ElasticSearch | 否 | 是 |
| HDFS | 否 | 是 |
| Twitter Streaming PI | 是 | 否 |
kafka连接器
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_2.12</artifactId>
<version>1.13.1</version>
</dependency>
Properties properties = new Properties();
properties.setProperty("boostrap.servers", "teacher2:9092");
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnviroment();
FlinkKafkaConsumer consumer = new FlinkKafkaConsumer("topicName", new SimpleStringSchema(), properties);
DataStreamSource<String> data = env.addSource(consumer);
Transformation
-
Map: 输入一个元素, 返回一个元素,中间可以进行清洗转换操作
-
FlatMap: 输入一个元素,可以返回0个, 1个或者多个元素
-
Filter: 过滤函数,对传入的数据进行判断, 符合条件的数据留下
-
KeyBy : 根据指定的Key进行分组, Key相同的数据会进入同一个分区
KeyBy 有两种用法
- DataStream.keyBy("someKey")指定对象中的someKey字段作为分组Key
- DataStream.keyBy(0) 指定Tuple中的第一个元素作为分组Key
-
Reduce: 对数据进行聚合操作, 结合当前元素和上一次返回的值进行聚合操作,返回新值
-
Aggregations: sum() min() max()等
Sink
- writeAsText(): 将元素以字符串形式逐行写入, 通过调用toString()方法获取
- print()/printToErr(): 打印到标准输出或者标准错误输出流中
- 自定义 addSink 输出第三方介质
DataSet常用API
DataStream
- 基于集合 fromCollection(Collection)
- 基于文件 readTextFile(path), 基于HDFS数据\
Transformation
- Map
- FlatMap
- Filter
- Reduce
- Aggregations: sum() min() max()
SInk
- WriteAsText()
- WriteAsCsv()
- print()/printToErr()
窗口机制
窗口可以是基于时间驱动的,也可以是基于数据驱动的, 可以分为 翻滚窗口(Tumbling Window), 滑动窗口(Sliding Window), 会话窗口(Session Window), 全局窗口
Flink Time
EventTime【事件时间】
IngestionTime【摄入时间】 进入source
ProcessingTime【处理时间】
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
此时窗口需要考虑到事件时间, 使用watermark解决
watermark
水印(watermark)就是一个时间戳,Flink可以给数据流添加水印,
可以理解为:收到一条消息后,额外给这个消息添加了一个时间字段,这就是添加水印。
- 水印并不会影响原有Eventtime事件时间
- 当数据流添加水印后,会按照水印时间来触发窗口计算
也就是说watermark水印是用来触发窗口计算的
- 一般会设置水印时间,比事件时间小几秒钟,表示最大允许数据延迟达到多久
(即水印时间 = 事件时间 - 允许延迟时间)10:09:57 = 10:10:00 - 3s
- 当接收到的 水印时间 >= 窗口结束时间,则触发计算 如等到一条数据的水印时间为10:10:00 >= 10:10:00 才触发计算,也就是要等到事件时间为10:10:03的数据到来才触发计算
(即事件时间 - 允许延迟时间 >= 窗口结束时间 或 事件时间 >= 窗口结束时间 + 允许延迟时间)
并行度
一个Operator由多个并行的task线程执行, 一个Operator的并行Task数目被称为并行度
并行度可以有几种指定方式
- Operator Level 算子级别
- ExecutionEnvironment
- Client Level 客户端 命令端 -p
- System Level flink-conf.yaml文件中parallelism.default
source不可以被并行执行, 设置了也不会生效
尽可能规避算子并行度设置,因为会造成task的重新划分, 带来shuffer问题
推荐使用任务提交的时候 动态指定并行度
slot是静态的概念,指taskManager具有的并发执行能力, parallelism是动态概念,程序运行时实际使用的并发能力.
Flink-Kafka
1.1 消费策略
-
setStartFromGroupOffsets()【默认消费策略】
默认读取上次保存的offset信息 如果是应用第一次启动,读取不到上次的offset信息,则会根据这个参数auto.offset.reset的值来进行消费数据
-
setStartFromEarliest() 从最早的数据开始进行消费,忽略存储的offset信息
-
setStartFromLatest() 从最新的数据进行消费,忽略存储的offset信息
-
setStartFromSpecificOffsets(Map<KafkaTopicPartition, Long>) 从指定位置进行消费
-
当checkpoint机制开启的时候,KafkaConsumer会定期把kafka的offset信息还有其他operator的状态信息一块保存起来。当job失败重启的时候,Flink会从最近一次的checkpoint中进行恢复数据,重新消费kafka中的数据。
-
为了能够使用支持容错的kafka Consumer,需要开启checkpoint env.enableCheckpointing(5000); // 每5s checkpoint一次
1.2 Kafka consumer offset自动提交:
kafka consumer offset自动提交的配置需要根据job是否开启checkpoint来区分
checkpoint关闭时:
checkpoint开启时:
如果启用了checkpoint,并且启用了checkpoint完成时提交offset,返回ON_CHECKPOINTS。
如果未启用checkpoint,但是启用了自动提交,返回KAFKA_PERIODIC。
其他情况都返回DISABLED。
OffsetCommitMode是一个枚举类型,具有如下三个值:
- DISABLED:完全禁用offset提交。
- ON_CHECKPOINTS:当checkpoint完成的时候再提交offset。
- KAFKA_PERIODIC:周期性提交offset。
Flink-Kafka-Consumer:
package com.lagou.source;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import java.util.Properties;
public class FromKafka {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties properties = new Properties();
properties.setProperty("bootstrap.servers", "teacher2:9092");
FlinkKafkaConsumer<String> consumer = new FlinkKafkaConsumer<>("mytopic", new SimpleStringSchema(), properties);
//从最早开始消费
consumer.setStartFromEarliest();
DataStream<String> stream = env.addSource(consumer);
stream.print();
//stream.map();
env.execute();
}
}
Flink kafka Producer
代码:
package com.lagou.sink;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
import java.util.Properties;
public class SinkToKafka {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> data = env.socketTextStream("192.168.1.3", 7777);
Properties properties = new Properties();
properties.setProperty("bootstrap.servers","teacher2:9092");
FlinkKafkaProducer producer = new FlinkKafkaProducer("teacher2:9092", "mytopic2", new SimpleStringSchema());
data.addSink(producer);
env.execute();
}
}