Flink 大数据初体验(1.17环境搭建+hello world案例)

2,439 阅读5分钟

摘要:应该2022年就应该学习的技能,硬推到了2023年才开始学习,下面将开始记录一下我的学习历程,本文介绍Flink的环境搭建和hello world案例。

Flink简介

官网 nightlies.apache.org/flink/flink…

Flink是一个近年来大数据领域里的一颗新星,对于Flink的定义,官方定义如下: Flink一个框架和分布式的处理引擎,用于对无界和有界数据流进行状态计算。

  • 有界的数据流就是有限量的静态数据,比如数据库里现在存好的数据,它就是有界的。
  • 无界数据流就是有一个数据源给你不断的发送数据,比如一个传感器不断的向服务器发送状态信息,比如服务器的实时监控程序。

Flink特点

  • 支持高性能,高吞吐,低延迟的数据流处理
  • 事件驱动
  • 分层的API
  • 精确一次(Exactly-Once)的一致性保证。(数据不多不少刚好被执行一次)
  • 乱序数据的处理

Flink的三个角色

  • 客户端(Client):代码由客户端获取并做转换,之后提交给JobManger
  • JobManager就是Flink集群里的“管事人”,对作业进行中央调度管理:而它获取到要执行的作业后,会进一步处理转换,然后分发任务给众多的TaskManager。
  • TaskManager,就是真正“干活的人”,数据的处理操作都是它们来做的

部署模式

主要有以下三种: 会话模式(Session Mode)单作业模式(Per-Job Mode)应用模式(Application Mode)。 它们的区别主要在于:集群的生命周期以及资源的分配方式: 以及应用的 ain 方法到底在哪里执行一一客户端(Client)还是 JobManager

会话模式

会话模式其实最符合常规思维。我们需要先启动一个集群,保持一个会话,在这个会话中通过究户端提交作业。集群启动时所有资源就都已经确定,所以所有提交的作业会竞争集群中的资源。

单作业模式

会话模式因为资源共享会导致很多问题,所以为了更好地隔离资源,我们可以考虑为每个提交的作业启动一个集群,这就是所谓的单作业 (Per-Job) 模式。 作业完成后,集群就会关闭,所有资源也会释放。 这些特性使得单作业模式在生产环境运行更加稳定,所以是实际应用的首选模式

应用模式

前面提到的两种模式下,应用代码都是在客户端上执行,然后由客户端提交给JobManagcr的。但是这种方式客户端需要占用大量网络带宽,去下载依赖和把二进制数据发送给JobManagcr: 加上很多情况下我们提交作业用的是同一个客户端,就会加重客户端所在节点的资源消耗 所以解决办法就是,我们不要客户端了,直接把应用提交到JobManger上运行。而这也就代表着,我们需要为每一个提交的应用单独启动一个JobManager,也就是创建一个集群。这个JobManager只为执行这一个应用而存在,执行结束之后JobManager也就关闭了,这就是所谓的应用模式。

运行模式

当前只介绍 standalone模式,其他模式后面补充。

Standalone模式

独立模式是独立运行的,不依赖任何外部的资源管理平台;当然独立也是有代价的:如果资源不足,或者出现故,没有自动扩展或重分配资源的保证,必须手动处理。所以独立模式一般只用在开发测试或作业非常少的场景下。

会话模式部署

提前启动集群,并通过 Web 页面客户端提交任务(可以多个任务,但是集群资源固定)。

单作业模式部署

Flink 的 Standalone 集群并不支持单作业模式部署。因为单作业模式需要借助一些资源管 理平台。

应用模式部署

阅读docker相关文档

环境搭建

下面的案例基于Standalone模式的会话模式部署的,底层环境是dockernightlies.apache.org/flink/flink…

docker-complse.yml

version: "2.2"
services:
  jobmanager:
    image: flink:1.17.1-scala_2.12
    ports:
      - "8081:8081"
    command: jobmanager
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager        

  taskmanager:
    image: flink:1.17.1-scala_2.12
    depends_on:
      - jobmanager
    command: taskmanager
    scale: 1
    environment:
      - |
        FLINK_PROPERTIES=
        jobmanager.rpc.address: jobmanager
        taskmanager.numberOfTaskSlots: 2      

