Flink开发技术点

Flink

1.算子分类

1.1 one-to-one

1.2redistributing

2.备注记

1.keyby的特殊

keyby是基于hashcode进行重分区的,而broadcast和rebanlance是随机重新分区
sum等算子是基于keyedsteam,【普通的数据类型是Dsteam

3.Flink流处理的API

flink的处理过程包含source transfer sink

3.1创建执行环境

    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    StreamExecutionEnvironment.createLocalEnvironment(1)
    StreamExecutionEnvironment.createRemoteEnvironment("172.168.72.64",4044,"")
   环境执行的俩种方式:
   第一种是flink自动获取,自动包装
   第二种是 手动切换的类型

3.2source几种的例子

readTextFile 读文件的source接入
socketTextStream:从文本流读入的方式
env.fromCollection(List(
  SendsoRendsourceTest("ceshi1",1547718199,123),
 SendsoRendsourceTest("ceshi2",1287319287,100),
  )):从内存读集合的方式

从kafka中读取数据的方式

  val properties = new Properties()
    properties.setProperty("bootstrap.serers","localhost:9092")
    properties.setProperty("group.id","consumer-group")
    properties.setProperty("auto.offset.reset","latest")
    properties.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer")
    properties.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer")
    val data: DataStream[String] = env.addSource(new FlinkKafkaConsumer011[String]("ceshi",new SimpleStringSchema(),properties))
    data.print("data")
    env.execute("source test job")

java版本

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.api.java.tuple.Tuple1;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.*;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011;
import org.apache.flink.util.Collector;
import scala.Int;

import javax.xml.crypto.*;
import java.util.Properties;

public class FlinkSteamingApi {
    public static void main(String[] args) {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//    StreamExecutionEnvironment.createLocalEnvironment(1)
//    StreamExecutionEnvironment.createRemoteEnvironment("172.168.72.64",4044,"")
        env.setParallelism(4);
        Properties properties = new Properties();
        properties.setProperty("","");
        DataStreamSource<String> inputData = env.addSource(new FlinkKafkaConsumer011<String>("test", new SimpleStringSchema(), properties));
//        SingleOutputStreamOperator mapDataStream =
        SingleOutputStreamOperator<Int> sum = inputData.flatMap(new FlatMapFunction<String, Int>() {
            public void flatMap(String o, Collector collector) throws Exception {
                String[] split = o.split("");
                for (String data : split) {
                    collector.collect(new Tuple2(data, 1));
                }
            }
        }).keyBy(0).sum(1);
        sum.print();
        try {
            env.execute("cveshi");
        } catch (Exception e) {
            e.getMessage();
        }

    }
}

3.3不同算子执行在不同的slot上的设置

3.3.1slotSharingGroup

slotSharingGroup("1")将slot划分组管理,同一组内可以共享slot
默认情况:slotSharingGroup ("defualt")
在指定上slot组之后,后面的算子默认是和之前指定的slot组
应用场景:类似于窗口计算比较耗时的算子任务,可以单独使用slot组区分资源应用

3.3.2disablechaining和startNewChain

disablechaining :算子作用,算子前后任务都不能合并成一个任务执行
startNewChain:算子作用算子断开前面算子

3.4自定义source源

import java.util.{Properties, Random}

import org.apache.flink.api.common.serialization.SimpleStringSchema
import org.apache.flink.streaming.api.scala._
import org.apache.flink.api.java.utils.ParameterTool
import org.apache.flink.streaming.api.functions.source.SourceFunction
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011
import org.apache.kafka.common.serialization.StringDeserializer

