Flink DataStream API 编程
很久没更新博客了,这半年多来,公司的事情也比较多,没多少时间学习,又赶上疫情,都是在家办公,慢慢感觉自己要颓废下去了,赶紧抽个周末让自己学点东西。
word count
每个分布式计算模型都是从Word count 开始的,学习Flink肯定也不例外,下面这个程序收集socket 端口的输入,统计5s内出现的单词书,并输出到控制台。
object ApplicationDemo {
def main(args: Array[String]): Unit = {
//获取执行环境
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
// val data: DataStream[String] = FlinkConnectKafka.getKafkaSource(env, "flink-kafka-topic")
//指定数据源
val data = env.socketTextStream("localhost", 9999)
val result: DataStream[(String, Int)] = data.flatMap {
_.toLowerCase.split(" ") filter {
_.nonEmpty
}
}
.map(x => (x, 1))
.keyBy(0)
.timeWindow(Time.seconds(5))
.sum(1)
result.print()
env.execute("Flink Test")
}
}
一段最简单的Flink 代码。
数据的流向通过 Data Source ==> Transformation ==> Data Sink
Data Source
一、内部数据源
- 文件 数据源
通过readTextFile(path)、readFile(fileInputFormat, path) 、readFile(fileInputFormat, path, watchType, interval, pathFilter)内置的方法获取数据 - Socket 数据源
通过socketTextStream获取数据 - 集合数据源
通过
fromCollection(Seq)、
fromCollection(Iterator)、
fromElements(elements: _*)、
fromParallelCollection(SplittableIterator)、
generateSequence(from, to) 获取数据
二、外部数据源
实现SourceFunction的自定义数据源,通过addSource()注册使用。
Data Sink
一、内部数据接收
- writeAsText()
- writeAsCsv(…)
- print()/ printToErr()
- writeUsingOutputFormat()
- writeToSocket
二、外部数据接收
实现SinkFunction 的自定义数据接收,通过addSink()注册使用。
流连接器(connector)
预定义的source和sink
- Apache Kafka (source/sink)
- Apache Cassandra (sink)
- Amazon Kinesis Streams (source/sink)
- Elasticsearch (sink)
- Hadoop FileSystem (sink)
- RabbitMQ (source/sink)
- Apache NiFi (source/sink)
- Twitter Streaming API (source)
- Google PubSub (source/sink)
Flink 还有些一些额外的连接器通过 Apache Bahir 发布, 包括:
- Apache ActiveMQ (source/sink)
- Apache Flume (sink)
- Redis (sink)
- Akka(sink)
- Netty (source)
注:Apache Bahir 提供对分布式分析平台(如 Apache Spark 和 Apache Flink )的扩展,通过多种流连接器和 SQL 数据源扩展其覆盖范围。
通过下面代码创建一个kafka消费者
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
// 仅 Kafka 0.8 需要
//properties.setProperty("zookeeper.connect", "localhost:2181")
properties.setProperty("group.id", "test")
//kafka1.0.0版本之后使用FlinkKafkaConsumer
stream = env
.addSource(new FlinkKafkaConsumer[String]("topic", new SimpleStringSchema(), properties))
.print()
kafka 是我工作中用到的最常见的上下游组件,在这也就只列举一个创建kafka consumer的例子了。
Transformation
Transformation 操作是DataStream生成新的DataStream的过程,在转化过程中,每个操作类型被定义成不同的Operator;DataStream的操作可以分为Single-DataStream、Multi-DataStream、物理分区三类。
- Single-DataStream
对单个DataStream数据集元素进行逻辑处理
(1)、Map(取一个元素并产生一个元素)
通过用户定义的MapFunction对DataStream进行处理
(2)、FlatMap(取一个元素并产生零个,一个或多个元素)
通过用户自定义的FlatMapFunction对DataStream进行处理
(3)、Filter(对元素进行过滤)
通过用户自定义的FilterFunction对DataStream进行处理
(4)、KeyBy
从逻辑上将DataStream划分为不相交的分区。具有相同key的所有数据都分配给同一分区
(5)、Reduce(对相同key值的数据进行逻辑运算)
通过用户自定义的ReduceFunction对DataStream进行处理
(6)、Aggregations(对相同key值的数据进行逻辑运算)
封装了sum、min、minBy、max、maxBy等聚合函数 - Multi-DataStream
(1)、Union
将两个或多个流数据集合并成一个、需保证数据格式一致
(2)、Connect、CoMap、CoFlatMap
将两个或多个不同数据类型的流数据集合并成一个;CoMapFunction、CoFlatMapFunction在Paralism 大于1的情况下会产生乱序。
(3)、Split
按照一定的规则对DataStream 进行拆分,形成两个SplitStream
(4)、Select
从拆分流中选择一个或多个流。
(5)、Iterate
通过每一次迭代计算,将计算结果反馈到下一次的迭代计算中。 - 物理分区
(1)、Random partition(随机分区)
随机将元素分配到下游分区,相对均衡,容易失去原有的数据分区结构。
dataStream.shuffle()
(2)、Round-robin partitioning
通过循环的方式对数据集中的部分进行重分区,解决数据倾斜
dataStream.rebalance()
(3)、Rescaling partitioning
将元素循环地分区到下游操作的子集;在上游并发度和下游并发度成倍数关系,Rescaling 的效果优于Round-robin
dataStream.rescale()
(4)、Broadcasting
将元素广播到每个分区。
dataStream.broadcast()
(5)、Custom partitioning(自定义分区)
用户定义的分区程序为每个元素选择目标任务。继承Partitioner类,实现partition方法即可
dataStream.partitionCustom(partitioner, 0)
今天介绍了DataStream 的整个编程模型,从source 到transformation 到sink整个完整的链路;今天就到这吧。还是那句话,作为一个菜鸟,如果各位大佬在看博客的过程中发现什么问题,欢迎随时批评指正。