Flink核心API之DataStream

(一)Flink核心API

Flink中提供了4种不同层次的API,每种API在简洁和易表达之间有自己的权衡,适用于不同的场景。目前上面3个会用得比较多。

  • 低级API(Stateful Stream Processing):提供了对时间和状态的细粒度控制,简洁性和易用性较差,
    主要应用在一些复杂事件处理逻辑上。
  • 核心API(DataStream/DataSet API):主要提供了针对流数据和批数据的处理,是对低级API进行了一
    些封装,提供了filter、sum、max、min等高级函数,简单易用,所以这些API在工作中应用还是比
    较广泛的。
  • Table API:一般与DataSet或者DataStream紧密关联,可以通过一个DataSet或DataStream创建出一个Table,然后再使用类似于filter, join,或者 select这种操作。最后还可以将一个Table对象转成DataSet或DataStream。
  • SQL:Flink的SQL底层是基于Apache Calcite,Apache Calcite实现了标准的SQL,使用起来比其他API更加灵活,因为可以直接使用SQL语句。Table API和SQL可以很容易地结合在一块使用,因为它们都返回Table对象。

在这里插入图片描述
针对这些API我们主要学习下面这些:
在这里插入图片描述

(二)DataStreamAPI

DataStream API主要分为3块:DataSource、Transformation、DataSink。
DataSource是程序的输入数据源。
Transformation是具体的操作,它对一个或多个输入数据源进行计算处理,例如map、flatMap和filter等操作。
DataSink是程序的输出,它可以把Transformation处理之后的数据输出到指定的存储介质中。

(1)DataSoure

DataSource是程序的输入数据源,Flink提供了大量内置的DataSource,也支持自定义DataSource,不过目前Flink提供的这些已经足够我们正常使用了。
Flink提供的内置输入数据源:包括基于socket、基Collection
还有就是Flink还提供了一批Connectors,可以实现读取第三方数据源,

Flink 内置                Apache Bahir
Kafka                     ActiveMQ
Kinesis Streams            Netty
RabbitMQ
NiFi
Twitter Streaming API
Google PubSub

Flink 内置:表示Flink中默认自带的。
Apache Bahir:表示需要添加这个依赖包之后才能使用的。
针对source的这些Connector,我们在实际工作中最常用的就是Kafka

  • 当程序出现错误的时候,Flink的容错机制能恢复并继续运行程序,这种错误包括机器故障、网络故障、程序故障等
    针对Flink提供的常用数据源接口,如果程序开启了checkpoint快照机制,Flink可以提供这些容错性保证
DataSource     容错保证     备注
Socket        at most once
Collection    exactly once
Kafka         exactly once  需要使用0.10及以上版本
  1. Collection集合的使用
/**
* 基于collection的source的使用
* 注意:这个source的主要应用场景是模拟测试代码流程的时候使用
* Created by xuwei
*/
object StreamCollectionSourceScala {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
//使用collection集合生成DataStream
val text = env.fromCollection(Array(1,2,3,4,5))
text.print().setParallelism(1)
env.execute("StreamCollectionSource")
}
}

(2)Transformation

  1. transformation是Flink程序的计算算子,负责对数据进行处理,Flink提供了大量的算子,其实Flink中的大部分算子的使用和spark中算子的使用是一样的,下面我们来看一下:
算子    解释
map     输入一个元素进行处理,返回一个元素
flatMap 输入一个元素进行处理,可以返回多个元素
filter  对数据进行过滤,符合条件的数据会被留下
keyBy   根据key分组,相同key的数据会进入同一个分区
reduce  对当前元素和上一次的结果进行聚合操作
aggregations sum(),min(),max()

这里面的算子的用法其实和spark中对应算子的用法是一致的,这里面的map、flatmap、keyBy、reduce、sum这些算子我们都用过了。

用法不同的算子:

算子       解释
union    合并多个流,多个流的数据类型必须一致
connect  只能连接两个流,两个流的数据类型可以不同
split    根据规则把一个数据流切分为多个流
shuffle  随机分区
rebalance 对数据集进行再平衡,重分区,消除数据倾斜
rescale  重分区
partitionCustom 自定义分区
  1. union:表示合并多个流,但是多个流的数据类型必须一致。多个流join之后,就变成了一个流
    应用场景:多种数据源的数据类型一致,数据处理规则也一致
    代码:
/**
* 合并多个流,多个流的数据类型必须一致
* 应用场景:多种数据源的数据类型一致,数据处理规则也一致
*/
object StreamUnionScala {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
//第1份数据流
val text1 = env.fromCollection(Array(1, 2, 3, 4, 5))
//第2份数据流
val text2 = env.fromCollection(Array(6, 7, 8, 9, 10))
//合并流
val unionStream = text1.union(text2)
//打印流中的数据
unionStream.print().setParallelism(1)
env.execute("StreamUnionScala")
}
}
  1. connect:只能连接两个流,两个流的数据类型可以不同
    两个流被connect之后,只是被放到了同一个流中,它们内部依然保持各自的数据和形式不发生任何变化,两个流相互独立。
    connect方法会返回connectedStream,在connectedStream中需要使用CoMap、CoFlatMap这种函数,类似于map和flatmap
    代码:
