Flink笔记

文章目录

    • Flink简介
      • 是什么?
      • 为什么要用?
      • 哪些要用流数据?
      • 架构演变
      • Flink特点
      • Flink和Spark Streaming的区别
    • 部署
      • 配置
      • Standalone模式
      • Yarn模式
        • Session-cluster模式
        • Per-Job-Cluster模式
    • Flink运行时的组件
      • 任务提交流程
      • 任务调度原理
      • 专业术语
        • 并行度
        • Slots
        • 并行子任务的分配
        • 执行图(ExecutionGraph)
        • 数据传输形式
        • 任务链
    • Flink流处理API
      • Environment
      • 程序与数据流
      • Source
        • 从集合读取数据
        • 从文件读取数据
        • 从kafka读取数据
        • 自定义Source
      • Transform
        • map
        • flatmap
        • filter
        • KeyBy
        • 滚动聚合算子(Rolling Aggregation)
        • Reduce
        • split和select
        • Connect 和 CoMap
        • Union
      • Sink
        • 数据输出到kafka
        • 数据输出到Redis
        • 数据输出到ES
        • 数据输出到Mysql
      • 富函数(Rich Functions)
      • 数据重分区
    • window窗口
      • window类型
        • 滚动窗口(Tumbling Window)
        • 滑动窗口(Sliding Window)
        • 会话窗口(Session Window)
      • 窗口分配器

Flink简介

是什么?

Apache Flink 是一个框架和分布式处理引擎,用于对无界和有界数

据流进行状态计算。

为什么要用?

  1. 低延迟(来一个就处理一个)
  2. 高吞吐(能抵挡大量的数据,需要分布式)
  3. 结果的准确性和良好的容错性(分布式的话传输过程和数据处理的过程可能会出现乱序;一个挂了之后,回滚到非常近的状态,再跟着处理)

哪些要用流数据?

  • 数据报表
  • 广告投放

架构演变

事务处理

用户请求=》后台业务=》查询数据库

优点:实时性好

不足:数据量大后高并发难以支撑

Flink笔记_第1张图片

分析处理

先把数据整理放到一个数仓,再进行分析和查询

优点:能支持高并发

不足:实时性不好
Flink笔记_第2张图片

有状态的流处理

把数据不放在关系数据库,而放在本地内存,设置一个本地状态,相当于用本地内存中的状态代替了关系数据库中的表。这样的好处就是速度会快很多,在高并发时用集群做扩展。

改进

把数据从查数据库改为直接查内存,放到内存宕机了数据会丢失,所以需要定期去保存数据,(Periodic Checkpoint)出错了可以恢复。这样做好处就是速度快,但是如果在分布式下,数据的处理顺序会出现乱序。

Flink笔记_第3张图片

lambda架构

  • 保持低延迟和结果准确
  • 用了两套系统:Batch Layer进行批处理,保证了数据的准确性。Speed Layer进行快速处理,保证了低延迟。
  • 不足就是用了两套系统,复杂度高,占用系统资源

Flink笔记_第4张图片

Flink出现

  • 低延迟
  • 高吞吐
  • 数据准确不乱序

Flink笔记_第5张图片

Flink特点

  • 支持事件时间和处理时间语义
  • 精确一次的状态保证
  • 低延迟,每秒处理百万个事件,毫秒级延迟
  • 与众多常用存储系统的连接
  • 高可用,动态扩展,实现7*24小时全天运行

事件驱动

和传统的架构类似,数据到到flink,会在内存中进行处理,同时会定期存盘。当数据处理完后会写入事件日志中。

Flink笔记_第6张图片

无界流和有界流

无界流:没有边界,代表数据不断地在产生,是实时的数据。

有界流:有边界,比如离线的数据,不会再产生了。

分层API

  • 表级别的API(简单的操作)
  • 数据流级别的API(实时计算时用DataStream,离线用DataSet)
  • 事件状态级别的API

Flink笔记_第7张图片

Flink和Spark Streaming的区别

底层架构不一样,Flink是以流的方式,Spark Streaming把数据分的很细,但底层还是以批处理的方式。

Flink笔记_第8张图片

部署

配置

