20-02-flink

 

standLone是不建议的,真正的是yarn模式的。

./yarn-session.sh -n 2 -s 2 -jm 1024 -tm 1024 -nm test -d

实际的生产使用的yarn的。

看下这个包,默认是没有这个包的。

其中yarn的resiurceManager是独享的,flnk的resourceManager是共享的。

参数的意思:

-n是taskManager的数量

一个cpu在一个时间点是只能执行一个线程的。

关于yarn和k8s。

不细讲了。

---01---

正常情况下每一个job会被一个jobManager执行。

yarn-session的所有的job是共享一个resourceManager和jobManager的。

k8s的底层是docker。

箭头是找的yarn的。

---02---

这个图是yarn的job提交方式。

yarn的session模式。

keyBy是不能设置并行度的。

4.3任务调度。

算子和任务:

不同算子的任务是可以放在同一个slot里面的。

每一个算子是一个任务,每一个算子有一个并行度,每一个任务有子任务。

1.什么是任务,任务就是一步操作而已,具体的一步计算是可以设置并行度的,比如sum等。但是keyBy是不可以设置并行度的。

一段代码生成多少个任务?

什么样的任务可以合并到一起呢?

这个就合并在一起了

1.one to one

2.并行度相同的

我们回忆之前的操作:

这么看这里面的任务是远远超过6个的。

2.slot到底是什么,一段代码需要多少个slot执行。

一个taskManager相当于一个jvm的进程。slot相当于一个线程推荐一个核心。一个slot允许有不同的任务的子任务。

笔记总结:

TM -- process 进程
Task on slot -- thread 线程

每个线程执行在固定的计算资源上,这个资源就是slot;
slot之间内存是独享的,CPU不是独享的
所以slot数量最好配成 CPU 核心数

并行的概念:
数据并行 —— 同一个任务,不同的并行子任务,同时处理不同的数据
任务并行 —— 同一时间,不同的slot在执行不同的任务

允许slot共享,可以提高资源的利用率

一个流处理程序需要的slot数量,其实就是所有任务中最大的那个并行度

如果并行度相同、one-to-one数据传输,那么可以把多个算子合并成一个任务

TM的数量和slot数量,决定了并行处理的最大能力(静态)
但是不一定程序执行时一定都用到。程序执行时的并行度才是用到的能力(动态)。
------------------------------------------------------
核心问题:
1. 任务是什么?一段代码到底会生成多少任务?
代码中定义的每一步操作(算子,operator)就是一个任务
算子可以设置并行度,所以每一步操作可以有多个并行的子任务,子任务就是算子的多个并行。
Flink可以将前后执行的不同的任务合并起来
2. slot到底是什么,slot跟任务的关系?一段代码到底需要多少个slot来执行?
slot是TM拥有的计算资源的一个子集,一个任务必须在一个slot上执行
每一个算子的并行任务,必需执行在不同的slot上
如果是不同算子的任务,可以共享一个slot
一般情况下,一段代码执行需要的slot数量,就是并行度最大的算子的并行度
3. 并行度和slot数量的关系?
并行度和任务有关,就是每一个算子拥有的并行任务数量;动态概念
slot数量只跟TM的配置有关,代表TM并行处理数据的能力,静态概念
4. 什么样的任务能够合并在一起?
one-to-one操作,并行度相同

不同算子的子任务是可以放在同一个slot里面去跑的。

所以slot的数量去取决于并行度最大的算子,并行度就是slot的数量。

一个taskmanager可以认为是jvm的进程,一个slot是线程。

一个算子并行的子任务一定在不同的slot上执行。

------------------02-03--------------

不是多少个任务多少个slot就是算子的最大并行度个slot。

首先看下我们的能力,这个最大支持的并行度是9

代码>提交>集群配置文件

并行度是taskmanager*slot的值。

程序流图:

总的流程图:

拆开是按照并行度的拆开的。

一个很经典的图:

圆圈是算子,底下的角标是并行度。

16个任务。4个slot。

其中a和b是可以不跨分区的。

举例:

这个有5个任务,只要有2个slot就可以跑起来了。

跨slot跨RM是有开销的。

如果不是一对一就是reblance,否则就是keyBy。

---

ketBy得到的是:

这一步是不可以设置并行度的。

一台机器一般是一个taskManager,但是没有限制的。

---04---

快手flink

session当前是公用的RM。

---05---

流处理就这几步:

source:代码

我们在集合读取数据