/**
* 只能连接两个流,两个流的数据类型可以不同
* 应用:可以将两种不同格式的数据统一成一种格式
*/
object StreamConnectScala {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
//第1份数据流
val text1 = env.fromElements("user:tom,age:18")
//第2份数据流
val text2 = env.fromElements("user:jack_age:20")
//连接两个流
val connectStream = text1.connect(text2)
connectStream.map(new CoMapFunction[String,String,String] {
//处理第1份数据流中的数据
override def map1(value: String): String = {
value.replace(",","-")
}
//处理第2份数据流中的数据
override def map2(value: String): String = {
value.replace("_","-")
}
}).print().setParallelism(1)
env.execute("StreamConnectScala")
}
}
  1. split:根据规则把一个数据流切分为多个流
    注意:split只能分一次流,切分出来的流不能继续分流
    split需要和select配合使用,选择切分后的流
    应用场景:将一份数据流切分为多份,便于针对每一份数据使用不同的处理逻辑

代码:

/**
* 根据规则把一个数据流切分为多个流
* 注意:split只能分一次流,切分出来的流不能继续分流
* split需要和select配合使用,选择切分后的流
* 应用场景:将一份数据流切分为多份,便于针对每一份数据使用不同的处理逻辑
*/
object StreamSplitScala {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
import org.apache.flink.api.scala._
//按照数据的奇偶性对数据进行分流
val splitStream = text.split(new OutputSelector[Int] {
override def select(value: Int): lang.Iterable[String] = {
val list = new util.ArrayList[String]()
if(value % 2 == 0){
list.add("even")//偶数
}else{
list.add("odd")//奇数
}
list
}
})
//选择流
val evenStream = splitStream.select("even")
evenStream.print().setParallelism(1)
env.execute("StreamSplitScala")
}
}

如果是只需要切分一次的话使用split或者side output都可以
如果想要切分多次,就不能使用split了,需要使用side output
在这里插入图片描述
4. 和分区有关的算子

|算子| 解释|
|random| 随机分区|
|rebalance| 对数据集进行再平衡,重分区,消除数据倾斜|
|rescale| 重分区|
|custom partition| 自定义分区|
random:随机分区,它表示将上游数据随机分发到下游算子实例的每个分区中,在代码层面体现是调用shuffle()函数
查看源码 ,shuffle底层对应的是ShufflePartitioner这个类
这个类里面有一个selectChannel函数,这个函数会计算数据将会被发送给哪个分区,里面使用的是random.nextInt,所以说是随机的。
rebalance:重新平衡分区(循环分区),我觉得叫循环分区更好理解,它表示对数据集进行再平衡,消除数据倾斜,为每个分区创建相同的负载,其实就是通过循环的方式给下游算子实例的每个分区分配数据,
在代码层面体现是调用rebalance()函数查看源码,rebalance底层对应的是RebalancePartitioner这个类
这 个 类 里 面 有 一 个 setup 和 selectChannel 函 数 , setup 函 数 会 根 据 分 区 数 初 始 化 一 个 随 机 值
nextChannelToSendTo ,然后selectChannel函数会使用nextChannelToSendTo 加1和分区数取模,把计算的值再赋给nextChannelToSendTo ,后面以此类推,其实就可以实现向下游算子实例的多个分区循环发送数据了,这样每个分区获取到的数据基本一致。

(3)DataSink

DataSink是 输出组件,负责把计算好的数据输出到其它存储介质中
Flink支持把流数据输出到文件中,不过在实际工作中这种场景不多,因为流数据处理之后一般会存储到一些消息队列里面,或者数据库里面,很少会保存到文件中的。
还有就是print,直接打印,这个其实我们已经用了很多次了,这种用法主要是在测试的时候使用的,方便查看输出的结果信息
Flink提供了一批Connectors,可以实现输出到第三方目的地

Flink 内置       Apache Bahir
Kafka            ActiveMQ
Cassandra         Flume
Kinesis Streams   Redis
Elasticsearch     Akka
Hadoop FileSysterm
RabbitMQ
NiFi
JDBC

针对sink的这些connector,在实际工作中最常用的是kafka、redis
针对Flink提供的常用sink组件,可以提供这些容错性保证

DataSink     容错保证    备注
Redis      at least once
Kafka
at least once / exactly
once
Kafka0.9和0.10提供at least once,Kafka0.11及以上提供
exactly once

注意:redis sink是在Bahir这个依赖包中,所以在pom.xml中需要添加对应的依赖

/**
* 需求:接收Socket传输过来的数据,把数据保存到Redis的list队列中。
*/
object StreamRedisSinkScala {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
//连接socket获取输入数据
val text = env.socketTextStream("bigdata04", 9001)
import org.apache.flink.api.scala._
//组装数据,这里组装的是tuple2类型
//第一个元素是指list队列的key名称
//第二个元素是指需要向list队列中添加的元素
val listData = text.map(word => ("l_words_scala", word))
//指定redisSink
val conf = new FlinkJedisPoolConfig.Builder().setHost("bigdata04").setPort
val redisSink = new RedisSink[Tuple2[String, String]](conf, new MyRedisMap
listData.addSink(redisSink)
env.execute("StreamRedisSinkScala")
}
class MyRedisMapper extends RedisMapper[Tuple2[String,String]]{
//指定具体的操作命令
override def getCommandDescription: RedisCommandDescription = {
new RedisCommandDescription(RedisCommand.LPUSH)
}
//获取key
override def getKeyFromData(data: (String, String)): String = {
data._1
}
//获取value
override def getValueFromData(data: (String, String)): String = {
data._2
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值