五 Flink DataStream API

1.map

实现Map操作,有三种方法.map操作是UDF(一进一出)
1.第一种是和spark的map类似,直接在map算子中传入匿名函数
2.其实map算子接收的是一个实现MapFunction接口的类,我们可以传入一个匿名类
3.可以创建一个实现MapFunction接口的类.

package org.example.puapi

import org.apache.flink.api.common.functions.MapFunction
import org.apache.flink.streaming.api.scala._
import org.example.source.self.{SensorReading, SensorSource}

/**
 * 利用map算子把流中的SensorReading,转换成元组.
 */
object MapExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream = env.addSource(new SensorSource)

      //方法1,往map传入匿名函数,函数是传入一个参数,出来一个参数
//      .map(obj => (obj.id,obj.temperature,obj.timestamp))


      //方法二 传入一个实现MapFunction接口的匿名类
      /**
       * [SensorReading,(String,Double,Long)] : 传入类型是SensorReading
       * 返回类型是(String,Double,Long)
       * 根据逻辑来写map函数
       */
//        .map(new MapFunction[SensorReading,(String,Double,Long)] {
//          override def map(value: SensorReading) = {
//            (value.id,value.temperature,value.timestamp)
//          }
//        })
//

      //方法3
        .map(new MyMapFunction)


    stream.print()

    env.execute()
  }

  //方法3,自定义类实现MapFunction接口
  class MyMapFunction extends MapFunction[SensorReading,(String,Double,Long)] {
    override def map(value: SensorReading): (String, Double, Long) = {
      (value.id,value.temperature,value.timestamp)

    }
  }

}

2.filter

filter算子是对数据进行过滤用的.它和map算子一样,实现的方法有3中,和map不同的是flink需要一个返回值是boolean,用来判断该条数据是否保留.而传入map算子的类必须要实现
fliter是一进0出或者1出

package org.example.puapi

import org.apache.flink.api.common.functions.FilterFunction
import org.apache.flink.streaming.api.scala._
import org.example.source.self.{SensorReading, SensorSource}

/**
 * 利用filter算子把id为1的数据过滤出来
 */

object FilterExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream = env.addSource(new SensorSource)
        //方法1
//      .filter(_.id.equals("sensor_1"))

      //方法2
//        .filter(new FilterFunction[SensorReading] {
//          override def filter(value: SensorReading): Boolean = value.id.equals("sensor_1")
//        })

    //方法3
        .filter(new MyFilterFunction)
        .print()

    env.execute()
  }

  class MyFilterFunction extends FilterFunction[SensorReading] {
    override def filter(value: SensorReading): Boolean =  value.id.equals("sensor_1")
  }

}

3 flatmap函数

flatmap函数是一进多出
传入的类需要实现的接口是FlatMapFunction,方法和map算子是一样的.

package test2

import org.apache.flink.api.common.functions.FlatMapFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

object FlatMapExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env
      .addSource(new SensorSource)

    // 使用`FlatMapFunction`实现`MapExample.scala`中的功能
    stream
      .flatMap(
        new FlatMapFunction[SensorReading, String] {
          override def flatMap(value: SensorReading, out: Collector[String]): Unit = {
            // 使用`collect`方法向下游发送抽取的传感器ID
            out.collect(value.id)
          }
        }
      )
      .print()

    // 使用`FlatMapFunction`实现`FilterExample.scala`中的功能
    stream
        .flatMap(
          new FlatMapFunction[SensorReading, SensorReading] {
            override def flatMap(value: SensorReading, out: Collector[SensorReading]): Unit = {
              if (value.id.equals("sensor_1")) {
                out.collect(value)
              }
            }
          }
        )
        .print()

    env.execute()
  }
}

4 keyby

keyby是对流进行分组.相同的key进入到一个组中,但每个组中的key值不一定相同.同一个组内的不同key值逻辑上隔离的.

keyby之后的流就会变成keyedstream.
keyby的用法:
可以通过传入位置参数

keyby(0)

如果是类
可以传入属性值

keyby(''id'')

还可以传入匿名函数:

package org.example.puapi

