flink 入门介绍

220 阅读4分钟

什么是flink

Flink是一个针对流数据和批数据的分布式处理引擎。对Flink而言,其所要处理的主要场景就是流数据,批数据只是流数据的一个极限特例而已。在Flink框架当中,所有的任务当成流来处理,因此实现了更低延迟的实时流处理。

特点

  • 同时支持高吞吐、低延迟、高性能。
  • 同时支持事件时间和处理时间语义。
  • 支持有状态计算,并提供精确一次的状态一致性保障。
  • 基于轻量级分布式快照实现的容错机制。
  • 保证了高可用,动态扩展,实现7 * 24小时全天候运行。
  • 支持高度灵活的窗口操作。

几个基本概念

  • 有边界数据,无边界数据 无边界数据是一种不断增长,没有边界的数据集合,这类数据无法判断何时终止,也称为流数据,如:电商交易数据,PM2.5检测等等

有边界数据(静态数据): 这种数据更常见于已经保存好了的数据中。

  • 批与流

image.png 批处理的特点是有界、持久、大量,非常适合需要访问全套记录才能完成的计算工作,一般用于离线统计。

流处理的特点是无界、实时, 无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。

  • 有状态无状态 无状态:每次的执行都不依赖上一次或上N次的执行结果,每次的执行都是独立的

有状态:每次执行都需要以来上一次或者上N次的执行结果,某次的执行需要以来前面时间的处理结果

  • 时间 和 窗口

image.png

  1. 滚动窗口:每个小时,每一分钟,没有重叠
  2. 滑动窗口sliding:有窗口大小也有滑动步长,窗口之间是有重叠
  3. 会话窗口session:没有一个固定的持续时间

应用场景

  • 实时智能推荐
  • 复杂事件处理
  • 实时欺诈检测
  • 实时数仓与ETL
  • 流数据分析
  • 实时报表分析

分层API

image.png

  • ProcessFunction: 可以处理一或两条输入数据流中的单个事件或者归入一个特定窗口内的多个事件。
  • DataStream API:为许多通用的流处理操作提供了处理原语。 e.g. TopN计算
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime); //以processtime作为时间语义

DataStream<String> text = env.socketTextStream(hostName, port); //监听指定socket端口作为输入

与离线wordcount类似,程序首先需要把输入的整句文字按照分隔符split成一个一个单词,然后按照单词为key实现累加。

DataStream<Tuple2<String, Integer>> ds = text
                .flatMap(new LineSplitter()); //将输入语句split成一个一个单词并初始化count值为1的Tuple2<String, Integer>类型

private static final class LineSplitter implements
            FlatMapFunction<String, Tuple2<String, Integer>> {

        @Override
        public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
            // normalize and split the line
            String[] lines = value.toLowerCase().split("\W+");

            // emit the pairs
            for (String line : lines) {
                if (line.length() > 0) {
                    out.collect(new Tuple2<String, Integer>(line, 1));
                }
            }
        }
    }
DataStream<Tuple2<String, Integer>> wcount = ds
                .keyBy(0) //按照Tuple2<String, Integer>的第一个元素为key,也就是单词
                .window(SlidingProcessingTimeWindows.of(Time.seconds(600),Time.seconds(20))) 
                //key之后的元素进入一个总时间长度为600s,每20s向后滑动一次的滑动窗口
                .sum(1);// 将相同的key的元素第二个count值相加

全局TopN:数据流经过前面的处理后会每20s计算一次各个单词的count值并发送到下游窗口

 DataStream<Tuple2<String, Integer>> ret = wcount
                .windowAll(TumblingProcessingTimeWindows.of(Time.seconds(20))) 
                //所有key元素进入一个20s长的窗口(选20秒是因为上游窗口每20s计算一轮数据,topN窗口一次计算只统计一个窗口时间内的变化)
                .process(new TopNAllFunction(5));//计算该窗口TopN

windowAll是一个全局并发为1的特殊操作,也就是所有元素都会进入到一个窗口内进行计算。

private static class TopNAllFunction
            extends
            ProcessAllWindowFunction<Tuple2<String, Integer>, Tuple2<String, Integer>, TimeWindow> {

        private int topSize = 10;

        public TopNAllFunction(int topSize) {
            // TODO Auto-generated constructor stub

            this.topSize = topSize;
        }

        @Override
        public void process(
                ProcessAllWindowFunction<Tuple2<String, Integer>, Tuple2<String, Integer>, TimeWindow>.Context arg0,
                Iterable<Tuple2<String, Integer>> input,
                Collector<Tuple2<String, Integer>> out) throws Exception {
            // TODO Auto-generated method stub

            TreeMap<Integer, Tuple2<String, Integer>> treemap = new TreeMap<Integer, Tuple2<String, Integer>>(
                    new Comparator<Integer>() {

                        @Override
                        public int compare(Integer y, Integer x) {
                            // TODO Auto-generated method stub
                            return (x < y) ? -1 : 1;
                        }

                    }); //treemap按照key降序排列,相同count值不覆盖

            for (Tuple2<String, Integer> element : input) {
                treemap.put(element.f1, element);
                if (treemap.size() > topSize) { //只保留前面TopN个元素
                    treemap.pollLastEntry();
                }
            }

            for (Entry<Integer, Tuple2<String, Integer>> entry : treemap
                    .entrySet()) {
                out.collect(entry.getValue());
            }

        }

    }

www.jianshu.com/p/fbda25d99…

  • SQL & Table API: Flink 支持两种关系型的 API,Table API 和 SQL。