# jobManager 的IP地址
jobmanager.rpc.address: localhost
# JobManager 的端口号
jobmanager.rpc.port: 6123
# JobManager JVM heap 内存大小
jobmanager.heap.size: 1024m
# TaskManager JVM heap 内存大小
taskmanager.heap.size: 1024m
# 每个 TaskManager 提供的任务 slots 数量大小
taskmanager.numberOfTaskSlots: 1
# 程序默认并行计算的个数
parallelism.default: 1
# 文件系统来源
# fs.default-scheme

Standalone模式

  • 启动:./start-cluster.sh
  • 并行度优先级:代码层面>界面配置>默认配置文件配置

用命令方式执行:

…/flink run -c kmoon.zhu.StreamWordCount –p 2

FlinkTutorial-1.0-SNAPSHOT-jar-with-dependencies.jar --host lcoalhost –port 7777

Yarn模式

要求Flink具有Hadoop支持的版本,Hadoop环境要在2.2以上,并且集群中安装有HDFS服务。

Session-cluster模式

  • 需要先启动集群,然后再提交作业。
  • 向Yarn请求资源空间后,资源不变。
  • 资源满了,其他作业需要等待
  • 适合小规模执行时间短的作业
  • 所有作业共享Dispatch和ResourceManager

Flink笔记_第9张图片

Per-Job-Cluster模式

  • 每个Job对应每个集群

  • 每提交一个作业都会单独向yarn申请资源

  • 作业之间相互不影响

  • 适合大规模长时间运行的作业

  • 所有作业独享Dispatcher和ResourceManager

Flink笔记_第10张图片

Flink运行时的组件

作业管理器(JobManager)

  • 让TaskManager干活
  • 向管理器(ResourceManage)申请资源
  • 协调检查点(checkpoints)进行数据存盘

任务管理器(TaskManager)

  • 负责为JobManager干活
  • 一个JobManager负责多个TaskManager,1个TaskManager包含多个插槽,插槽多少个代表并行度多少
  • TaskManager会向资源管理器注册它的插槽,收到资源管理器指令后,TaskManager会把插槽提供给JobManager调用,JobManager就可以向插槽分配任务来执行

资源管理器(ResourceManager)

  • 负责管理插槽(slot),插槽在flink中是最小的处理资源单元。
  • 不同的环境和资源管理工具有不同的资源管理器,比如YARN、Mesos、K8s、standalone部署
  • 当JobManager申请插槽资源时,如果ResourceManager没有足够的插槽,它则会向资源提供平台发起会话。

分发器Dispatcher)

  • 可以跨作业运行,为应用提供了Rest接口
  • 提供WebUI界面,方便展示和监控Flink

任务提交流程

Flink笔记_第11张图片

YARN
Flink笔记_第12张图片

任务调度原理

Flink笔记_第13张图片

怎样进行并行计算?
每一个任务,每一步操作,都可以设置并行度,然后拆成并行的几个task,几个任务,就可以完成并行计算。
设置并行任务,分配到不同的slot上,多线程就可以执行起来。

并行的任务,需要占用多少slot?

一个流处理程序,到底包含多少个任务?

专业术语

并行度

  • 特定算子的子任务的个数
  • 整条流Stream的并行度是指当前所有并行度中最大的那个

Flink笔记_第14张图片

Slots

  • 执行一个独立任务/线程所需要的最小单元
  • 默认按照CPU的核心数来分配资源,每个slots的内存是隔离的,但是CPU可以共享的。
  • TaskManager对应一个进程,Slots对应一个线程
  • 默认情况下,Flink允许子任务共享slot,即使他们是不同任务的子任务。这样的结果是,一个slot可以保存作业的整个管道。
  • 一个slot可以保存作业整个管道的好处:如果其他的TaskManager挂掉了,并行度降低到1,也能保证作业能够完成执行。提高了健壮性。
  • 使用slotSharingGroup对slot分组后,slot不会保存作业的整个管道
    Flink笔记_第15张图片

并行子任务的分配

一共有2+4+2+4+4=16个任务
需要slot=4
一般情况下,算子里面最大的并行度,代表了我们当前需要的slot数量
Flink笔记_第16张图片

  • 在运行时,Flink上运行的程序会被映射成“逻辑数据流”(dataflows),它包 含了这三部
  • 每一个dataflow以一个或多个sources开始以一个或多个sinks结束。

执行图(ExecutionGraph)