object FlinkSteaming {
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
//    StreamExecutionEnvironment.createLocalEnvironment(1)
//    StreamExecutionEnvironment.createRemoteEnvironment("172.168.72.64",4044,"")
      env.setParallelism(4)
//    val data: DataStream[SendsoRendsourceTest] = env.fromCollection(List(
//      SendsoRendsourceTest("ceshi1",1547718199,123),
//      SendsoRendsourceTest("ceshi2",1287319287,100),
//      SendsoRendsourceTest("ceshi2",1287319287,99),
//      SendsoRendsourceTest("ceshi1",1547718200,123),
//      SendsoRendsourceTest("ceshi1",1547718201,123),
//      SendsoRendsourceTest("ceshi1",1547718202,123)
//    ))
//    data.print("data")
//    val data: DataStream[String] = env.readTextFile("E:\\java_home_work\\IdeaProjects\\zbs_learn\\zbs_spring\\src\\main\\resources\\Tess_data")
//    env.socketTextStream("",21)
// v从kafka中读取数据
    val properties = new Properties()
    properties.setProperty("bootstrap.serers","localhost:9092")
    properties.setProperty("group.id","consumer-group")
    properties.setProperty("auto.offset.reset","latest")
    properties.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer")
    properties.setProperty("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer")
    val data: DataStream[String] = env.addSource(new FlinkKafkaConsumer011[String]("ceshi",new SimpleStringSchema(),properties))
    data.print("data")
    data.flatMap(_.split(" ")).map((_,1)).slotSharingGroup("1").disableChaining()
      .filter(_._1.nonEmpty).keyBy(0).sum(1).startNewChain()
    data.slotSharingGroup("1")
    env.execute("source test job")

  }

}
//数据的输入形式是集合
case class SendsoRendsourceTest(id:String,timestap:Double,rmperatur:Double)
//从文件中读取数据

object sourceDemo{
  def main(args: Array[String]): Unit = {
    val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
    val stream5: DataStream[SendsoRendsourceTest] = env.addSource(new MysensorSource())
    stream5.print()
    env.execute("ceshisource")
  }
}
//实现一个自定义的sourceFunction,自动生成测试数据
class MysensorSource extends SourceFunction[SendsoRendsourceTest] {
//  定义一个FLAG,表示数据源是否正常运行
  var running:Boolean=true
  override def run(ctx: SourceFunction.SourceContext[SendsoRendsourceTest]): Unit = {
    val rand = new Random()
    var curTemps = 1.to(10).map(
      i=>("sensor"+i,60 +rand.nextGaussian()*20)
    )
    //无限循环,生成循环数据
    while (running){
      //随机生成微小波动
      curTemps.map(
        data=> (data._1,data._2+rand.nextGaussian())
      )
      val cutrs=System.currentTimeMillis()
      //包装成样例类,用ctx发送数据
      curTemps.foreach(
        data=>ctx.collect(SendsoRendsourceTest(data._1,cutrs,data._2))
      )
     Thread.sleep(1000)
    }
  }

  override def cancel(): Unit = {
    running=false
  }
}

3.5transform

scource之后,sink之前的算子都是转换算子

基本转换算子

map、flatmap、filter

特殊算子,只在数据传输之前起作用,起到重分区和类型装换

keyby 基于key的hashcode做重分区 datastream -> keystream
下面可以做滚动聚合算子

dataStream 是没有聚合操作,目前所有的聚合操作都是针对keySteam

滚动聚合算子

