摘要:应该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模式的会话模式部署的,底层环境是docker。nightlies.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,就能得到如下界面
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文件
主程序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();
}
}
本地执行结果如下
打包
maven clean package -Dmaven.test.skip=true
上传jar包运行
查看
taskManager日志,因为我们没有无界数据流,所以任务执行完就停止了,上面的案例会报找不到word.txt,因为我们打包文件没有打进去,下面的文章会使用sock流来演示。
无界数据流hello world案例
基础准备
我们代码还是在同一个工程中,我们只需要新增一个类,在
centos系统上开启一个nc -lk 7777的socket数据监听端口。
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();
}
}
运行
上传任务
运行结果
我们输入单词来统计,并查看日志