Flink 中的执行图可以分成四层:StreamGraph -> JobGraph -> ExecutionGraph ->
物理执行图。

  • StreamGraph:是根据用户通过 Stream API 编写的代码生成的最初的图。用
    来表示程序的拓扑结构。
  • JobGraph:StreamGraph 经过优化后生成了 JobGraph,提交给 JobManager 的
    数据结构。主要的优化为,将多个符合条件的节点 chain 在一起作为一个节点,这
    样可以减少数据在节点之间流动所需要的序列化/反序列化/传输消耗。
  • ExecutionGraph : JobManager 根 据 JobGraph 生 成 ExecutionGraph 。
    ExecutionGraph 是 JobGraph 的并行化版本,是调度层最核心的数据结构。
  • 物理执行图:JobManager 根据 ExecutionGraph 对 Job 进行调度后,在各个
    TaskManager 上部署 Task 后形成的“图”,并不是一个具体的数据结构。

数据传输形式

  • 一个程序中,不同的算子可能具有不同的并行度
  • 算子之间传输数据的形式可以是one-to-one模式也可以是redistributing模式,具体哪种形式,取决于算子的种类
  • map、filter、flatMap等算子都是one-to-one模式
  • Redistributing模式:stream的分区会发生改变,例如,keyBy 基于 hashCode 重
    分区、而 broadcast 和 rebalance 会随机重新分区

任务链

  • Flink采用了一种任务链优化技术。
  • 如果并行度相同,并且为one-to-one,则这样的算子可以链接在一起形成一个task。
    -Flink笔记_第17张图片

Flink流处理API

Environment

getExecutionEnvironmen
创建一个执行环境,表示当前执行程序的上下文。 如果程序是独立调用的,则
此方法返回本地执行环境;如果从命令行客户端调用程序以提交到集群,则此方法
返回此集群的执行环境,也就是说,getExecutionEnvironment 会根据查询运行的方
式决定返回什么样的运行环境,是最常用的一种创建执行环境的方式。

ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();

程序与数据流

所有的Flink的程序都是由三部分组成:
Source、Transformation、Sink
Source:负责读取数据源
Transformation:利用各种算子进行处理加工
Sink:负责输出

Source

从集合读取数据

public class SourceTest1_Collection {
    public static void main(String[] args) throws Exception{
        // 创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从集合中读取数据
        DataStream<SensorReading> dataStream = env.fromCollection(Arrays.asList(
                new SensorReading("sensor_1", 1547718199L, 35.8),
                new SensorReading("sensor_6", 1547718201L, 15.4),
                new SensorReading("sensor_7", 1547718202L, 6.7),
                new SensorReading("sensor_10", 1547718205L, 38.1)
        ));

        DataStream<Integer> integerDataStream = env.fromElements(1, 2, 4, 67, 189);

        // 打印输出
        dataStream.print("data");
        integerDataStream.print("int");

        // 执行
        env.execute();
    }
}

从文件读取数据

public class SourceTest2_File {
    public static void main(String[] args) throws Exception{
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从文件读取数据
        DataStream<String> dataStream = env.readTextFile("D:\\Projects\\BigData\\FlinkTutorial\\src\\main\\resources\\sensor.txt");

        // 打印输出
        dataStream.print();

        env.execute();
    }
}

从kafka读取数据

public class SourceTest3_Kafka {
    public static void main(String[] args) throws Exception{
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        Properties properties = new Properties();
        properties.setProperty("bootstrap.servers", "localhost:9092");
        properties.setProperty("group.id", "consumer-group");
        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");

        // 从文件读取数据
        DataStream<String> dataStream = env.addSource( new FlinkKafkaConsumer011<String>("sensor", new SimpleStringSchema(), properties));

        // 打印输出
        dataStream.print();

        env.execute();
    }
}

自定义Source

 public class SourceTest4_UDF {
    public static void main(String[] args) throws Exception{
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从文件读取数据
        DataStream<SensorReading> dataStream = env.addSource( new MySensorSource() );

        // 打印输出
        dataStream.print();

        env.execute();
    }

    // 实现自定义的SourceFunction
    public static class MySensorSource implements SourceFunction<SensorReading>{
        // 定义一个标识位,用来控制数据的产生
        private boolean running = true;

        @Override
        public void run(SourceContext<SensorReading> ctx) throws Exception {
            // 定义一个随机数发生器
            Random random = new Random();

            // 设置10个传感器的初始温度
            HashMap<String, Double> sensorTempMap = new HashMap<>();
            for( int i = 0; i < 10; i++ ){
                sensorTempMap.put("sensor_" + (i+1), 60 + random.nextGaussian() * 20);
            }

            while (running){
                for( String sensorId: sensorTempMap.keySet() ){
                    // 在当前温度基础上随机波动
                    Double newtemp = sensorTempMap.get(sensorId) + random.nextGaussian();
                    sensorTempMap.put(sensorId, newtemp);
                    ctx.collect(new SensorReading(sensorId, System.currentTimeMillis(), newtemp));
                }
                // 控制输出频率
                Thread.sleep(1000L);
            }
        }

        @Override
        public void cancel() {
            running = false;
        }
    }}