访问http://xxxx:8081,就能得到如下界面

image.png

hello world案例

先来一个统计文件单词数量的,采用读取文件的方式。

pom.xml

<properties>
    <flink.version>1.17.0</flink.version>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <log4j.version>2.20.0</log4j.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>
    <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-runtime-web</artifactId>
        <version>${flink.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j-impl</artifactId>
        <scope>compile</scope>
        <version>${log4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
        <scope>compile</scope>
        <version>${log4j.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <scope>compile</scope>
        <version>${log4j.version}</version>
    </dependency>
</dependencies>

log4j2.xml日志配置

<?xml version="1.0" encoding="UTF-8"?>
<configuration monitorInterval="5">
    <Properties>
        <property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
        <!-- LOG_LEVEL 配置你需要的日志输出级别       -->
        <property name="LOG_LEVEL" value="INFO" />
    </Properties>

    <appenders>
        <console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_PATTERN}"/>
            <ThresholdFilter level="${LOG_LEVEL}" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>
    </appenders>

    <loggers>
        <root level="${LOG_LEVEL}">
            <appender-ref ref="Console"/>
        </root>
    </loggers>

</configuration>

定义word.txt文件

image.png

主程序WordCountStreamDemo

public class WordCountStreamDemo {

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

        DataStreamSource<String> lineDs = env.readTextFile("input/word.txt");

        SingleOutputStreamOperator<Tuple2<String, Integer>> singleOutputStreamOperator = lineDs.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {


            @Override
            public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
                String[] words = value.split(" ");
                for (String word : words) {
                    out.collect(Tuple2.of(word, 1));
                }
            }
        });

        KeyedStream<Tuple2<String, Integer>, String> tuple2StringKeyedStream = singleOutputStreamOperator.keyBy(new KeySelector<Tuple2<String, Integer>, String>() {

            @Override
            public String getKey(Tuple2<String, Integer> value) throws Exception {
                return value.f0;
            }
        });

        SingleOutputStreamOperator<Tuple2<String, Integer>> sum1 = tuple2StringKeyedStream.sum(1);

        sum1.print();

        env.execute();

    }

}

本地执行结果如下

image.png

打包

maven clean package -Dmaven.test.skip=true

上传jar包运行

image.png

image.png

查看taskManager日志,因为我们没有无界数据流,所以任务执行完就停止了,上面的案例会报找不到word.txt,因为我们打包文件没有打进去,下面的文章会使用sock流来演示。

无界数据流hello world案例

基础准备

我们代码还是在同一个工程中,我们只需要新增一个类,在centos系统上开启一个nc -lk 7777socket数据监听端口。

WordCountUnboundStreamDemo

public class WordCountUnboundStreamDemo {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(new Configuration());

        DataStreamSource<String> lineDs = env.socketTextStream("192.168.137.100",7777);

        SingleOutputStreamOperator<Tuple2<String, Integer>> singleOutputStreamOperator = lineDs.flatMap(new FlatMapFunction<String, Tuple2<String, Integer>>() {


            @Override
            public void flatMap(String value, Collector<Tuple2<String, Integer>> out) throws Exception {
                String[] words = value.split(" ");
                for (String word : words) {
                    out.collect(Tuple2.of(word, 1));
                }
            }
        });

        KeyedStream<Tuple2<String, Integer>, String> tuple2StringKeyedStream = singleOutputStreamOperator.keyBy(new KeySelector<Tuple2<String, Integer>, String>() {

            @Override
            public String getKey(Tuple2<String, Integer> value) throws Exception {
                return value.f0;
            }
        });

        SingleOutputStreamOperator<Tuple2<String, Integer>> sum1 = tuple2StringKeyedStream.sum(1);

        sum1.print();

        env.execute();

    }

}

运行

上传任务

image.png

运行结果

image.png

我们输入单词来统计,并查看日志

image.png

image.png