Apache Flink入门

Flink官方文档:https://flink.apache.org/

Flink官方中文文档:https://apachecn.github.io/flink-doc-zh

一.Flink是什么

Flink 主页在其顶部展示了该项目的理念:“Apache Flink 是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源流处理框架”。

具体来说,Apache Flink 是一个框架和分布式处理引擎,用于对无界有界数据流进行有状态计算。Flink 被设计在所有常见的集群环境中运行,以内存执行速度和任意规模来执行计算。

a1764552e2c74b82b3ec86c115629fb9.jpeg

Flink的优点有

  • 高吞吐和低时延
  • 可以连接到常用的外部系统例如MQ、MySQL、Redis、HDFS等
  • 高可用 

Flink的应用场景

  • 例如天猫双11实时交易额数字大屏
  • 传感器实时数据采集、展示
  • 平台实时检测用户异常行为

二.入门概念

无界数据流:有定义流的开始,但没有定义流的结束。无界数据流会源源不断的产生数据,且无界流的数据需要持续处理。无界流处理通常被称为流处理

有界数据流:有定义流的开始,也有定义流的结束。有界流可以等待所有数据到达后再进行计算,且可以进行排序,有界流处理通常被称为批处理

DataStream:流处理,用于处理无界数据流。

DataSet:批处理,用于处理有界数据流。

有状态计算:是指在程序计算过程中,在Flink程序内部存储计算产生的中间结果,并提供给后续Function或算子计算结果使用。状态数据可以存储在Flink自己的存储介质中。

三.快速上手案例(单词统计)

需求:统计一段文字中,每个单词出现的频次。

1.创建一个Maven工程。

2.导入Maven坐标。

<properties>
    <flink.version>1.17.0</flink.version>
</properties>

<dependencies>
    <!--Flink核心依赖-->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-java</artifactId>
        <version>${flink.version}</version>
    </dependency>
    <!--Flink客户端-->
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients</artifactId>
        <version>${flink.version}</version>
    </dependency>
</dependencies>

3.批处理(DataSet API 已过时)

批处理基本思路:先逐行读入文件数据,然后将每一行文字拆分成单词;接着按照单词分组,统计每组数据的个数,就是对应单词的频次。

3.1 数据准备

在项目根目录创建 input 文件夹,并在下面创建文件 words.txt ,并输入一些文本。

92ef744c5b9449a4893b7b5dd53e2cee.jpeg

3.2 代码编写

新建Java类BatchWordCount。

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.AggregateOperator;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.FlatMapOperator;
import org.apache.flink.api.java.operators.UnsortedGrouping;
import org.apache.flink.api.java.tuple.Tuple2;

/**
 * DataSet API 实现 WordCCount (不推荐)
 */
public class BatchWordCount {
    public static void main(String[] args) throws Exception {
        // 1. 创建批式执行环境
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        // 2. 从文件读取数据  按行读取(存储的元素就是每行的文本)
        DataSource<String> lineDS = env.readTextFile("input/words.txt");
        // 3. 转换数据格式
        FlatMapOperator<String, Tuple2<String, Long>> wordAndOne = lineDS.flatMap((FlatMapFunction<String, Tuple2<String, Long>>) (line, out) -> {
            String[] words = line.split(" ");
            for (String word : words) {
                // 转换成 二元组 ( word , 1 ) 并通过 采集器 向下游发送数据
                out.collect(Tuple2.of(word,1L));
            }
        });
        // 4. 按照 word 进行分组
        UnsortedGrouping<Tuple2<String, Long>> wordAndOneUG = wordAndOne.groupBy(0);
        // 5. 分组内聚合统计
        AggregateOperator<Tuple2<String, Long>> sum = wordAndOneUG.sum(1);
        // 6.打印结果
        sum.print();
    }
}

输出结果:

(flink,1)
(world,1)
(hello,3)
(java,1)

4.流处理(有界流)

DataStream API更加强大,可以直接处理批处理和流处理的所有场景,也是官方推荐的方式。

4.1 代码编写

新建Java类StreamWordCount 。

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

public class StreamWordCount {

    public static void main(String[] args) throws Exception {
        // 1.创建流式执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 2. 从文件读取数据  按行读取(存储的元素就是每行的文本)
        DataStreamSource<String> lineStream = env.readTextFile("input/words.txt");
        // 3. 转换、分组、求和,得到统计结果
        SingleOutputStreamOperator<Tuple2<String, Long>> sum = lineStream.flatMap((FlatMapFunction<String, Tuple2<String, Long>>) (line, out) -> {
            String[] words = line.split(" ");
            for (String word : words) {
                // 转换成 二元组 ( word , 1 ) 并通过 采集器 向下游发送数据
                out.collect(Tuple2.of(word, 1L));
            }
        }).keyBy(data -> data.f0).sum(1);
        // 4.打印
        sum.print();
        // 5.执行
        env.execute();
    }
}