sum 
min和minby区别 min只输出指定字段的最小值,其他字段使用第一个的值,minby是根据最小值的,其他字段也是最小值的其他字段   
max和maxby区别 同上
reduce  类型为keyedstream keyby流 根据历史结果+新传入的数据进行计算 所以两个参数是相同的
fold 已删除
aggregate  
reduce和aggregate 
.reduce(
        (a: SendsoRendsourceTest, b: SendsoRendsourceTest) => {
          a.rmperatur = a.rmperatur + b.rmperatur
          a
        }

拆分

split-select 分流  
connect 和comap/coflatmap
成对出现,都是针对 splitSteam 或者 connectSteam ,然后通过select 或者comap coflatmap 进行获取
union 合流 
 data.split(x=>{   //TraversableOnce sacla里的复杂类型  seq list等
      if(x.isEmpty){
        List("high")
      }                   
      else
        List("low")
    }).select("high")

有状态的算子

mapwithstate
函数类:flink提供了所有的udf的函数类 都可以通过继承实现自己的函数类

富函数类:flink提供了所有的udf的函数类 都可以通过继承实现自己的函数类比一般的udf类多加生命周期的函数
        open() 初始化的时候,只调用一次
        close() 结束的时候,只调用一次
        getruntimecontext():获取运行时上下文,例如状态 
        状态操作多用于 状态编程 
        getruntimecontext().getIndexOfThisSubtask:获取当前任务的子任务编号

3.6sink源

sink: .writeAsCsv()  输出为csv文件的sink方法
       ..writeToSocket() 一般测试数据
官方提供的sink源 sourece源
  kafka                 sink/sources
  cassandra   
  kinesis Streams       sink/sources
  es
  hdfs
  rabbitmq              sink/sources
  nifi                  sink/sources
  twitter streaming api source 
Bahir 
	activemq            sink/sources
	flume
	redis
	akka
	netty

kafka sink 案例分析:
tuple2SingleOutputStreamOperator.addSink(
                new FlinkKafkaProducer011>("localhost:9092","sinktest",new mySerializationSchema())
        );
class   mySerializationSchema implements SerializationSchema> {
    @Override
    public byte[] serialize(Tuple2 stringIntegerTuple2) {
        return stringIntegerTuple2.toString().getBytes();
    }
}

redis sink 案例分析:

        org.apache.bahir
        flink-connector-redis_2.11
        1.0
  apache bahir的依赖


 tuple2SingleOutputStreamOperator.addSink(new RedisSink>(new FlinkJedisPoolConfig.Builder().setHost("").setPort(6379).build(),
                new RedisMapper>() {
                    @Override 
                    //定义保存数据到redis的命令
                    public RedisCommandDescription getCommandDescription() {
                        new RedisCommandDescription(RedisCommand.HSET,"sadasdas");
                    }

                    @Override
                    public String getKeyFromData(Tuple2 data) {
                        return data.f0;
                    }

                    @Override
                    public String getValueFromData(Tuple2 data) {
                        return data.f1.toString();
                    }
                });
        env.execute();
自定义sink源: 使用richfunction 使用open方式创建连接然后实现invoke方式发送数据 实现数据插入
    tuple2SingleOutputStreamOperator.addSink(new RichSinkFunction>() {
            //声明连接和预编译语句
            Connection connection = null;
            PreparedStatement insertstmt = null;
            PreparedStatement updateStmnt = null;

            @Override
            public void open(Configuration parameters) throws Exception {
                connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");
                System.out.println(connection);
                insertstmt = connection.prepareStatement("insert into ceshi(name,id) values (?,?)");
                updateStmnt = connection.prepareStatement("update  ceshi set id=? where name=?");

            }

            //每来一条数据,就调用连接执行sql


            @Override
            public void invoke(Tuple2 value, Context context) throws Exception {
                //直接执行更新语句,如果没有更新就拆入
                updateStmnt.setInt(1, value.f1);
                updateStmnt.setString(2, value.f0);
                updateStmnt.execute();
                if (updateStmnt.getUpdateCount() == 0) {
                    insertstmt.setString(1,value.f0);
                    insertstmt.setInt(2,value.f1);
                    insertstmt.execute();
                }
            }

            @Override
            public void close() throws Exception {
                insertstmt.close();
                updateStmnt.close();
                connection.close();
            }
        });

        env.execute();

4.窗口

windows:窗口就是将无限流切割为有限流的一种方式,塔会将流数据发送到有限大小的桶中进行分析
 

4.1窗口的类型

时间窗口:
  滚动时间窗口-tumbling Winows: 数据根据固定的窗口长度对数据进行划分 时间对齐,窗口长度固定,没有重叠
  滑动时间窗口-Sliding Windows:滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口长度和滑动间隔组成,窗口长度固定,可以有重叠
  会话窗口-特殊的一种窗口Session Windows:由一系列事件组合一个指定时间长度的timeout间隙组成,也就是一段时间就么有接受到新数据就会生成一个新的窗口
计数窗口:
  滚动计数窗口-tumbling Winows: 数据根据固定的窗口长度对数据进行划分
  滑动计数窗口-Sliding Windows:滑动窗口是固定窗口的更广义的一种形式,滑动窗口由固定的窗口长度和滑动间隔组成,窗口长度固定,可以有重叠

4.2Window ApI

窗口分配器.window()方法
我们可以用window()来定义一个窗口,然后基于这个windows去做一些聚合和其他的处理操作,注意这个window() 方法必须在keyby以后使用
Flink还提供了更加简单的.timeWindow和.countWindow方法,用于定义时间和计数窗口
窗口测试:
在不使用keyby算子的开窗函数,调用windowall方法,这个方法会默认吧数据都会放到一个key里,相当于提前加了group ,建议不是用
splitDataStream.windowAll();
基于keyby之后的开窗更常见
正常的写法:
 DataStream<Tuple2<String, Integer>> tuple2SingleOutputStreamOperator = splitDataStream.keyBy(0).timeWindow(Time.seconds(2)).sum(1);
        tuple2SingleOutputStreamOperator.print();
窗口的分配器:globalWindows共同的窗口计数器
           tumblingWinows滚动窗口计数器
           SlidingWindows滑动窗口计数器
           sessionWindows会话窗口计数器
实例: 滚动时间窗口 .timeWindow(Time.seconds(15))
      滑动时间窗口 .timeWindow(Time.seconds(15),Time.seconds(5))
      会话窗口     .window(EventTimeSessionWindows.withGap(Time.minutes(10)))
      滚动计数窗口 .countWindow(5)
      滑动计数窗口 .countWindows(10,2)

4.3窗口聚合操作

增量聚合函数

每条数据来之后,保持一个简单的状态 
ReduceFunction AggregateFunction 来一个计算一个,保持状态,不输出 .sum .min .max 

全窗口函数 ds-aws-ds

全把窗口的所有数据收集起来,等到计算的时候遍历所有数据
ProcessWindowFunction WindowFunction 类似于批处理
        DataStream<Tuple2<String, Integer>> tuple2SingleOutputStreamOperator = splitDataStream.keyBy(0).timeWindow(Time.seconds(2)
        ).aggregate(new AggregateFunction<Tuple2<String, Integer>, Integer, Tuple2<String, Integer>>() {
            @Override
            public Integer createAccumulator() {
                return 0;
            }

            @Override
            public Integer add(Tuple2<String, Integer> stringIntegerTuple2, Integer integer) {
                return integer+1;
            }

            @Override
            public Tuple2<String, Integer> getResult(Integer integer) {
                return null;
            }

            @Override
            public Integer merge(Integer integer, Integer acc1) {
                return null;
            }
        })
                ;
        tuple2SingleOutputStreamOperator.print();
        env.execute();

    }
.apply(new WindowFunction<Tuple2<String, Integer>, Tuple2<String, Integer>, Tuple, TimeWindow>() {
            @Override
            public void apply(Tuple tuple, TimeWindow window, Iterable<Tuple2<String, Integer>> input, Collector<Tuple2<String, Integer>> out) throws Exception {
                //获取最终时间
                long end = window.getEnd();
                ArrayList<Tuple2<String, Integer>> list = IteratorUtils.toList(input.iterator();
                out.collect(list.get(0));
            }
        })
.countWindow(10,2).aggregate(new AggregateFunction<Tuple2<String, Integer>, Tuple2<Integer,Integer>, Tuple2<String, Integer>>() {
                    @Override
                    public Tuple2<Integer, Integer> createAccumulator() {
                        return new  Tuple2(1,1);
                    }

                    @Override
                    public Tuple2<Integer, Integer> add(Tuple2<String, Integer> stringIntegerTuple2, Tuple2<Integer, Integer> integerIntegerTuple2) {

                        return new Tuple2< Integer, Integer>(stringIntegerTuple2.f1+integerIntegerTuple2.f0,stringIntegerTuple2.f1+1);
                    }

                    @Override
                    public Tuple2<String, Integer> getResult(Tuple2<Integer, Integer> integerIntegerTuple2) {
                        return null;
                    }

                    @Override
                    public Tuple2<Integer, Integer> merge(Tuple2<Integer, Integer> integerIntegerTuple2, Tuple2<Integer, Integer> acc1) {
                        return null;
                    }
                })
                ;
注意:刚开窗的滑动步长就是输出的频率

4.4窗口的其他方法

process context 是上下文
.trigger() 触发器
   定义window关闭,触发计算,输出结果
·evictor() 移除器 定义移除某些数据的逻辑
.allowedLateness() 允许处理迟到的数据
.sideOutputLateData() 讲迟到的数据放入侧输出流
.getSideOutput获取侧输出流

窗口的迟到的逻辑,现在原窗口的时间输出一次,切窗口不会立即关闭,会等待一个时间的,等待数据的传入,再次计算,再次输出,等到迟到时间到后,才会关闭窗口
迟到数据-》窗口等待-》测输出流写入处理-》侧输出流获取数据
.countWindow(10,2)
//                .trigger()
//                .evictor()
                .allowedLateness(Time.seconds(2))
                .sideOutputLateData(new OutputTag<>("123"))
                .aggregate(new AggregateFunction<Tuple2<String, Integer>, Tuple2<Integer,Integer>, Tuple2<String, Integer>>() {
                    @Override
                    public Tuple2<Integer, Integer> createAccumulator() {
                        return new  Tuple2(1,1);
                    }

                    @Override
                    public Tuple2<Integer, Integer> add(Tuple2<String, Integer> stringIntegerTuple2, Tuple2<Integer, Integer> integerIntegerTuple2) {

                        return new Tuple2< Integer, Integer>(stringIntegerTuple2.f1+integerIntegerTuple2.f0,stringIntegerTuple2.f1+1);
                    }

                    @Override
                    public Tuple2<String, Integer> getResult(Tuple2<Integer, Integer> integerIntegerTuple2) {
                        return null;
                    }

                    @Override
                    public Tuple2<Integer, Integer> merge(Tuple2<Integer, Integer> integerIntegerTuple2, Tuple2<Integer, Integer> acc1) {
                        return null;
                    }
                })
注意点:侧输出流的获取只能用 SingleOutPutStreamOperator这个数据类型,DataStream没有getSidwOutput的方法
例如:SingleOutputStreamOperator alertStream = (SingleOutputStreamOperator) alerts;

5.时间语义

主要包含三个时间语义:
       Event Time  事件创建的时间
       ingestion time 数据进入Flink的时间
       processing time 执行操作算子的本地系统时间,与机器相关
不同的时间语义有不同的应用的场景
Event time是我们更关心的时间 
设置代码的处理时间是处理时间 
 env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
然后设置流的事件时间时.assignTimestampsAndWatermarks 

5.1乱序时间的影响

Event Time也会带来数据的影响,因为数据的到达是可能是无序,影响数据窗口的操作
例子,当新的窗口数据到时,上一个窗口将要关闭,但是上一窗口执行什么操作,是关闭还是等待,怎么关闭,原来的迟到数据处理会带来一个问题
      ,窗口先输出结果,然后再迟到一条计算一条,会造成大量的输出窗口计算,

5.2Watermark

遇到一个时间戳达到了窗口的关闭时间,不应该立刻触发窗口的计算,而是等待一段时间,等迟到的数据来了在关闭窗口
watermark是衡量数据EventTime的机制,可以设定延迟触发
通常结合window的实现,数据流的Watermark用于timestamp小于Watewrmark的数据,都已经到达了,因此,window的执行也是Watermark触发的
watermark,让窗口延迟触发,allowedLateness 运行时等待迟到数据,singoutput侧输出流处理真正数据的延迟

5.3Watermark的特点

watermark是一条特殊的数据记录
watermark必须单点递增,以确保任务的事件时间时钟在向前推进,而不是在后退,watermark于数据的时间戳有关
举例:
   以数字代表时间戳
            1  5  3  6  8  7 
watermark     2     5        8
watermark的时间怎么设置,最大的迟到时间 
数据会根据时间分桶,但是窗口的关闭是根据water
0-5的桶
1 w -2
4 w 1
5 w 2

5.4Watermark的传递

涉及到多个分区的数据 Watermark怎么保证共同 广播下游
涉及到多个上游watermark向下传递时,

每一个任务可能会有多个并行的上游任务发送watermark 取上游最小的watermaek,同时也要跟多个并行的下游任务 去发送他的watermark broadcast

5.5Watermark的定义

.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<Tuple2<String, Integer>>( Time.seconds(10)) {
            @Override
            public long extractTimestamp(Tuple2<String, Integer> element) {
                return element.f1 *1000L ;
            }
        });
实现的类里传的参数是watermark的延迟时间 类中是取哪个字段是通过extractTimestamp 来设置 取哪个字段
.assignTimestampsAndWatermarks(new AscendingTimestampExtractor>() {
            @Override
            public long extractAscendingTimestamp(Tuple2 element) {
                return element.f1;
            }
        });
如果数据是升序的,即不需要设置Watermark的时间 默认是-1毫秒的
assignTimestampsAndWatermarks方法可以传两个function 
主要分为两类:
AssignerWithPeriodicWatermarks 周期性生成watermark 隔一段时间自动生成
AssignerWithPunctuatedWatermarks 立即生成watermark 基于数据来判断是否生成watermaek 

5.6窗口开始时间

窗口的开始时间:
窗口的起始点, 源码里是一个取模的操作 使用当前的时间戳-offset
        splitDataStream.keyBy(0).window(TumblingEventTimeWindows.of(Time.seconds(2),Time.seconds(1)));
        splitDataStream.keyBy(0).window(SlidingEventTimeWindows.of(Time.seconds(2),Time.seconds(1),Time.seconds(1)));
通过窗口的时间变量控制窗口的开始时间
HiveSQL ->AST(抽象语法树) -> QB(查询块) ->OperatorTree(操作树)->优化后的操作树->mapreduce任务树->优化后的mapreduce任务树
Hive SQL编译过程

词法、语法解析: Antlr 定义 SQL 的语法规则,完成 SQL 词法,语法解析,将 SQL 转化为抽象语法树 AST Tree;
Antlr是一种语言识别的工具,可以用来构造领域语言。使用Antlr构造特定的语言只需要编写一个语法文件,定义词法和语法替换规则即可,Antlr完成了词法分析、语法分析、语义分析、中间代码生成的过程。

语义解析: 遍历 AST Tree,抽象出查询的基本组成单元 QueryBlock;
生成逻辑执行计划: 遍历 QueryBlock,翻译为执行操作树 OperatorTree;
优化逻辑执行计划: 逻辑层优化器进行 OperatorTree 变换,合并 Operator,达到减少 MapReduce Job,减少数据传输及 shuffle 数据量;
生成物理执行计划: 遍历 OperatorTree,翻译为 MapReduce 任务;
优化物理执行计划: 物理层优化器进行 MapReduce 任务的变换,
生成最终的执行计划

6状态编程

6.1状态的基本概念

Flink的状态 
 分为两大类:
   算子状态 OPeratior State
   键控状态 Keyed State
状态后端 State Backends

我们任务数据流一条一条处理的,可以看做是无状态算子,
类似于 window的 minby sum这种是带状态的算子,会有一个额外的任务来维护这个状态 state会缓存的内存中
状态需要关联 因为本地的状态需要有互通的(序列化反序列化 以及哪些状态储存在哪里 ) 高效存储 状态一致性等。 
被mange state  raw state

6.2举例示范

连续两个数据差值超过10度
public class FlatmapRichFunctionDev extends RichFlatMapFunction<Tuple2<String, Integer>, Tuple3<String, Integer, Integer>> {
    private Integer num_flag;
    //定义状态,保存上一次的值
    private ValueState<Integer> state;

    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);
        state = getRuntimeContext().getState(new ValueStateDescriptor<Integer>("last_temp", Integer.class));
    }

    public FlatmapRichFunctionDev(int num_flag) {
        this.num_flag = num_flag;
    }

    @Override
    public void flatMap(Tuple2<String, Integer> stringIntegerTuple2, Collector<Tuple3<String, Integer, Integer>> collector) throws Exception {
        Integer value = state.value();
        if (value != null) {
            Integer i = Math.abs(stringIntegerTuple2.f1 - value);
            if(i > num_flag) { 
                collector.collect(new Tuple3<>(stringIntegerTuple2.f0,value,stringIntegerTuple2.f1)) ;
            }
           
        }
        state.update(stringIntegerTuple2.f1);
    }
}