Transform

map

把流数据转换成一个一个的数据输出
Flink笔记_第18张图片

 //把String转换成长度输出
        DataStream<Integer> mapStream = inputStream.map(new MapFunction<String, Integer>() {
            @Override
            public Integer map(String value) throws Exception {
                return value.length();
            }
        });

flatmap

把流数据转换成多条数据输出

 // flatmap,按逗号分字段
        DataStream<String> flatMapStream = inputStream.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public void flatMap(String value, Collector<String> out) throws Exception {
                String[] fields = value.split(",");
                for( String field: fields )
                    out.collect(field);
            }
        });

filter

把流数据进行过滤后输出
Flink笔记_第19张图片

// filter, 筛选sensor_1开头的id对应的数据
        DataStream<String> filterStream = inputStream.filter(new FilterFunction<String>() {
            @Override
            public boolean filter(String value) throws Exception {
                return value.startsWith("sensor_1");
            }
        });

KeyBy

把当前流分成多个分区,DataStream → KeyedStream:逻辑地将一个流拆分成不相交的分区,每个分
区包含具有相同 key 的元素,在内部以 hash 的形式实现的。
Flink笔记_第20张图片

滚动聚合算子(Rolling Aggregation)

这些算子可以针对 KeyedStream 的每一个支流做聚合。
sum()
min()
max()
minBy()
maxBy()

 KeyedStream<SensorReading, Tuple> keyedStream = dataStream.keyBy("id");
 // 滚动聚合,取当前最大的温度值
     DataStream<SensorReading> resultStream = keyedStream.maxBy("temperature");
     resultStream.print("result");

Reduce

一个分组数据流的聚合操作,合并当前的元素
和上次聚合的结果,产生一个新的值,返回的流中包含每一次聚合的结果,而不是
只返回最后一次聚合的最终结果。

// reduce 聚合,取最小的温度值,并输出当前的时间戳
 DataStream<SensorReading> reduceStream = keyedStream.reduce(new 
ReduceFunction<SensorReading>() {
 @Override
 public SensorReading reduce(SensorReading value1, SensorReading value2) 
throws Exception {
 return new SensorReading(
 value1.getId(),
 value2.getTimestamp(),
 Math.min(value1.getTemperature(), value2.getTemperature()));
 }
 });

split和select

用split切分流,切分后用select来得到不同的流数据

 //  分流,按照温度值30度为界分为两条流
        SplitStream<SensorReading> splitStream = dataStream.split(new OutputSelector<SensorReading>() {
            @Override
            public Iterable<String> select(SensorReading value) {
                return (value.getTemperature() > 30) ? Collections.singletonList("high") : Collections.singletonList("low");
            }
        });

        DataStream<SensorReading> highTempStream = splitStream.select("high");
        DataStream<SensorReading> lowTempStream = splitStream.select("low");
        DataStream<SensorReading> allTempStream = splitStream.select("high", "low");

Connect 和 CoMap

用Connect来连接两个流,连接后内部依然保持各自的数据和形式不发生任何变化,所以再需要用CoMap对流进行处理后变成一个流

ConnectedStreams<Tuple2<String, Double>, SensorReading> connectedStreams = warningStream.connect(lowTempStream);

        DataStream<Object> resultStream = connectedStreams.map(new CoMapFunction<Tuple2<String, Double>, SensorReading, Object>() {
            @Override
            public Object map1(Tuple2<String, Double> value) throws Exception {
                return new Tuple3<>(value.f0, value.f1, "high temp warning");
            }

            @Override
            public Object map2(SensorReading value) throws Exception {
                return new Tuple2<>(value.getId(), "normal");
            }
        });

Union

可以合并多条流,但是合并的数据类型得一样

 highTempStream.union(lowTempStream, allTempStream);

