Flink基础知识梳理

基础编程框架

不管是批处理还是流式处理,Flink的编程框架均遵循如下框架:

  • 批处理:
    设置批处理的execution environment
    代码:ExecutionEnvironment.getExecutionEnvironment
  • 流处理:
    设置流处理的execution environment
    代码:StreamExecutionEnvironment.getExecutionEnvironment
  • 从environment中获取数据
  • 使用对应的Flink API处理数据
  • 执行程序

wordcount批处理版本

object FlinkBatchJob {

  def main(args: Array[String]): Unit = {
    // 获取批处理上下文,等同于SparkContext
    val env = ExecutionEnvironment.getExecutionEnvironment
    // 读取数据
    val text: DataSet[String] =env.readTextFile("data/wc.data")

    // transformation操作
    // flink中没有reducebykey
    val result = text.flatMap(_.toLowerCase.split(","))
                     .filter(_.nonEmpty)   // 过滤掉有空格的情况
                     .map((_,1))
                     .groupBy(0)    // 根据单词进行分组(根据index取字段)
                     .sum(1)         // 根据value进行求和(根据index取字段)

    result.print()

  }

}

wordcount流处理版本

object FlinkStreamingJob {

  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    // 接收数据
    val text: DataStream[String] = env.socketTextStream("localhost", 9999)

    // 通过tuple方式来指定key
    // transformation操作
    // flink流处理中没有groupby只有keyby
//    val result = text.flatMap(_.toLowerCase.split(","))
//      .filter(_.nonEmpty)     // 过滤掉有空格的情况
//      .map((_,1))
//      .keyBy(0)        // 根据单词进行分组(根据index取字段)
//      .sum(1)        // 根据value进行求和(根据index取字段)


    val result = text.flatMap(_.toLowerCase.split(","))
        .filter(_.nonEmpty)
        .map(x => WC(x,1))
//        .keyBy("word")      // 通过字段表达式来指定key,这种方式代码的可读性更高
        .keyBy(_.word)        // 使用key的selector function来指定key
        .sum("count")

    result.print("huhu_bigdata").setParallelism(2)   // 设置最大的并行度为2

    // 触发程序
    env.execute(this.getClass.getSimpleName)
  }
}

case class WC(word: String, count: Int)

Flink基本API

ExecutionEnvironment

在使用DataSet/DataStream进行相关的编程时,创建ExecutionEnvironment(上下文)有3种方式:

  • getExecutionEnvironment()
  • createLocalEnvironment()
  • createRemoteEnvironment(host: String, port: Int, jarFiles: String*)
    其实我们在使用时,通过getExecutionEnvironment进行获取即可;内部实现里,会取决于程序如何进行执行,如果是在IDE中执行的就会去createLocalEnvironment,如果以jar包的方式在服务器上运行,则会createRemoteEnvironment

注意点:

  • 对于流处理来讲,最后需要调用一下env.excute()方法,否则的话是不会执行的
  • 对于批出来来讲,最后只要有个sink就行了

Lazy Evaluation

所有的flink应用程序都是延迟执行的,当应用程序的main方法被加载的时候,数据的加载、transformation并不会立刻的去被执行,但是每一步的操作都会被添加到执行计划中去,当触发execute()方法后,这些操作才会被真正的执行(这点与spark是一样的)
延迟执行,可以使我们在应用程序中去构建一个非常复杂的执行计划

Specifying Keys

一些transformation,比如:join、coGroup、keyBy、grouBy等,都是需要有1个key定义在集合上之后才能使用这些算子的,那么对应的数据源必须为(key,value)这种结构的
指定key的3种方式:

  • 通过tuple的方式来指定key
    使用tuple中key的下标0、1、2…来进行指定,比如xxx.keyBy(0)
    可读性较差,换一个人来不知道这个0代表的是啥字段
  • 使用字段表达式来指定key
    引入case class,比如xxx.keyBy(“word”)
    这种方式使用的多,相比上面那种可读性更高,但是可能会写错字段名,虽然编译通过了,但是程序执行直接挂了
    使用case class会带来最方便的一点是:case class默认就是序列化的
  • 使用key的selector function来指定key
    比如xxx.keyBy(_.word),生产最推荐使用这种方式