6.3键控状态

键控状态是根据输入数据流中定义的建 来维护和访问的
Flink为每一个key维护一个状态实例,并将具有相同键的所有数据,都分区到同一个算子中,这个任务会维护和处理这个key对应的状态
当任务处理一条数据时
键控状态的API
1)值状态(ValueState)
获取值:valueState.value()
修改值:valueState.update(value: T)
2)列表状态(ListState)
单个添加值:listState.add(value: T)
添加所有值:listState.addAll(values: java.util.List[T])
获得所有值:ListState.get()(注意:返回的是Iterable[T])
修改所有值:ListState.update(values: java.util.List[T])
3)映射状态(MapState)
根据Key获取值:mapState.get(key: K)
添加一对值:mapState.put(key: K, value: V)
判断Key是否存在:mapState.contains(key: K)
移除某个Key:mapState.remove(key: K)
4)聚合状态(ReducingState & AggregatingState)
add方法:ReducingState.add(value: T)
在使用聚合状态时,ReducingState需要传递三个参数:
5)通用API:
State.clear()是清空操作
键控状态的数据结构:
        值状态: Value state 列表状态 list state 映射状态 Map state 聚合状态 Reducing state & Aggregating State
键控装填的声明:
myValueState = getRuntimeContext().getState(new ValueStateDescriptor("last_temp", Integer.class));
算子状态:定义一个变量 提交给flink 通过checkpoint 将数据保存和恢复