Sink

数据输出到kafka

public class SinkTest1_Kafka {
    public static void main(String[] args) throws Exception{
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

//        // 从文件读取数据
//        DataStream inputStream = env.readTextFile("D:\\Projects\\BigData\\FlinkTutorial\\src\\main\\resources\\sensor.txt");

        Properties properties = new Properties();
        properties.setProperty("bootstrap.servers", "localhost:9092");
        properties.setProperty("group.id", "consumer-group");
        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");

        // 从文件读取数据
        DataStream<String> inputStream = env.addSource( new FlinkKafkaConsumer011<String>("sensor", new SimpleStringSchema(), properties));

        // 转换成SensorReading类型
        DataStream<String> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2])).toString();
        });

        dataStream.addSink( new FlinkKafkaProducer011<String>("localhost:9092", "sinktest", new SimpleStringSchema()));

        env.execute();
    }
}

数据输出到Redis

public class SinkTest2_Redis {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从文件读取数据
        DataStream<String> inputStream = env.readTextFile("D:\\MyConfiguration\\kmoon.zhu.TCENT\\Desktop\\Flink\\FlinkTutorial\\src\\main\\resources\\sensor.txt");

        // 转换成SensorReading类型
        DataStream<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });

        // 定义jedis连接配置
        FlinkJedisPoolConfig config = new FlinkJedisPoolConfig.Builder()
                .setHost("10.181.122.24")
                .setPort(6379)
                .build();

        dataStream.addSink( new RedisSink<>(config, new MyRedisMapper()));

        env.execute();
    }

    // 自定义RedisMapper
    public static class MyRedisMapper implements RedisMapper<SensorReading>{
        // 定义保存数据到redis的命令,存成Hash表,hset sensor_temp id temperature
        @Override
        public RedisCommandDescription getCommandDescription() {
            return new RedisCommandDescription(RedisCommand.HSET, "sensor_temp");
        }

        @Override
        public String getKeyFromData(SensorReading data) {
            return data.getId();
        }

        @Override
        public String getValueFromData(SensorReading data) {
            return data.getTemperature().toString();
        }
    }
}

数据输出到ES

public class SinkTest3_Es {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从文件读取数据
        DataStream<String> inputStream = env.readTextFile("D:\\Projects\\BigData\\FlinkTutorial\\src\\main\\resources\\sensor.txt");

        // 转换成SensorReading类型
        DataStream<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });

        // 定义es的连接配置
        ArrayList<HttpHost> httpHosts = new ArrayList<>();
        httpHosts.add(new HttpHost("localhost", 9200));

        dataStream.addSink(new ElasticsearchSink.Builder<SensorReading>(httpHosts, new MyEsSinkFunction()).build());

        env.execute();
    }

    // 实现自定义的ES写入操作
    public static class MyEsSinkFunction implements ElasticsearchSinkFunction<SensorReading>{
        @Override
        public void process(SensorReading element, RuntimeContext ctx, RequestIndexer indexer) {
            // 定义写入的数据source
            HashMap<String, String> dataSource = new HashMap<>();
            dataSource.put("id", element.getId());
            dataSource.put("temp", element.getTemperature().toString());
            dataSource.put("ts", element.getTimestamp().toString());

            // 创建请求,作为向es发起的写入命令
            IndexRequest indexRequest = Requests.indexRequest()
                    .index("sensor")
                    .type("readingdata")
                    .source(dataSource);

            // 用index发送请求
            indexer.add(indexRequest);
        }
    }
}

数据输出到Mysql

public class SinkTest4_Jdbc {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        // 从文件读取数据
//        DataStream inputStream = env.readTextFile("D:\\Projects\\BigData\\FlinkTutorial\\src\\main\\resources\\sensor.txt");
//
//        // 转换成SensorReading类型
//        DataStream dataStream = inputStream.map(line -> {
//            String[] fields = line.split(",");
//            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
//        });

        DataStream<SensorReading> dataStream = env.addSource(new SourceTest4_UDF.MySensorSource());

        dataStream.addSink(new MyJdbcSink());

