【Flink】02、flink集群安装部署

一、standalone模式

1、上传flink到cdh01节点,并解压

tar -zxvf flink-1.10.0-bin-scala_2.11.tgz -C /opt/module

2、修改配置文件

cd /opt/module/flink-1.10.0/conf/
vim flink-conf.yaml

修改一下下面的几个参数,我这内存比较多,直接加了个0:
在这里插入图片描述

修改slaves文件:

cdh01
cdh02
cdh03

修改masters文件:

cdh01:8081

3、分发到其它两台节点

scp -r flink-1.10.0/ root@cdh02:`pwd`
scp -r flink-1.10.0/ root@cdh03:`pwd`

4、启动:

[root@cdh01 bin] ./start-cluster.sh

在cdh01 jps,有两个进程

86721 TaskManagerRunner
86368 StandaloneSessionClusterEntrypoint

5、访问webui:

http://192.168.66.11:8081/#/overview
在这里插入图片描述
6、使用webui提交程序

先打包:在这里插入图片描述
点击add New:
在这里插入图片描述
上传之后的效果:
在这里插入图片描述
在cdh01启动netcat:

nc -lk 7777

双击jar包名称并且填写主类:
在这里插入图片描述
点击提交任务之后的效果:
在这里插入图片描述

上面的图有两条任务链

输入数据流:
在这里插入图片描述
那么在哪里看 结果呢?

点击最后一个任务链:
在这里插入图片描述
可知它在cdh01,我的ip是11结尾:
在这里插入图片描述
点进去即可找到结果输出:
在这里插入图片描述

二、flink并行度和slot

  • slot是静态的概念,是指taskManager具有的并发执行能力
  • parallelism是动态的概念,是指应用 程序实际使用的并发能力

建议slot的数量和CPU的核数保持一致

一个slot就是一个线程

并行度可以在四个层面进行设置:

  • 算子层面
  • 执行环境层面
  • 客户端层面
  • 系统层面

二、yarn模式

底层 ProcessFunctionAPI

ProcessFunction 是一个低层次的流处理操作,允许返回所有 Stream 的基础构建模块:

  • 访问 Event 本身数据(比如:Event 的时间,Event 的当前 Key 等)
  • 管理状态 State(仅在 Keyed Stream 中)
  • 管理定时器 Timer(包括:注册定时器,删除定时器等)

总而言之,ProcessFunction 是 Flink 最底层的 API,也是功能最强大的。
例如:监控每一个手机,如果在 5 秒内呼叫它的通话都是失败的,发出警告信息。注意:
这个案例中会用到状态编程,请同学们只要知道状态的意思,不需要掌握。后面的章节中会
详细讲解 State 编程。

package com.learning.flink.transformation

import org.apache.flink.api.common.state.{ValueState, ValueStateDescriptor}
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.util.Collector

/**
  * 监控每一个手机号,如果在5秒内呼叫它的通话都是失败的,发出警告信息
  * 在5秒中内只要有一个呼叫不是fail则不用警告
  */
object ProcessFunctionAPI {
  def main(args: Array[String]): Unit = {
    //初始化Flink的Streaming(流计算)上下文执行环境
    val streamEnv: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    streamEnv.setParallelism(1)
    //导入隐式转换,建议写在这里,可以防止IDEA代码提示出错的问题
    import org.apache.flink.streaming.api.scala._
    //读取文件数据
    val data = streamEnv.socketTextStream("hadoop101", 8888)
      .map(line => {
        var arr = line.split(",")
        new StationLog(arr(0).trim,arr(1).trim,arr(2).trim,arr(3).trim,arr(4).trim.toLong,arr(5).trim.to Long)
      })
    55
    //处理数据
    data.keyBy(_.callOut)
      .process(new MonitorCallFail())
      .print()
    streamEnv.execute()
  }

  //监控逻辑
  class MonitorCallFail() extends KeyedProcessFunction[String, StationLog, String] {
    //使用一个状态记录时间
    lazy val timeState: ValueState[Long] = getRuntimeContext.getState(new ValueStateDescriptor[Long]("time", classOf[Long]))