7.状态后端

7.1概念

Flink提供不同的状态后端(State Backend)来区分状态的存储方式和存储位置。主要是指定存储状态数据的位置
Flink状态可以存储在java堆内存内或者内存之外。通过状态后端的设置,Flink允许应用持有大容量的状态。开发者可以在不改变应用逻辑的情况下设置状态后端。
实例:
env.setStateBackend(new FsStateBackend("hdfs://namenode:40010/flink/checkpoints"));

7.2自带的state Backend

1.MemoryStateBackend
2.FsStateBackend
3.RocksDBStateBackend

7.2.1MemoryStateBackend

默认使用的就是MemoryStateBackend,此时Flink的状态会保存在TaskManager的内存中,
                                而Checkpoint会保存在JobManager的内存中。
默认开启异步快照 可以通过MemoryStateBackend的构造函数配置进行关闭
                                new MemoryStateBackend(MAX_MEM_STATE_SIZE, false);

限制

每个独立的状态(state)默认限制大小为5MB, 可以通过构造函数增加容量
状态的大小不能超过akka的framesize大小 akka.framesize “10485760b”
聚合状态(aggregate state )必须放入JobManager的内存。

场景

本地测试
Flink任务状态数据量较小的场景

7.2.2 FsStateBackend

FsStateBackend将动态数据保存在taskmanger的内存中,
通过checkpoint机制,将状态快照写入配置好的文件系统或目录中。
最小元数据保存jobManager的内存中,
另外FsStateBackend通过配置一个fileStateThreshold阈值,小于该值时state存储到metadata中而非文件中。