import org.apache.flink.api.java.functions.KeySelector
import org.apache.flink.streaming.api.scala._
import org.example.source.self.{SensorReading, SensorSource}

object KeyByExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream = env.addSource(new SensorSource)
      //1. 根据位置
      //.keyBy(0) 
      // 2. 根据属性
      //.keyBy("id")
      
      //3. 或者传入匿名函数
//      .keyBy(_.id)
      
      //4. 自己定义KeySelector
      .keyBy(new KeySelector[SensorReading,String] {
        override def getKey(value: SensorReading): String = value.id
      })
      
      .sum(2)
      .print()

    env.execute()
  }

}

4.1 滚动聚合

滚动聚合算子由 KeyedStream 调用,并生成一个聚合以后的 DataStream,例如: sum, minimum, maximum。一个滚动聚合算子会为每一个观察到的 key 保存一个聚合的值。针对每
一个输入事件,算子将会更新保存的聚合结果,并发送一个带有更新后的值的事件到下游算
子。滚动聚合不需要用户自定义函数,但需要接受一个参数,这个参数指定了在哪一个字段
上面做聚合操作。 DataStream API 提供了以下滚动聚合方法。

滚动聚合算子只能用在滚动窗口,不能用在滑动窗口。

• sum():在输入流上对指定的字段做滚动相加操作。
• min():在输入流上对指定的字段求最小值。
• max():在输入流上对指定的字段求最大值。
• minBy():在输入流上针对指定字段求最小值,并返回包含当前观察到的最小值的事件。
• maxBy():在输入流上针对指定字段求最大值,并返回包含当前观察到的最大值的事件。

滚动聚合算子无法组合起来使用,每次计算只能使用一个单独的滚动聚合算子。

5 Rich Functions

DataStream API 提供的所有转换操作函数,都拥有它们的 “富” 版本.只需要在常规函数前面加上Rich前缀就是富函数了.

RichMapFunction
RichFlatMapFunction
RichFilterFunction
....

当我们使用富函数时,我们可以实现两个额外的方法:
• open() 方法是 rich function 的初始化方法,当一个算子例如 map 或者 filter 被调用之前,open() 会被调用。 open() 函数通常用来做一些只需要做一次即可的初始化工作。
• close() 方法是生命周期中的最后一个调用的方法,通常用来做一些清理工作

package org.example.puapi

import org.apache.flink.api.common.functions.RichFlatMapFunction
import org.apache.flink.api.scala._
import org.apache.flink.configuration.Configuration
import org.apache.flink.util.Collector

object RichExample {
  def main(args: Array[String]): Unit = {
    val env = ExecutionEnvironment.getExecutionEnvironment

    val stream = env.fromElements(1, 2, 3)
    stream.flatMap(new MyFlatMap).print()

    env.execute()
  }

  class MyFlatMap extends RichFlatMapFunction[Int,Int] {
    override def open(parameters: Configuration): Unit = {
      println("开始生命周期")
    }
    override def flatMap(value: Int, out: Collector[Int]): Unit = {
      println("并行任务索引是:" + getRuntimeContext.getIndexOfThisSubtask)
      out.collect(value)
    }

    override def close(): Unit = {
      println("结束生命周期")
    }
  }

}

6 union

DataStream.union() 方法将两条或者多条 DataStream 合并成一条具有与输入流相同类型的输出 DataStream。接下来的转换算子将会处理输入流中的所有元素。事件合流的方式为 FIFO 方式。操作符并不会产生一个特定顺序的事件流。 union 操作符也不会进行去重。每一个输入事件都被发送到了下一个操作符。
在这里插入图片描述

而且合并流的类型要一样.

package org.example.puapi

import org.apache.flink.streaming.api.scala._
import org.example.source.self.SensorSource

object UnionExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream = env.addSource(new SensorSource)
        .filter(r => r.id.equals("sensor_1"))


    val stream2 = env.addSource(new SensorSource)
      .filter(r => r.id.equals("sensor_2"))


    val stream3 = env.addSource(new SensorSource)
      .filter(r => r.id.equals("sensor_3"))


    //把三条流合并成一条
    val unionStream = stream.union(
      stream2,
      stream3
    )


    
    unionStream.print()

    env.execute()
  }
}

