实时计算FLINK

前言:
今年阿里的实时计算峰值达到了破纪录的每秒40亿条记录,数据量也达到了惊人的7TB每秒,相当于一秒钟需要读完500万本《新华字典》。Flink的强悍之处,阿里屡试不爽
实时计算FLINK_第1张图片

FIRST PART.什么是FLINK
1.1初识Flink
在德语中,Flink一词表示快速和灵巧,项目采用一只松鼠的彩色图案作为logo,这不仅是因为松鼠具有快速和灵巧的特点,还因为柏林的松鼠有一种迷人的红棕色,而Flink的松鼠logo拥有可爱的尾巴,尾巴的颜色与Apache软件基金会的logo颜色相呼应,也就是说,这是一只Apache风格的松鼠。
实时计算FLINK_第2张图片
在这里插入图片描述

Flink Logo
Flink项目的理念是:“Apache Flink是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架”。
Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。
1.2 Flink的重要特点
1.2.1 事件驱动型(Event-driven)
事件驱动型应用是一类具有状态的应用,它从一个或多个事件流提取数据,并根据到来的事件触发计算、状态更新或其他外部动作。比较典型的就是以kafka为代表的消息队列几乎都是事件驱动型应用。
与之不同的就是SparkStreaming微批次,如图:
在这里插入图片描述

事件驱动型:
实时计算FLINK_第3张图片

1.2.2 流与批的世界观
批处理的特点是有界、持久、大量,非常适合需要访问全套记录才能完成的计算工作,一般用于离线统计。
流处理的特点是无界、实时, 无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。
在spark的世界观中,一切都是由批次组成的,离线数据是一个大批次,而实时数据是由一个一个无限的小批次组成的。
而在flink的世界观中,一切都是由流组成的,离线数据是有界限的流,实时数据是一个没有界限的流,这就是所谓的有界流和无界流。
无界数据流:无界数据流有一个开始但是没有结束,它们不会在生成时终止并提供数据,必须连续处理无界流,也就是说必须在获取后立即处理event。对于无界数据流我们无法等待所有数据都到达,因为输入是无界的,并且在任何时间点都不会完成。处理无界数据通常要求以特定顺序(例如事件发生的顺序)获取event,以便能够推断结果完整性。
有界数据流:有界数据流有明确定义的开始和结束,可以在执行任何计算之前通过获取所有数据来处理有界流,处理有界流不需要有序获取,因为可以始终对有界数据集进行排序,有界流的处理也称为批处理。
实时计算FLINK_第4张图片

这种以流为世界观的架构,获得的最大好处就是具有极低的延迟。

SECOND PART. Flink运行架构
1.1Flink运行时的组件
联系到生活中我们理解下面的概念:
jobManger像一个经理 负责总体的调配任务 不实际参与任务的执行,只负责调度
ResourceManger像一个HR 负责调度 TaskManger
TsakMager像一个包工头 启动之后 HR报告他有多少苦力:收到HR的指令后,包工头就会将 一个或者多个苦力提供给经理调用 ,在执行过程中一个包工头可以和其他运行同一应用程序的包工头交换数据

