getExecutionEnvironment
创建一个执行环境,表示当前执行程序的上下文。getExecutionEnvironment 会根据查询运行的方 式决定返回什么样的运行环境,是最常用的一种创建执行环境的方式。
val env = StreamExecutionEnvironment.getExecutionEnvironment
如果没有设置并行度,会以 flink-conf.yaml 中的配置为准,默认是 1。
Source
从集合读取数据
// 定义样例类,传感器 id,时间戳,温度
package com.slabout.api
import org.apache.flink.streaming.api.scala._
object SourceTest {
case class SensorReading(id: String, timestamp: Long, temperature: Double)
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val stream1 = env
.fromCollection(List(
SensorReading("sensor_1", 1547718199, 35.80018327300259),
SensorReading("sensor_6", 1547718201, 15.402984393403084),
SensorReading("sensor_7", 1547718202, 6.720945201171228),
SensorReading("sensor_10", 1547718205, 38.101067604893444)
))
stream1.print("stream1:").setParallelism(1)
env.execute()
}
}
从文件读取数据
val stream2 = env.readTextFile("YOUR_FILE_PATH")
以 kafka 消息队列的数据作为来源 (需要引入 kafka 连接器的依赖:)
<!--
https://mvnrepository.com/artifact/org.apache.flink/flink-connector-kafka-0.11
-->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka-0.11_2.11</artifactId>
<version>1.7.2</version>
</dependency>
具体代码 (设置配置属性,通过addSource 传入 参数是 (主题, 值反序列化的工具 ,配置属性) )
object SourceTest {
case class SensorReading(id: String, timestamp: Long, temperature: Double)
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
val properties = new Properties()
properties.setProperty("bootstrap.servers", "localhost:9092")
properties.setProperty("group.id", "consumer-group")
properties.setProperty("key.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("value.deserializer",
"org.apache.kafka.common.serialization.StringDeserializer")
properties.setProperty("auto.offset.reset", "latest")
val stream3 = env.addSource(new FlinkKafkaConsumer011[String]("sensor", new
SimpleStringSchema(), properties))
stream3.print("stream3:").setParallelism(1)
env.execute()
}
}
测试的话,要先将集群的kafka启动起来。要进行测试的话,则在kafka上面创建一个生产者,然后生产数据。程序接收数据消费,然后输出
在大数据的框架中,为了容错性的保证,都在框架中加入了checkpoint机制,
flink 从kafka中读数据,读一条则更改一次,而Spark是一批一批的,
如果kafka读取数据,数据已经读进来了,kafka的偏移量改了,但是突然出现了异常,则需要回滚到上一个检查点的位置,然后重新再开始读数据,(如果偏移量不改变,则可能造成不能重复读取,也就造成数据丢失)
Spark如何处理:
方法一:在数据消费的时候不更改偏移量,而是等到数据完成后再更改偏移量,手动写程序提交偏移量
方法二:可以在恢复之前的那个状态的时候, 也就是之前那个保存点的时候,应该把之前的那些数据都要去存下来,存下来之后,若要kafka读取数据的话,则要手动更改新的偏移量重新提交一次(因为已经有少部分数据存下来了嘛)
这是Spark中对状态一致性的保证
Flink如何处理:
1、因为flink是来一条处理一条,和Spark不一样,
2、flink本身是有状态的流处理,在这个过程中可以保存状态,所以flink可以把kafka当前的偏移量作为状态保存下来(如果有异常,到时候恢复的时候,就可以从之前存盘的地方重新读取,重新恢复,也就是相当于把之前的偏移量恢复回来。)
这个过程中flink自动的保证与kafka的状态一致性,不用我们干涉。
自定义Source
package com.slabout.api
import java.util.Random
import com.slabout.api.SourceCustom.SensorReading
import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.api.scala._
object SourceCustom {
case class SensorReading(id: String, timestamp: Long, temperature: Double)
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
//在测试中,可以自定义 source。需要做的,只是传入 一个 SourceFunction 就可以。具体调用如下:
val stream4 = env.addSource( new MySensorSource() )
stream4.print("stream4:").setParallelism(1)
env.execute()
}
}
//可以随机生成传感器数据,MySensorSource 具体的代码实现如下:
class MySensorSource