【Flink】初识 Flink,来一起做个 WordCount!

Flink 概述

Apache Flink 是一个开源的流处理框架,专为分布式、高性能、随时可用且准确的流处理应用程序而设计。它提供了丰富的状态管理功能和事件时间支持,能够确保精确一次的计算语义,非常适合构建可扩展的ETL、数据分析和事件驱动的流式应用程序。

Flink 能够处理无界和有界数据流,并且能够运行在各种集群资源管理器上,如 YARN、Mesos 和 Kubernetes,同时也支持裸机集群部署。

有界数据集指的是数据量是有限的、已知的,并且可以在某个时间点完全获取或处理的数据集合。
无界数据流指的是数据是连续不断生成的,数据量理论上是无限的,或者在处理时数据尚未完全产生。

Flink 特点

  • 高吞吐和低延迟
    Flink 设计用于实现高速数据流处理,它可以在毫秒级别实现低延迟。

  • 有状态的流处理
    Flink 支持有状态的流处理操作,允许用户在无界数据流上维护和访问状态。

  • 事件时间(Event Time)语义
    Flink 支持基于事件时间的流处理,允许程序正确处理乱序事件和处理时间的不确定性。

  • 水印(Watermarks)机制
    Flink 使用水印机制来处理事件时间以及乱序事件,确保事件能够按照时间顺序进行处理。

  • 窗口操作
    Flink 提供了丰富的窗口操作,包括滚动窗口、滑动窗口和会话窗口等。

  • 精确一次(Exactly-once)状态一致性
    Flink 通过其检查点(Checkpoint)机制和状态后端确保了即使在故障发生后,也能提供精确一次的状态一致性。

  • 多样化的 API
    Flink 提供了 DataStream API 用于流处理,DataSet API 用于批处理,Table API 和 SQL 用于关系型数据操作。

Flink 应用场景

Flink 的应用场景非常广泛,包括但不限于:

  1. 事件驱动型应用:例如实时推荐、金融反欺诈、实时规则预警等,这类应用通常根据事件流中的事件触发计算、更新状态或进行外部系统操作。

  2. 数据分析型应用:例如营销大屏、销量统计、用户行为分析等,这些应用需要对大量数据进行实时分析和聚合。

  3. 数据管道型应用(ETL):Flink 通过丰富的 Connector 支持多种数据源和数据 Sink,非常适合进行数据的抽取、转换和加载。

Flink 与 Spark Streaming 对比

在我们学习 Flink 的时候,常常会和 Spark Streaming 进行比较,那么,通过下面的表格,概述了两者在不同特性上的差异:

对比项FlinkSpark Streaming
处理模型原生流处理模型(Native)微批处理(Micro-Batching)
API 类型声明式声明式
语义保证精确一次(Exactly-once)精确一次(Exactly-once)
容错机制检查点(Checkpoint)检查点(Checkpoint)
状态管理有状态基于 DStream
数据延迟中等
吞吐量
编程范式支持有状态的流处理和批处理主要面向批处理,流处理是扩展
窗口操作支持时间、计数、会话等多种窗口操作支持滑动窗口、滚动窗口
状态后端支持多种状态后端,如内存、文件系统、RocksDB通常依赖于外部存储系统
容错与恢复支持端到端的精确一次语义支持但需要外部系统的配合
内存管理优化的内存管理,支持 Off-Heap 内存通常使用 JVM 堆内存
易用性易于进行状态管理和时间操作易于与 Spark 生态系统集成
社区和生态活跃的开源社区强大的社区和成熟的生态系统

批处理与流处理环境

在 Flink 中,主要有批处理 ExecutionEnvironment 和 流处理 StreamExecutionEnvironment 两种不同的执行环境,它们分别用于批处理和流处理操作。

ExecutionEnvironment

  • 批处理 ExecutionEnvironment 是 Flink 中用于执行批处理操作的环境。它提供了创建数据集(DataSet)API 的方法,允许用户对静态数据集进行操作。
  • 特性
    • 用于有界数据集的处理。
    • 操作通常在数据已经全部加载到内存中后执行。
    • 适合于传统的批处理作业。

StreamExecutionEnvironment

  • 流处理 StreamExecutionEnvironment 是 Flink 中用于创建流处理作业的环境。它提供了创建数据流(DataStream)API 的方法,允许用户对无界或有界数据流进行操作。
  • 特性
    • 用于无界数据流的处理,但同时也支持有界数据流。
    • 可以持续地从数据源接收数据,并进行实时处理。
    • 适合于实时分析和事件驱动的应用程序。

区别

  • 数据模型ExecutionEnvironment 针对批处理,操作在 DataSet 上;而 StreamExecutionEnvironment 针对流处理,操作在 DataStream 上。

  • 应用场景:批处理环境用于处理有限的数据集,通常在数据已经全部可用时;流处理环境用于处理连续的数据流,可以是无限的,通常需要实时处理。

  • API:两者提供的 API 不同,StreamExecutionEnvironment 提供了如时间窗口、watermarks 等流特定的操作。

  • 执行方式:批处理作业通常在所有数据加载完成后执行,而流处理作业则是持续不断地处理实时到达的数据。