场景

大状态、长窗口、大key/value状态的的任务
全高可用配置

7.2.3RocksDBStateBackend

RocksDBStateBackend将工作状态保存在RocksDB数据库(RocksDB 是一个基于 LSM 实现的 KV 数据库,
State数据部分存储在内存中,一部分存储在磁盘文件上)。
通过checkpoint, 整个RocksDB数据库被复制到配置的文件系统中。
最小元数据保存jobManager的内存中。
RocksDBStateBackend可以通过enableIncrementalCheckpointing参数配置是否进行增量Checkpoint(而MemoryStateBackend 和 FsStateBackend不能)。
跟FsStateBackend 不同的是,RocksDBStateBackend仅支持异步快照(asynchronous snapshots)。

场景

大状态、长窗口、大key/value状态的的任务
全高可用配置 由于RocksDBStateBackend将工作状态存储在taskManger的本地文件系统,状态数量仅仅受限于本地磁盘容量限制,对比于FsStateBackend保存工作状态在内存中,RocksDBStateBackend能避免flink任务持续运行可能导致的状态数量暴增而内存不足的情况,因此适合在生产环境使用。

8.ProcessFunction

8.1process

ProcessFunction 函数是低阶流处理算子,可以访问流应用程序所有(非循环)基本构建块:
事件 (数据流元素)
状态 (容错和一致性)
定时器 (事件时间和处理时间)
ProcessFunction 可以被认为是一种提供了对 KeyedState 和定时器访问的 FlatMapFunction。每在输入流中接收到一个事件,就会调用来此函数来处理。
定时器可以对处理时间和事件时间的变化做一些处理。每次调用 processElement() 都可以获得一个 Context 对象,通过该对象可以访问元素的事件时间戳以及 TimerServiceTimerService 可以为尚未发生的事件时间/处理时间实例注册回调。当定时器到达某个时刻时,会调用 onTimer() 方法。在调用期间,所有状态再次限定为定时器创建的键,允许定时器操作 KeyedState
对于容错的状态,ProcessFunction 可以通过 RuntimeContext 访问 KeyedState,类似于其他有状态函数访问 KeyedState。
如果要访问 KeyedState 和定时器,那必须在 KeyedStream 上使用 ProcessFunction。
stream.keyBy(...).process(new MyProcessFunction())

8.1.3.CoProcessFunction,双流操作

绑定到两个不同的输入流,分别调用processElement1和processElement2对两个输入流的数据进行处理

8.1.4.KeyedProcessFunction

区别在于Context多了 getCurrentKey方法 keyedProcessFunction作为ProcessFunction的扩展,在其onTimer方法中提供了对定时器对应key的访问

8.1.5.TimerService

计时器

你可能感兴趣的:(flink,kafka,大数据)