7 connect,comap 和 coflatmap

联合两条流的事件是非常常见的流处理需求。例如监控一片森林然后发出高危的火警警报。报警的 Application 接收两条流,一条是温度传感器传回来的数据,一条是烟雾传感器传回来的数据。当两条流都超过各自的阈值时,报警。
DataStream API 提供了 connect 操作来支持以上的应用场景。 DataStream.connect() 方法接收一条 DataStream,然后返回一个 ConnectedStreams 类型的对象,这个对象表示了两条连接的流。
ConnectedStreams 提供了 map() 和 flatMap() 方法,分别需要接收类型为 CoMapFunction和 CoFlatMapFunction 的参数。以上两个函数里面的泛型是第一条流的事件类型和第二条流的事件类型,以及输出流的事件类型。还定义了两个方法,每一个方法针对一条流来调用。 map1() 和 flatMap1() 会调用在第一条流的元素上面, map2() 和 flatMap2() 会调用在第二条流的元素上面。

package org.example.puapi

import org.apache.flink.streaming.api.functions.co.CoMapFunction
import org.apache.flink.streaming.api.scala._

object CoMapExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val stream1 = env.fromElements((1, 100))
    val stream2 = env.fromElements((2, "tow"))

    val stream3 = stream1.connect(stream2)

    val stream4 = stream3.map(new MyCoMap)

    stream4.print()

    env.execute()
  }

  //定义CoMapFunction类
  class MyCoMap extends CoMapFunction[(Int,Int),(Int,String),String] {
    override def map1(value: (Int, Int)): String = {
      value._2.toString + "来自第一条流"
    }

    override def map2(value: (Int, String)): String = {
      value._2 + "来自第二条流"
    }
  }

}

package org.example.puapi

import org.apache.flink.streaming.api.functions.co.CoFlatMapFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector

object CoFlatMapExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    println(env.getParallelism)

    val one = env.fromElements((1, 1L))
      .setParallelism(1)

    val two = env.fromElements((1, "tow"))
      .setParallelism(1)

    val conn = one.keyBy(_._1)
      .connect(two.keyBy(_._1))

    val printed = conn.flatMap(new MyCoFlatMap)

    printed.print()

    env.execute()
  }

  class MyCoFlatMap extends CoFlatMapFunction[(Int,Long),(Int,String),String]{
    override def flatMap1(value: (Int, Long), out: Collector[String]): Unit = {
      out.collect(value._2.toString + "来自第一条流")
    }

    override def flatMap2(value: (Int, String), out: Collector[String]): Unit = {

      out.collect(value._2.toString + "来自第二条流")
    }
  }


}

8 split和select

Split 是 Union 的反函数。 Split 将输入的流分成两条或者多条流。每一个输入的元素都可以被路由到 0、 1 或者多条流中去。所以, split 可以用来过滤或者复制元素。下图展示了 split操作符将所有的白色事件都路由到同一条流中去了,剩下的元素去往另一条流.
在这里插入图片描述

DataStream.split() 方法接受 OutputSelector 类型,此类型定义了输入流中的元素被分配到哪个名字的流中去。 OutputSelector 定义了 select() 方法,此方法将被每一个元素调用,并返回 java.lang.Iterable[String] 类型的数据。返回的 String 类型的值将指定元素将被路由到哪一条流。
不推荐使用 split 方法,推荐使用 Flink 的侧输出(side-output)特性。

package org.example.puapi

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

object SplitExample {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)

    val stream = env.fromElements(
      (1001, "1001"),
      (999, "999")
    )


    //把流分成两条流,实际上还是一条流,只是把大于1000打上"large"标签,
    //把小于等于1000的打上"small"标签
    val splitStream = stream.split(t => if (t._1 > 1000) Seq("large") else Seq("small"))

    //把相应标签的数据领出来生成一条全新的流
    val largeStream = splitStream.select("large")
    val smallStream = splitStream.select("small")

    val allStream = splitStream.select("large","small")


    largeStream.print()
    env.execute()
  }
}

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页