    override def processElement(value: StationLog, ctx: KeyedProcessFunction[String,
      StationLog, String]#Context, out: Collector[String]): Unit = {
      //从状态中取得时间
      var time = timeState.value()
      if (value.callType.equals("fail") && time == 0) { //表示第一次发现呼叫当前手机号是失败的
        //获取当前时间,并注册定时器
        var nowTime = ctx.timerService().currentProcessingTime()
        var onTime = nowTime + 5000L //5秒后触发
        ctx.timerService().registerProcessingTimeTimer(onTime)
        timeState.update(onTime)
      }
      if (!value.callType.equals("fail") && time != 0) {
        //表示有呼叫成功了,可以取消触发器
        ctx.timerService().deleteProcessingTimeTimer(time)
        timeState.clear()
      }
    }

    //时间到了,执行触发器,发出告警
    override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[String, StationLog, String]#OnTimerContext, out: Collector[String]): Unit = {
      var warnStr = "触发时间:" + timestamp + " 手机号:" + ctx.getCurrentKey
      out.collect(warnStr)
      timeState.clear()
    }
  }

}

侧输出流 Side Output

在 flink 处理数据流时,我们经常会遇到这样的情况:在处理一个数据源时,往往需要
将该源中的不同类型的数据做分割处理,如果使用 filter 算子对数据源进行筛选分割的
话,势必会造成数据流的多次复制,造成不必要的性能浪费;flink 中的侧输出就是将数据
流进行分割,而不对流进行复制的一种分流机制。flink 的侧输出的另一个作用就是对延时
迟到的数据进行处理,这样就可以不必丢弃迟到的数据。在后面的章节中会讲到!
案例:根据基站的日志,请把呼叫成功的 Stream(主流)和不成功的 Stream(侧流)
分别输出。

package com.learning.flink.transformation

import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.util.Collector

/**
  * 把呼叫成功的Stream(主流)和不成功的Stream(侧流)分别输出。
  */
object SideOutputStreamTest {
  //导入隐式转换,建议写在这里,可以防止IDEA代码提示出错的问题
  import org.apache.flink.streaming.api.scala._
  //侧输出流首先需要定义一个流的标签
  var notSuccessTag= new OutputTag[StationLog]("not_success")
  def main(args: Array[String]): Unit = {
    //初始化Flink的Streaming(流计算)上下文执行环境
    val streamEnv: StreamExecutionEnvironment =
      StreamExecutionEnvironment.getExecutionEnvironment
    streamEnv.setParallelism(1)
    //读取文件数据
    val data = streamEnv.readTextFile(getClass.getResource("/station.log").getPath)
      .map(line=>{
        var arr =line.split(",")
        new StationLog(arr(0).trim,arr(1).trim,arr(2).trim,arr(3).trim,arr(4).trim.toLong,arr(5).trim.to Long)
      })
    val mainStream: DataStream[StationLog] = data.process(new CreateSideOutputStream(notSuccessTag))
    //得到侧流
    val sideOutput: DataStream[StationLog] = mainStream.getSideOutput(notSuccessTag)
    mainStream.print("main")
    sideOutput.print("sideoutput")
    streamEnv.execute()
  }
  class CreateSideOutputStream(tag: OutputTag[StationLog]) extends ProcessFunction[StationLog,StationLog]{
    override def processElement(value: StationLog, ctx: ProcessFunction[StationLog, StationLog]#Context, out: Collector[StationLog]): Unit = {
      if(value.callType.equals("success")){//输出主流
        out.collect(value)
      }else{//输出侧流
        ctx.output(tag,value)
      }
    }
  }
}

Flink State 管理与恢复
Flink 是一个默认就有状态的分析引擎,前面的 WordCount 案例可以做到单词的数量的
累加,其实是因为在内存中保证了每个单词的出现的次数,这些数据其实就是状态数据。但
是如果一个 Task 在处理过程中挂掉了,那么它在内存中的状态都会丢失,所有的数据都需
要重新计算。从容错和消息处理的语义(At -least-once 和 Exactly-once)上来说,Flink
引入了 State 和 CheckPoint。
 State 一般指一个具体的 Task/Operator 的状态,State 数据默认保存在 Java 的堆内存
58
中。
 CheckPoint(可以理解为 CheckPoint 是把 State 数据持久化存储了)则表示了一个