以上代码会报错

e123305709aa4ddb85032ccedc124b1c.png

这是因为Flink还具有一个类型提取系统,可以分析函数的输入和返回类型,自动获取类型信息,从而获得对应的序列化器和反序列化器。但是,由于Java中泛型擦除的存在,在某些特殊情况下(比如Lambda表达式中),自动提取的信息是不够精细的——只告诉Flink当前的元素由“船头、船身、船尾”构成,根本无法重建出“大船”的模样;这时就需要显式地提供类型信息,才能使应用程序正常工作或提高其性能。

因为对于flatMap里传入的Lambda表达式,系统只能推断出返回的是Tuple2类型,而无法得到Tuple2<String, Long>。只有显式地告诉系统当前的返回类型,才能正确地解析出完整数据。

则需要改成以下,或者使用匿名内部类实现。

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * DataStream API 实现 WordCCount
 */
public class StreamWordCount {

    public static void main(String[] args) throws Exception {
        // 1.创建流式执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 2. 从文件读取数据  按行读取(存储的元素就是每行的文本)
        DataStreamSource<String> lineStream = env.readTextFile("input/words.txt");
        // 3. 转换、分组、求和,得到统计结果
        SingleOutputStreamOperator<Tuple2<String, Long>> sum = lineStream.flatMap((FlatMapFunction<String, Tuple2<String, Long>>) (line, out) -> {
            String[] words = line.split(" ");
            for (String word : words) {
                out.collect(Tuple2.of(word, 1L));
            }
        }).returns(Types.TUPLE(Types.STRING, Types.LONG))
            .keyBy(data -> data.f0)
            .sum(1);
        // 4.打印
        sum.print();
        // 5.执行
        env.execute();
    }
}

输出结果:

2> (java,1)
7> (flink,1)
3> (hello,1)
3> (hello,2)
3> (hello,3)
5> (world,1)

通过结果也能体现“流”的概念,数据来一条处理一条,hello这个单词累加的过程,从2到3,就是有状态计算的体现。其中结果前的数字是并行线程编号。

主要观察与批处理程序BatchWordCount的不同:

  • 创建执行环境的不同,流处理程序使用的是StreamExecutionEnvironment
  • 转换处理之后,得到的数据对象类型不同。
  • 分组操作调用的是keyBy方法,可以传入一个匿名函数作为键选择器(KeySelector),指定当前分组的key是什么。
  • 代码末尾需要调用env的execute方法,开始执行任务。

5.流处理( Socket 无界流) 

        在实际的生产环境中,真正的数据流其实是无界的,有开始却没有结束,这就要求我们需要持续地处理捕获的数据。为了模拟这种场景,可以监听socket端口,然后向该端口不断的发送数据。

5.1 安装netcat

在服务器上安装netcat,用于开启Socket隧道,让flink连接。

[root@VM-12-13-centos ~]# yum install -y netcat

配置并使环境变量生效

export NETCAT_HOME=/usr/local/netcat
export PATH=$PATH:$NETCAT_HOME/bin

5.2 代码编写

新建Java类SocketStreamWordCount ,代码如下:

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

/**
 * DataStream 实现 wordCount : 读Socket(无界流)
 */
public class SocketStreamWordCount {
    public static void main(String[] args) throws Exception {
        // 创建流式执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 2. 读取文本流:socketTextStream(hostname,port);
        DataStreamSource<String> lineStream = env.socketTextStream("服务器ip", 8877);
        System.out.println(lineStream);
        // 3. 转换、分组、求和,得到统计结果
        SingleOutputStreamOperator<Tuple2<String, Long>> sum = lineStream.flatMap((String line, Collector<Tuple2<String, Long>> out) -> {
                    String[] words = line.split(" ");
                    for (String word : words) {
                        out.collect(Tuple2.of(word, 1L));
                    }
                }).returns(Types.TUPLE(Types.STRING, Types.LONG))
                .keyBy(data -> data.f0)
                .sum(1);
        // 4. 打印
        sum.print();

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

启动。 

5.3 启动Socket

启动Socket隧道,并设置通信端口8877

[root@VM-12-13-centos ~]# nc -lk 8877

开启 监听模式,用于指定nc将处于监听模式。通常 这样代表着为一个 服务等待客户端来链接指定的端口。(记得开放对应端口)

注意:要先启动端口,后启动StreamWordCount程序,否则会报超时连接异常。

5.4 启动java程序

程序启动之后没有任何输出、也不会退出。这是正常的,因为Flink的流处理是事件驱动的,当前程序会一直处于监听状态,只有接收到数据才会执行任务、输出统计结果。

5.5 发送数据

往命令行输入一些内容,例如:

[root@VM-12-13-centos ~]# nc -lk 8877
hello eason
hello flink

控制台则会输出:

6> (eason,1)
3> (hello,1)
7> (flink,1)
3> (hello,2)

Flink入门就先写到这。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

- Hola -

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值