jobManger作业管理器 首先接受要执行的应用程序(作业图JobGraph、逻辑数据流图 和打包了所有的类和jar包)负责管理调度 提交任务后首先调度 jobManger 生成执行图 根据jiob的资源(并行度,slot的个数后,向资源管理器(ResourceManger)请求执行任务必要的资源,也就是Slot,取得了足够的资源后向TaskManger分派任务后,JM负责进行资源的协调,checkpoint,不参与任务的执行
TsakMager 启动之后 先向资源管理器注册他的插槽:收到资源管理器的指令后,TaskManger就会将 一个或者多个插槽提供给给JobManger调用 ,在执行过程中一个TaskManger可以和其他运行同一应用程序的TM交换数据
dispacher 为外界提供接口

任务提交流程 提交任务 经理向HR要苦力->HR启动包工头 ->包工头向HR提交苦力名单 ->HR告诉包工头向经理提供Slot->包工头向经理提供Slot->经理给交在苦力中要执行的任务

Flink运行时架构主要包括四个不同的组件,它们会在运行流处理应用程序时协同工作:作业管理器(JobManager)、资源管理器(ResourceManager)、任务管理器(TaskManager),以及分发器(Dispatcher)。因为Flink是用Java和Scala实现的,所以所有组件都会运行在Java虚拟机上。每个组件的职责如下:
作业管理器(JobManager)
控制一个应用程序执行的主进程,也就是说,每个应用程序都会被一个不同的JobManager所控制执行。JobManager会先接收到要执行的应用程序,这个应用程序会包括:作业图(JobGraph)、逻辑数据流图(logical dataflow graph)和打包了所有的类、库和其它资源的JAR包。JobManager会把JobGraph转换成一个物理层面的数据流图,这个图被叫做“执行图”(ExecutionGraph),包含了所有可以并发执行的任务。JobManager会向资源管理器(ResourceManager)请求执行任务必要的资源,也就是任务管理器(TaskManager)上的插槽(slot)。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的TaskManager上。而在运行过程中,JobManager会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。
资源管理器(ResourceManager)
主要负责管理任务管理器(TaskManager)的插槽(slot),TaskManger插槽是Flink中定义的处理资源单元。Flink为不同的环境和资源管理工具提供了不同资源管理器,比如YARN、Mesos、K8s,以及standalone部署。当JobManager申请插槽资源时,ResourceManager会将有空闲插槽的TaskManager分配给JobManager。如果ResourceManager没有足够的插槽来满足JobManager的请求,它还可以向资源提供平台发起会话,以提供启动TaskManager进程的容器。另外,ResourceManager还负责终止空闲的TaskManager,释放计算资源。
任务管理器(TaskManager)
Flink中的工作进程。通常在Flink中会有多个TaskManager运行,每一个TaskManager都包含了一定数量的插槽(slots)。插槽的数量限制了TaskManager能够执行的任务数量。启动之后,TaskManager会向资源管理器注册它的插槽;收到资源管理器的指令后,TaskManager就会将一个或者多个插槽提供给JobManager调用。JobManager就可以向插槽分配任务(tasks)来执行了。在执行过程中,一个TaskManager可以跟其它运行同一应用程序的TaskManager交换数据。

Window-demo 实时输出五秒内体温最高的人的信息
package com.morant.apitest

import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

object WindowTest {
def main(args: Array[String]): Unit = {

val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getConfig.setAutoWatermarkInterval(100)

   val inputStream: DataStream[String] = env.readTextFile("D:\\DEVELOP_CODE\\workspace\\FlinkTest\\src\\main\\resources\\Temperature.txt")

// val inputStream = env.socketTextStream(“172.19.177.124”, 7777)
// Transform操作 val dataStream: DataStream[Temperature] = inputStream
.map(data => {
val dataArray = data.split(",")
Temperature(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
})
// .assignAscendingTimestamps(_.timestamp * 1000L) // 对乱序数据分配时间戳和watermark .assignTimestampsAndWatermarks( new BoundedOutOfOrdernessTimestampExtractorTemperature {
override def extractTimestamp(t: Temperature): Long = t.timestamp * 1000L } )

// 统计每个温度计器每5秒内的温度最大值 val processedStream = dataStream
.keyBy(_.id)
// .window(TumblingProcessingTimeWindows.of(Time.hours(1))) .timeWindow(Time.seconds(5)) // 定义长度为5秒的滚动窗口 .reduce((curMaxTempData, newData) =>
Temperature(curMaxTempData.id, newData.timestamp, newData.temperature.max(curMaxTempData.temperature)
)
)
// .reduce()
processedStream.print()
env.execute(“window test”)
}
}
2.1 Window及基于体温数据的Demo
2.1.1 Window概述
streaming流式计算是一种被设计用于处理无限数据集的数据处理引擎,而无限数据集是指一种不断增长的本质上无限的数据集,而window是一种切割无限数据为有限块进行处理的手段。
Window是无限数据流处理的核心,Window将一个无限的stream拆分成有限大小的”buckets”桶,我们可以在这些桶上做计算操作。
3.1.2 Window类型
Window可以分成两类:
Ø CountWindow:按照指定的数据条数生成一个Window,与时间无关。
Ø TimeWindow:按照时间生成Window。
对于TimeWindow,可以根据窗口实现原理的不同分成三类:滚动窗口(Tumbling Window)、滑动窗口(Sliding Window)和会话窗口(Session Window)。

  1. 滚动窗口(Tumbling Windows)
    将数据依据固定的窗口长度对数据进行切片。
    特点:时间对齐,窗口长度固定,没有重叠。
    滚动窗口分配器将每个元素分配到一个指定窗口大小的窗口中,滚动窗口有一个固定的大小,并且不会出现重叠。例如:如果你指定了一个5分钟大小的滚动窗口,窗口的创建如下图所示:
    实时计算FLINK_第5张图片