在文件读取:

 

  val stream1: DataStream[SensorReading] = env.fromCollection( List(
      SensorReading("sensor_1", 1547718199, 35.8),
      SensorReading("sensor_6", 1547718201, 15.4),
      SensorReading("sensor_7", 1547718202, 6.7),
      SensorReading("sensor_10", 1547718205, 38.1),
      SensorReading("sensor_1", 1547718207, 37.2),
      SensorReading("sensor_1", 1547718212, 33.5),
      SensorReading("sensor_1", 1547718215, 38.1)
    ) )

scala的类:https://www.runoob.com/scala/scala-classes-objects.html

代码:

package com.atguigu.apitest

import org.apache.flink.streaming.api.scala._

case class SensorReadingMy(id: String, timestamp: Long, temperature: Double)

object SoucetestMy {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val stream1: DataStream[SensorReadingMy] = env.fromCollection(List(
      SensorReadingMy("sensor_1", 1547718199, 35.8),
      SensorReadingMy("sensor_6", 1547718201, 15.4),
      SensorReadingMy("sensor_7", 1547718202, 6.7),
      SensorReadingMy("sensor_10", 1547718205, 38.1),
      SensorReadingMy("sensor_1", 1547718207, 37.2),
      SensorReadingMy("sensor_1", 1547718212, 33.5),
      SensorReadingMy("sensor_1", 1547718215, 38.1)
    ))
    stream1.print("stream2")
    env.execute("stream123")
  }
}

print也是sink。

package com.atguigu.apitest

import org.apache.flink.streaming.api.scala._

case class SensorReadingMy(id: String, timestamp: Long, temperature: Double)

object SoucetestMy {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val stream1: DataStream[String] = env.readTextFile("D:\\codeMy\\CODY_MY_AFTER__KE\\sggBigData\\20-flink\\FlinkTutorial\\src\\main\\resources\\sensor.txt")
    stream1.print("stream")
    env.execute("stream")
  }
}
   val properties = new Properties()
    properties.setProperty("bootstrap.servers", "192.168.244.133:9092")
    //properties.setProperty("group.id", "ab")
    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 stream4 = env.addSource( new FlinkKafkaConsumer011[String]("testinfo", new SimpleStringSchema(), properties) )

 

011继承010,010继承009

在kafka中读取数据,需要也纳入kafka的包:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka-0.11_2.11</artifactId>
    <version>1.10.0</version>
</dependency>

我们需要启动下zk和kafka:https://blog.csdn.net/qq_28764557/article/details/109913185

我在kafka创建了一个topic:spark

我们是可以消费成功的:

我的代码:

---06---

同一个算子并行的子任务必须占据不同的slot。不同的算子是可以共享同一个slot的。

这里面直接是5个任务依次排开。

我们自己控制每一个任务放在不同的solt里面。

这个意思是在同一个共享组里面的才可以共享slot的,这个组的名字是1和2

不同组的必须放在不同的solt里面去的,同一个组是可以共享slot的。

没有指定的是在default里面。

指定的和后面的任务在自己指定的组里面。

这个是6个任务的,两个slot就够了。

一般是选取并行度最大的,其他的是slot选取并行度最大的。

按照分组之后呢?

source是1个slot 两个fm是两个 后面的可以共享slot 5个slot

---

这个是不合并算子链,就是不合并为一个大的任务。

只断开前面不断开后面,如果不断开的话会自己进行连接的。主要是用来断开任务的。

全局可配置。dis是断开前后的。

---07---

看一个代码:

实际做项目时候往往都是kafka的。

实现一个自定义的sourceFunction:可以自动生成测试数据。

代码:

package com.atguigu.apitest

import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.api.scala._

import scala.util.Random

case class SensorReadingMy(id: String, timestamp: Long, temperature: Double)

object SoucetestMy {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val stream = env.addSource( new MySensorSourcMy() )
    stream.print("stream")
    env.execute("stream")
  }
}


// 实现一个自定义的 SourceFunction,自动生成测试数据
class MySensorSourcMy() extends SourceFunction[SensorReading]{
  // 定义一个flag,表示数据源是否正常运行
  var running: Boolean = true

  override def cancel(): Unit = running = false