Specifying Transformation Functions

在Flink当中使用transformation有3种方式:

  • 使用lambda function
  • 自定义function
  • 使用rich function

使用lambda function

这种方式比较简单快速,生产上也较为推荐
在这里插入图片描述

自定义Function

我们观察可以发现:像filter算子里有FilterFunction,map算子里也有MapFunction:
在这里插入图片描述
其实在Flink中每个算子都是有对应的function的,同时我们也可以通过自定义来进行实现,需要自定义RuozedataFilter,继承FilterFunction:
在这里插入图片描述
实际使用:
在这里插入图片描述
该实现方式指定死了参数,无法动态传入,因此可以改写完动态传入参数的形式:
在这里插入图片描述在这里插入图片描述
实际上还有第三种写法,就是使用匿名内部类的方式,如下:
在这里插入图片描述

Rich Function

Rich代表增强的意思,在Flink中RichXXXFunction是一种比较牛逼的东西,后面会深入剖析
以RichFilterFunction为例继承了AbstractRichFunction这个接口,该接口有如下方法:

  • open初始化方法
  • close资源释放
  • getRuntimeContext拿到整个作业运行时的上下文,这个很有用
    对于RichXXXFunction而言都是基于AbstractRichFunction实现的,该Function为一个生命周期函数;以MapReduce的编程模型为例,当我们在MapReduce中实现Mapper方法的时候,需要实现setup、cleanup、run方法,同时会在run中调用了setup与cleanup;比如要连接数据库,总不可能每条记录去连一条的,肯定是需要在task运行起来的是初始化,结束后再进行释放资源;同理,在Flink中也可以在RichFunction中这样做
    自定义RuozedataMap继承RichMapFunction,在里面重写map方法并注入数据处理逻辑:
    在这里插入图片描述
    运行时我们可以发现open的调用次数与我们设置的并行度相关,并行度设置为多少则open调用多少次

代码

object SpecifyingTransformationsApp {

  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(2) // 并行度设置

    val stream = env.readTextFile("data/access.log")
//    val accessStream = stream.map(x => {
//      val splits = x.split(",")
//      Access(splits(0).toLong, splits(1), splits(2).toLong)
//    })

    // 需求: 过滤traffic大于4000的
    // 使用Lambda Functions
//    accessStream.filter(_.traffic > 4000).print()

    // 使用自定义Function,写死
//    accessStream.filter(new RuozedataFilter).print()

    // 使用自定义Function,传参
//    accessStream.filter(new RuozedataFilterValue(5000)).print()

    // 使用自定义Function,匿名内部类方式
//    accessStream.filter(new FilterFunction[Access] {
//      override def filter(value: Access): Boolean = value.traffic > 4000
//    }).print()

    // 使用Rich Function
    stream.map(new RuozedataMap).filter(_.traffic > 4000).print()




    env.execute(this.getClass.getSimpleName)
  }

}

/**
  * 自定义Function,写死
  */
class RuozedataFilter extends FilterFunction[Access] {
  override def filter(value: Access): Boolean = value.traffic > 4000
}

/**
  * 自定义Function,传参
  */
class RuozedataFilterValue(traffic: Long) extends FilterFunction[Access] {
  override def filter(value: Access): Boolean = value.traffic > traffic
}

/**
  * 自定义RichFunction
  * String: 读进来的数据
  * Access: 写出去的类型
  */
class RuozedataMap extends RichMapFunction[String, Access] {

  override def map(value: String): Access = {
    val splits = value.split(",")
    Access(splits(0).toLong, splits(1), splits(2).toLong)
  }

  // 调用次数与并行度设置的大小相同
  override def open(parameters: Configuration): Unit = {
    super.open(parameters)
    println("====open====")
  }

  override def getRuntimeContext: RuntimeContext = {
    super.getRuntimeContext
  }

  override def close(): Unit = {
    super.close()
  }

}

Function体系结构

map ==> MapFunction
filter ==> FilterFunction
xxx ==> XxxFunction
RichXxxFunction,比较重要,继承AbstractRichFunction,并实现XxxFunction
RichMapFunction,是对MapFunction的增强;继承AbstractRichFunction,并实现MapFunction

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值