图 滚动窗口
适用场景:适合做BI统计等(做每个时间段的聚合计算)。
2.2. 滑动窗口(Sliding Windows)
滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口长度和滑动间隔组成。
特点:时间对齐,窗口长度固定,可以有重叠。
滑动窗口分配器将元素分配到固定长度的窗口中,与滚动窗口类似,窗口的大小由窗口大小参数来配置,另一个窗口滑动参数控制滑动窗口开始的频率。因此,滑动窗口如果滑动参数小于窗口大小的话,窗口是可以重叠的,在这种情况下元素会被分配到多个窗口中。
例如,你有10分钟的窗口和5分钟的滑动,那么每个窗口中5分钟的窗口里包含着上个10分钟产生的数据,如下图所示:
实时计算FLINK_第6张图片

图 滑动窗口
适用场景:对最近一个时间段内的统计(求某接口最近5min的失败率来决定是否要报警)。
2.3. 会话窗口(Session Windows)
由一系列事件组合一个指定时间长度的timeout间隙组成,类似于web应用的session,也就是一段时间没有接收到新数据就会生成新的窗口。
特点:时间无对齐。
session窗口分配器通过session活动来对元素进行分组,session窗口跟滚动窗口和滑动窗口相比,不会有重叠和固定的开始时间和结束时间的情况,相反,当它在一个固定的时间周期内不再收到元素,即非活动间隔产生,那个这个窗口就会关闭。一个session窗口通过一个session间隔来配置,这个session间隔定义了非活跃周期的长度,当这个非活跃周期产生,那么当前的session将关闭并且后续的元素将被分配到新的session窗口中去。
实时计算FLINK_第7张图片

2.4Window-demo 实时输出五秒内体温最高的人的信息
package com.morant.apitest

import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time

object WindowTest {
def main(args: Array[String]): Unit = {

val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.getConfig.setAutoWatermarkInterval(100)

   val inputStream: DataStream[String] = env.readTextFile("D:\\DEVELOP_CODE\\workspace\\FlinkTest\\src\\main\\resources\\Temperature.txt")

// val inputStream = env.socketTextStream(“172.19.177.124”, 7777)
// Transform操作 val dataStream: DataStream[Temperature] = inputStream
.map(data => {
val dataArray = data.split(",")
Temperature(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
})
// .assignAscendingTimestamps(_.timestamp * 1000L) // 对乱序数据分配时间戳和watermark .assignTimestampsAndWatermarks( new BoundedOutOfOrdernessTimestampExtractorTemperature {
override def extractTimestamp(t: Temperature): Long = t.timestamp * 1000L } )

// 统计每个温度计器每5秒内的温度最大值 val processedStream = dataStream
.keyBy(_.id)
// .window(TumblingProcessingTimeWindows.of(Time.hours(1))) .timeWindow(Time.seconds(5)) // 定义长度为5秒的滚动窗口 .reduce((curMaxTempData, newData) =>
Temperature(curMaxTempData.id, newData.timestamp, newData.temperature.max(curMaxTempData.temperature)
)
)
// .reduce()
processedStream.print()
env.execute(“window test”)
}
}
3.1侧输出流(SideOutput)及体温大于37.5放入测输出流的Demo
大部分的DataStream API的算子的输出是单一输出,也就是某种数据类型的流。除了split算子,可以将一条流分成多条流,这些流的数据类型也都相同。process function的side outputs功能可以产生多条流,并且这些流的数据类型可以不一样。一个side output可以定义为OutputTag[X]对象,X是输出流的数据类型。process function可以通过Context对象发射一个事件到一个或者多个side outputs。

SideOutPut 将体温高于37.5的信息再测输出流中输出
case class Temperature( id: String, timestamp: Long, temperature: Double )
object SideOutputTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment env.setParallelism(1)

   val inputStream: DataStream[String] = env.readTextFile("D:\\DEVELOP_CODE\\workspace\\FlinkTest\\src\\main\\resources\\Temperature.txt")

// val inputStream = env.socketTextStream(“192.168.1.101”, 7777)
// Transform操作 val dataStream: DataStream[Temperature] = inputStream
.map(data => {
val dataArray = data.split(",")
Temperature(dataArray(0).trim, dataArray(1).trim.toLong, dataArray(2).trim.toDouble)
})

val highTempStream = dataStream
.process(new SplitTempMonitor())

highTempStream.print(“low”)
highTempStream.getSideOutput(new OutputTagString).print(“high”)

env.execute(“process function test”)
}
}

// 自定义process function,实现分流操作class SplitTempMonitor() extends ProcessFunction[Temperature, Temperature]{
override def processElement(value: Temperature, ctx: ProcessFunction[Temperature, Temperature]#Context, out: Collector[Temperature]): Unit = {
// 判断当前数据的温度值,如果在37.5以上,输出到侧输出流 if( value.temperature > 37.5 ){
ctx.output(new OutputTagString, value.id+"->有病")
} else{
// 37.5度以下的数据,输出到主流 out.collect(value)
}
}
}

你可能感兴趣的:(实时计算FLINK)