  // 随机生成 SensorReading数据
  override def run(ctx: SourceFunction.SourceContext[SensorReading]): Unit = {
    // 定义一个随机数发生器
    val rand = new Random()
    // 随机生成 10个传感器的温度值,并且不停在之前温度基础上更新(随机上下波动)
    // 首先生成 10个传感器的初始温度
    var curTemps = 1.to(10).map(
      i => ("sensor_" + i, 60 + rand.nextGaussian() * 20)
    )
    // 无限循环,生成随机数据流
    while(running){
      // 在当前温度基础上,随机生成微小波动
      curTemps = curTemps.map(
        data => (data._1, data._2 + rand.nextGaussian())
      )
      // 获取当前系统时间
      val curTs = System.currentTimeMillis()
      // 包装成样例类,用ctx发出数据 ctx搜集起来就发送到flink里面去了
      curTemps.foreach(
        data => ctx.collect(SensorReading(data._1, curTs, data._2))
      )
      // 定义间隔时间
      Thread.sleep(1000L)
    }
  }
}

---08---

map等操作是不需要keyBy的。

什么是转换算子,除了输入输出都是transform算子。

keyby是根据hashcode做的重分区的。

经过keyBy才会走聚合函数sum等。在keyBy之后可以应用的算子是滚动的聚合算子,只能在keyBy之后运用的。

keyby之后会就会形成keyedStream的。

reduce算子:https://blog.csdn.net/dinghua_xuexi/article/details/107766222

tranform:

1. keyBy
基于key的hash code重分区;
同一个key只能在一个分区内处理,一个分区内可以有不同的key的数据
keyBy之后的KeyedStream上的所有操作,针对的作用域都只是当前的key

2. 滚动聚合操作
DataStream没有聚合操作,目前所有的聚合操作都是针对KeyedStream

3. 多流转换算子
split-select, connect-comap/coflatmap 成对出现
先转换成 SplitStream,ConnectedStreams,然后再通过select/comap操作转换回来DataStream
所谓coMap,其实就是基于ConnectedStreams的map方法,里面传入的参数是CoMapFunction

4. 富函数
是函数类的增强版,可以有生命周期方法,还可以获取运行时上下文,在运行时上下文可以对state进行操作
flink有状态的流式计算,做状态编程,就是基于RichFunction的

代码:

scala不new就可以新建对象:https://blog.csdn.net/qq_36330643/article/details/75268850

这个代码十分重要的。

第一种传递的方法:注意这个指的是第一个位置的元素:

第二种传递方法:也可以传字段的名称:

第三种传递方法:还可以传一个函数的,T是DataStream的元素,K是输出就是键Key。

第四种传递方式:还可以是keySelector

// 自定义函数类,key选择器 泛型前面的类型是input的类型 后i面是返回的key的类型
class MyIDSelector() extends KeySelector[SensorReading, String]{
  // 里面必须要实现一个getKey的方法
  override def getKey(value: SensorReading): String = value.id
}

实现的代码:

// 自定义函数类,key选择器 泛型前面的类型是input的类型 后i面是返回的key的类型
class MyIDSelector() extends KeySelector[SensorReading, String]{
  // 里面必须要实现一个getKey的方法
  override def getKey(value: SensorReading): String = value.id
}

看下keyBy的类型,keyBy之后得到的类型就是KeyedStream。称作分区或者键控流。我们看到keyedStream是一个元组的类型的,其中T是输入的类型,key的类型是一个元组,因为flink不知道是什么类型的。

四步:

sum滚动聚合,可以给一个字段或者position。

注意keyBy后面针对的各种操作都是针对当前的分组是有效的。相当于groupBy key,flink不知道我们的key是什么类型的,所以只能包装为元组的类型。

min和minBy的区别。

min后面的各种操作都只针对的是当前的分组。时间戳是按照第一个输出的,时间戳是按照第一个时间戳输出的,所以是有些不一样的。

minBy是完全对应的。

---

reduce是在当前的基础上再次聚合,类型是相同的。

看下reduce,这是十分重要的:

我们的需求,最大的timeStemap和最小的温度值。

注意类型不能变的。

聚合之后在开始的基础上再次聚合。

自定义:reduce

有一个需求,其中time取最大值,温度取最小值。

自己写reduceFunction:

获得侧输出流。

---09---

split分流:是把一个流在逻辑上分成两个流,只是相当于盖个戳而已。

   // 3.  分流
    val splitStream: SplitStream[SensorReading] = dataStream
      .split( data => {
        if( data.temperature > 30 )
          Seq("high")
        else
          Seq("low")
      } )
    val highTempStream: DataStream[SensorReading] = splitStream.select("high")
    val lowTempStream: DataStream[SensorReading] = splitStream.select("low")
    val allTempStream: DataStream[SensorReading] = splitStream.select("high", "low")
    env.execute("transform test job")