        env.execute();
    }

    // 实现自定义的SinkFunction
    public static class MyJdbcSink extends RichSinkFunction<SensorReading> {
        // 声明连接和预编译语句
        Connection connection = null;
        PreparedStatement insertStmt = null;
        PreparedStatement updateStmt = null;

        @Override
        public void open(Configuration parameters) throws Exception {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "3997");
            insertStmt = connection.prepareStatement("insert into sensor_temp (id, temp) values (?, ?)");
            updateStmt = connection.prepareStatement("update sensor_temp set temp = ? where id = ?");
        }

        // 每来一条数据,调用连接,执行sql
        @Override
        public void invoke(SensorReading value, Context context) throws Exception {
            // 直接执行更新语句,如果没有更新那么就插入
            updateStmt.setDouble(1, value.getTemperature());
            updateStmt.setString(2, value.getId());
            updateStmt.execute();
            if( updateStmt.getUpdateCount() == 0 ){
                insertStmt.setString(1, value.getId());
                insertStmt.setDouble(2, value.getTemperature());
                insertStmt.execute();
            }
        }

        @Override
        public void close() throws Exception {
            insertStmt.close();
            updateStmt.close();
            connection.close();
        }
    }
}

富函数(Rich Functions)

“富函数”是 DataStream API 提供的一个函数类的接口,所有 Flink 函数类都
有其 Rich 版本。它与常规函数的不同在于,可以获取运行环境的上下文,并拥有一
些生命周期方法,所以可以实现更复杂的功能。
RichMapFunction
RichFlatMapFunction
RichFilterFunction

   DataStream<Tuple2<String, Integer>> resultStream = dataStream.map( new MyMapper() );
     // 实现自定义富函数类
    public static class MyMapper extends RichMapFunction<SensorReading, Tuple2<String, Integer>>{
        @Override
        public Tuple2<String, Integer> map(SensorReading value) throws Exception {
//            getRuntimeContext().getState();
            return new Tuple2<>(value.getId(), getRuntimeContext().getIndexOfThisSubtask());
        }

        @Override
        public void open(Configuration parameters) throws Exception {
            // 初始化工作,一般是定义状态,或者建立数据库连接
            System.out.println("open");
        }

        @Override
        public void close() throws Exception {
            // 一般是关闭连接和清空状态的收尾操作
            System.out.println("close");
        }
    }

数据重分区

rebalance:均匀分布
global全部都分配到进程1了
keyBy非均匀分布 sensor_1 全部都分配到了进程1
shuffle:随机分布

window窗口

flink把无限流切割成有限流的一种方式

window类型

➢ CountWindow:按照指定的数据条数生成一个 Window,与时间无关。
滚动窗口(Tumbling Window)
滑动窗口(Sliding Window)
➢ TimeWindow:按照时间生成 Window
滚动窗口(Tumbling Window)
滑动窗口(Sliding Window)
会话窗口(Session Window)

滚动窗口(Tumbling Window)

  • 将数据依据固定的长度对窗口进行划分
  • 使用时需要指定窗口的长度
  • 时间对齐,窗口长度固定,没有重叠
  • 可以算作滑动步长和窗口宽度相等的一种特殊的滑动窗口
    适用场景:适合做BI统计(做每个时间段的聚合操作)
    Flink笔记_第21张图片

滑动窗口(Sliding Window)

  • 滑动窗口由固定的窗口长度和滑动间隔组成
  • 使用时需要指定窗口的长度和滑动间隔
  • 时间对齐,窗口长度固定,可以有重叠
    适用场景:对最近一个时间段内的统计(求某接口最近5min的失败率来决定是否要报警)

Flink笔记_第22张图片

会话窗口(Session Window)

  • 时间无对齐
  • 当它在一个固定的时间周期内不再收到元素,即非活动间隔产生,那个这个窗口就会关闭
  • 配置的是最小时间间隔
    Flink笔记_第23张图片

窗口分配器

  • 我们可以用 .window() 来定义一个窗口,然后基于这个 window 去做一些聚 合或者其它处理操作。
  • window()方法必须在KeyBy之后使用

创建不同类型的窗口
滚动时间窗口(tumbling time window)

.timeWindow(Time.seconds(15))

滑动时间窗口(sliding time window)

.timeWindow(Time.seconds(15),Time.seconds(5))

会话窗口(session window)

.window(EventTimeSessionWindows.withGap(Time.minutes(10))

创建不同类型的窗口
滚动计数窗口(tumbling count window)

.countWindow(5)

滑动计数窗口(sliding count window)

.countWindow(10,2)

你可能感兴趣的:(实习,同程实习笔记,中间件,flink,big,data,java)