Flink Job 在一个特定时刻的一份全局状态快照,即包含了所有 Task/Operator 的状态。

  1. 常用 State
    Flink 有两种常见的 State 类型,分别是:
     keyed State(键控状态)
     Operator State(算子状态)
  1. Keyed State (键控状态)
    Keyed State:顾名思义就是基于 KeyedStream 上的状态,这个状态是跟特定的 Key 绑
    定的。KeyedStream 流上的每一个 Key,都对应一个 State。Flink 针对 Keyed State 提供了
    以下可以保存 State 的数据结构:
     ValueState: 保存一个可以更新和检索的值(如上所述,每个值都对应到当前的输
    入数据的 key,因此算子接收到的每个 key 都可能对应一个值)。 这个值可以通过
    update(T) 进行更新,通过 T value() 进行检索。
     ListState: 保存一个元素的列表。可以往这个列表中追加数据,并在当前的列表上
    进行检索。可以通过 add(T) 或者 addAll(List) 进行添加元素,通过 Iterable
    get() 获得整个列表。还可以通过 update(List) 覆盖当前的列表。
     ReducingState: 保存一个单值,表示添加到状态的所有值的聚合。接口与
    ListState 类似,但使用 add(T) 增加元素,会使用提供的 ReduceFunction 进行聚合。
     AggregatingState<IN, OUT>: 保留一个单值,表示添加到状态的所有值的聚合。和
    ReducingState 相反的是, 聚合类型可能与 添加到状态的元素的类型不同。 接口与
    ListState 类似,但使用 add(IN) 添加的元素会用指定的 AggregateFunction 进行聚
    合。
     FoldingState<T, ACC>: 保留一个单值,表示添加到状态的所有值的聚合。 与
    ReducingState 相反,聚合类型可能与添加到状态的元素类型不同。接口与 ListState
    类似,但使用 add(T)添加的元素会用指定的 FoldFunction 折叠成聚合值。
     , MapState<UK, UV>: 维护了一个映射列表。 你可以添加键值对到状态中,也可以获得
    反映当前所有映射的迭代器。使用 put(UK,UV) 或者 putAll(Map<UK,UV>) 添加映射。
    使用 get(UK) 检索特定 key。 使用 entries(),keys() 和 values() 分别检索映射、
    键和值的可迭代视图。
  2. Operator State (算子状态)
    Operator State 与 Key 无关,而是与 Operator 绑定,整个 Operator 只对应一个 State。
    比如:Flink 中的 Kafka Connector 就使用了 Operator State,它会在每个 Connector 实例
    中,保存该实例消费 Topic 的所有(partition, offset)映射。
    在这里插入图片描述
/**
  * 统计每个手机的呼叫间隔时间,并输出
  */
object StateCallInterval {
  def main(args: Array[String]): Unit = {
    //初始化Flink的Streaming(流计算)上下文执行环境
    val streamEnv: StreamExecutionEnvironment =
      StreamExecutionEnvironment.getExecutionEnvironment
    streamEnv.setParallelism(1)
    //导入隐式转换,建议写在这里,可以防止IDEA代码提示出错的问题
    import org.apache.flink.streaming.api.scala._
    //读取文件数据
    val data = streamEnv.readTextFile(getClass.getResource("/station.log").getPath)
      .map(line=>{
        var arr =line.split(",")
        new
            StationLog(arr(0).trim,arr(1).trim,arr(2).trim,arr(3).trim,arr(4).trim.toLong,arr(5).trim.to
              Long)
      })
    60
    data.keyBy(_.callIn) //按照呼叫手机号分组
      .flatMap(new CallIntervalFunction())
      .print()
    streamEnv.execute()
  }
  class CallIntervalFunction() extends
    RichFlatMapFunction[StationLog,(String,Long)]{
    //定义一个保存前一条呼叫的数据的状态对象
    private var preData:ValueState[StationLog] =_
    override def open(parameters: Configuration): Unit = {
      val stateDescriptor =new
          ValueStateDescriptor[StationLog]("pre",classOf[StationLog])
      preData =getRuntimeContext.getState(stateDescriptor)
    }
    override def flatMap(in: StationLog, collector: Collector[(String, Long)]): Unit = {
      var pre: StationLog = preData.value()
      if(pre==null){ //如果状态中没有,则存入
        preData.update(in)
      }else{ //如果状态中有值则计算时间间隔
        var interval =in.callTime - pre.callTime
        collector.collect((in.callIn,interval))
      }
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值