合流操作,合流只是形式上合在了一起,其实底层还是各自是分开的。

package com.atguigu.apitest

import org.apache.flink.streaming.api.scala.{DataStream, SplitStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.scala._

object split {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    val inputStreamFromFile: DataStream[String] = env.readTextFile("D:\\codeMy\\CODY_MY_AFTER__KE\\sggBigData\\20-flink\\FlinkTutorial\\src\\main\\resources\\sensor.txt")
    val dataStream: DataStream[SensorReading] = inputStreamFromFile
      .map( data => {
        // 首先是,切分
        val dataArray = data.split(",")
        // 相当于直接new的对象
        SensorReading( dataArray(0), dataArray(1).toLong, dataArray(2).toDouble )
      } )
    val splitStream: SplitStream[SensorReading] = dataStream// 加上标签变为splitStream。
      .split( data => {
        if( data.temperature > 30 )
          Seq("high")
        else
          Seq("low")
      } )
    val highTempStream: DataStream[SensorReading] = splitStream.select("high")
    val lowTempStream: DataStream[SensorReading] = splitStream.select("low")
    val allTempStream: DataStream[SensorReading] = splitStream.select("high", "low")

    val warningStream: DataStream[(String, Double)] = highTempStream.map(
      data => (data.id, data.temperature)
    )
    val connectedStreams: ConnectedStreams[(String, Double), SensorReading] = warningStream
      .connect(highTempStream)
    val resultStream: DataStream[Object] = connectedStreams.map(// 一国两制的
      warningData => (warningData._1, warningData._2, "high temp warning"),
      lowTempData => (lowTempData.id, "normal")
    )
    resultStream.print("result")
    env.execute("transform test job")
  }
}
   // 4. 合流
    val warningStream: DataStream[(String, Double)] = highTempStream.map(
      data => (data.id, data.temperature)
//      new MyMapper
    )
    // 泛型是二元组+Double
    val connectedStreams: ConnectedStreams[(String, Double), SensorReading] = warningStream
      .connect(lowTempStream)
    val resultStream: DataStream[Object] = connectedStreams.map(
      warningData => (warningData._1, warningData._2, "high temp warning"),
      lowTempData => (lowTempData.id, "normal")
    )

map和flatMap的区别:https://www.cnblogs.com/yucy/p/10260014.html

多流的应用场景,就是我们要联合两条流的数据进行分析的,主要是会有这样的场景,就是假如我们监控森林的火警,但是,需要温度的流是不够的还需要我们有湿度的流。

---

union:

union的两条流的类型必须是一摸一样的。union可以连接多条流,但是connect的话就只能是两条流。

val unionStream: DataStream[SensorReading] = highTempStream.union(lowTempStream, allTempStream)

总结:

这个图是十分主要的。

---10---

UDF函数。

flink支持的数据类型:不细说了

自定义一个MapFunction

// 自定义MapFunction
class MyMapper extends MapFunction[SensorReading, (String, Double)]{
  override def map(value: SensorReading): (String, Double) = (value.id, value.temperature)
}

什么都有什么Function的。

富函数:上下文就是当前的线程的上下文

什么都有什么的Function。

富函数。

class MyRichMapper extends RichMapFunction[SensorReading, Int]{
  // 创建富函数调用的初始化的方法
  override def open(parameters: Configuration): Unit = {}

  // 获取运行时上下文 获取分区子任务的编号
  getRuntimeContext.getIndexOfThisSubtask

  override def map(value: SensorReading): Int = value.timestamp.toInt

  // 函数关闭的调用方法
  override def close(): Unit = {}
}

---11---

1. keyBy
基于key的hash code重分区;
同一个key只能在一个分区内处理,一个分区内可以有不同的key的数据
keyBy之后的KeyedStream上的所有操作,针对的作用域都只是当前的key

2. 滚动聚合操作
DataStream没有聚合操作,目前所有的聚合操作都是针对KeyedStream

3. 多流转换算子
split-select, connect-comap/coflatmap 成对出现
先转换成 SplitStream,ConnectedStreams,然后再通过select/comap操作转换回来DataStream
所谓coMap,其实就是基于ConnectedStreams的map方法,里面传入的参数是CoMapFunction

4. 富函数
是函数类的增强版,可以有生命周期方法,还可以获取运行时上下文,在运行时上下文可以对state进行操作
flink有状态的流式计算,做状态编程,就是基于RichFunction的

---12---

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值