如果你正在编写一个需要实时处理数据的应用程序,那么 StreamExecutionEnvironment 是更合适的选择。相反,如果你需要处理存储在文件系统或数据库中的静态数据集,那么 ExecutionEnvironment 将更加适合。

WordCount

下面的示例均采用 Java 进行演示,首先构建 Maven 项目,并引入依赖文件,参考如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.jsu</groupId>
    <artifactId>flink</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <flink.version>1.17.0</flink.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-java</artifactId>
            <version>${flink.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients</artifactId>
            <version>${flink.version}</version>
        </dependency>
    </dependencies>

</project>

有界词频统计

读取一个静态的文本文件 words.txt,进行词频统计,如下所示:

hello flink
hello bigdata
hello man
hello woman

因为读取的是一个静态的数据集,所以我们使用 ExecutionEnvironment 环境来进行处理。

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.operators.Order;
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.tuple.Tuple2;
import org.apache.flink.util.Collector;

public class WordCount {
    public static void main(String[] args) throws Exception {

        // 1.创建环境
        ExecutionEnvironment environment = ExecutionEnvironment.getExecutionEnvironment();

        // 2.读取数据
        DataSource<String> dataSource = environment.readTextFile("src/main/resources/words.txt");

        // 3.转换数据格式
        FlatMapOperator<String, Tuple2<String, Long>> flatMap = dataSource.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
            @Override
            public void flatMap(String line, Collector<Tuple2<String, Long>> collector) {
                String[] wordList = line.split(" ");
                for (String word : wordList) {
                    collector.collect(Tuple2.of(word, 1L));
                }
            }
        });

        // 4.分组统计个数并输出结果
        flatMap.groupBy(0).sum(1).print();
        
    }

}

输出结果如下:

无界词频统计

这里我创建了一台虚拟机,并通过 nc 服务创建了一个网络监听连接,用于传输数据,如下所示:

nc,即 Netcat,是一个用于网络工具,可以用于读写网络连接,使用 TCP 或 UDP 协议。执行 nc -l 7777 命令后,Netcat 将开始在本机的 7777 端口上监听传入的连接请求。如果有远程主机连接到这个端口,Netcat 将与之建立连接,并可以进行数据传输。

创建 Socket 流读取程序,进行词频统计。

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;
import org.apache.flink.util.Collector;

public class WordCountSocket {
    public static void main(String[] args) throws Exception {

        // 1.创建流式处理环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 2.从socket中读取数据
        DataStreamSource<String> streamSource = env.socketTextStream("hadoop201", 7777);

        // 3.转换数据格式
        SingleOutputStreamOperator<Tuple2<String, Long>> streamOperator = streamSource.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
            @Override
            public void flatMap(String line, Collector<Tuple2<String, Long>> collector) throws Exception {
                String[] split = line.split(" ");
                for (String s : split) {
                    collector.collect(Tuple2.of(s, 1L));
                }
            }
        });

        // 4.分组统计个数并输出结果
        streamOperator.keyBy(data -> data.f0).sum(1).print();

        // 5.启动环境
        env.execute();

    }
}

输出示例如下:

在这里插入图片描述

因为是流式处理的原因,所以形成了这种累加的效果,会依次对每一行数据发送过来的数据进行处理,等待处理完成后,再读取新的数据。

注意,启动程序前,需要先启动 nc 监听服务,否则程序会抛连接异常。

如果咱们没有虚拟机,那么也可以通过流式的方式读取上面的静态文本文件进行处理,效果是一样的,如下所示:

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;
import org.apache.flink.util.Collector;

public class WordCountStream {
    public static void main(String[] args) throws Exception {

        // 1.创建流式处理环境
        StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();

        // 2.读取静态文本数据
        DataStreamSource<String> dataStream = environment.readTextFile("src/main/resources/words.txt");

        // 3.转换数据格式
        SingleOutputStreamOperator<Tuple2<String, Long>> flatMap = dataStream.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
            @Override
            public void flatMap(String line, Collector<Tuple2<String, Long>> collector) {
                String[] wordList = line.split(" ");
                for (String word : wordList) {
                    collector.collect(Tuple2.of(word, 1L));
                }
            }
        });

        // 4.分组统计个数并输出
        flatMap.keyBy(data -> data.f0).sum(1).print();

        // 5.启动环境
        environment.execute();

    }

}

输出结果如下:

在这里插入图片描述

虽然和批处理读取的内容是一样的,但是输出结果大有不同,流式处理注重的是对每一条数据的处理,而批处理则注重批量数据的处理,相信通过这个案例,能让你对流式处理和批量处理有一个不错的理解。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

月亮给我抄代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值