当前位置: 首页 > news >正文

Flink 快速入门

本文涉及到大量的底层原理知识,包括运行机制图解都非常详细,还有一些实战案例,所以导致本篇文章会比较长,内容比较多,由于内容太多,很多目录可能展示不出来,需要去细心的查看,非常适合深入了解学习flink的小伙伴们,如果你们喜欢这篇文章可以多多关注,大家一起学习,还可以在评论区留言谈论一下问题。

一、系统架构

Standalone会话模式为例

1、客户端:提交的任务的节点,提交任务的地方,JobManager:管理者,TaskManager:实际工作者

2、1.1脚本启动执行,在客户端提交作业的时候,执行脚本命令,bin/flink run的操作,执行之后就会启动一个客户端进程

3、1.2解析参数,在启动一个客户端进程之后,首先客户端会解析我们脚本中的参数,例如-c执行程序的全类名,-d 后台运行,-D指定flink的一些依赖,客户端会对这个参数进行解析

4、1.3封装提交参数,解析完脚本中的参数之后,会进行封装操作

5、1.4提交任务、取消或更新任务,在封装完参数之后,会通过内部的通信系统(RPC节点,因为要跨节点,客户端跟JobManager不一定在一台服务器上) 将封装好的命令参数传递给 JobManager的通信系统

6、分发器 2提交并启动应用,JobManager在接收到客户端传递过来的参数之后,通过分发器启动提交具体的应用给JobMaster,

7、JobMaster 3请求slots,JobManager可以理解为进程,JobMaster可以理解为线程,JobMaster是真正负责管理作业的,请求资源,JobMaster会根据应用的实际参数情况去资源管理器中请求资源,这个资源就是slots(槽),

8、资源管理器 4请求slots,现在是以Standalone会话为例,所以这个资源管理器是Flink自己的资源管理器,如果是yarn模式,那么这个资源管理器就是yarn的,请求slots,资源管理器会去TaskManager请求Task Slots资源,还是通过通信系统进行请求和通信,

9、5 提供slots,然后TaskManager通过通信系统将可使用的task slot给到JobMaster

11、6分发任务,当TaskManager告诉JobMaster那些task slot可以使用的时候,JobMaster会把任务分发给这些task slot

12、7状态更新、计算结果,分发完任务之后,返回给客户端应用的执行状态,是否开始执行了,或者是否有报错执行失败了, 如果是执行成功,将执行的结果返回给客户端

13、注意:现在是Standalone会话模式,一个计算可以有多个任务,也就是多个job,第二个job在提交之后还是走一样的流程,只不过在JobManager中,还是使用原来的分发器跟资源管理器,但是会生成一个新的JobMaster,一个job只能有一个JobMaster,例如job2启动了,走到JobManager中,还是一样的流程,但是会启动一个JobMaster2,这个JobMaster2跟第一个任务的JobMaster执行一样的操作

1)作业管理器(JobManager)

JobManager是一个Flink集群中任务管理和调度的核心,是控制应用执行的主进程。也就是说,每个应用都应该被唯一的JobManager所控制执行。

JobManger又包含3个不同的组件。

(1)JobMaster

JobMaster是JobManager中最核心的组件,负责处理单独的作业(Job)。所以JobMaster和具体的Job是一一对应的,多个Job可以同时运行在一个Flink集群中, 每个Job都有一个自己的JobMaster。需要注意在早期版本的Flink中,没有JobMaster的概念;而JobManager的概念范围较小,实际指的就是现在所说的JobMaster。

在作业提交时,JobMaster会先接收到要执行的应用。JobMaster会把JobGraph转换成一个物理层面的数据流图,这个图被叫作“执行图”(ExecutionGraph),它包含了所有可以并发执行的任务。JobMaster会向资源管理器(ResourceManager)发出请求,申请执行任务必要的资源。一旦它获取到了足够的资源,就会将执行图分发到真正运行它们的TaskManager上。

而在运行过程中,JobMaster会负责所有需要中央协调的操作,比如说检查点(checkpoints)的协调。

(2)资源管理器(ResourceManager)

ResourceManager主要负责资源的分配和管理,在Flink 集群中只有一个。所谓“资源”,主要是指TaskManager的任务槽(task slots)。任务槽就是Flink集群中的资源调配单元,包含了机器用来执行计算的一组CPU和内存资源。每一个任务(Task)都需要分配到一个slot上执行。

这里注意要把Flink内置的ResourceManager和其他资源管理平台(比如YARN)的ResourceManager区分开。

(3)分发器(Dispatcher)

Dispatcher主要负责提供一个REST接口,用来提交应用,并且负责为每一个新提交的作业启动一个新的JobMaster 组件。Dispatcher也会启动一个Web UI,用来方便地展示和监控作业执行的信息。Dispatcher在架构中并不是必需的,在不同的部署模式下可能会被忽略掉。

2)任务管理器(TaskManager)

TaskManager是Flink中的工作进程,数据流的具体计算就是它来做的。Flink集群中必须至少有一个TaskManager;每一个TaskManager都包含了一定数量的任务槽(task slots)。Slot是资源调度的最小单位,slot的数量限制了TaskManager能够并行处理的任务数量。

启动之后,TaskManager会向资源管理器注册它的slots;收到资源管理器的指令后,TaskManager就会将一个或者多个槽位提供给JobMaster调用,JobMaster就可以分配任务来执行了。

在执行过程中,TaskManager可以缓冲数据,还可以跟其他运行同一应用的TaskManager交换数据。

二、核心概念

并行度**(Parallelism)**
如何在本地的idea中运行flink程序,并且生成Flink Web UI

在本地使用idea运行flink程序 生成Web UI,想要看flink程序算子的并行度运行情况,需要在Web UI上来看,但是在本地运行flink程序的话,也是可以启动Web UI的,只需要更改一下创建环境就行,首先修改一些pom文件,加上flink-runtime-web

<?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.example</groupId><artifactId>flink_flink</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><!-- 使用flink1.17.0版本,注意1.17版本的java依赖和Scala依赖是分开的 --><flink.version>1.17.0</flink.version></properties><dependencies><dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java</artifactId> <!-- flink基本依赖 --><version>${flink.version}</version><!-- <scope>provided</scope> --></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-clients</artifactId> <!-- flink客户端 --><version>${flink.version}</version><!-- <scope>provided</scope> --></dependency><dependency><groupId>org.apache.flink</groupId><artifactId>flink-runtime-web</artifactId><version>${flink.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><artifactSet><excludes><exclude>com.google.code.findbugs:jsr305</exclude><exclude>org.slf4j:*</exclude><exclude>log4j:*</exclude></excludes></artifactSet><filters><filter><!-- Do not copy the signatures in the META-INF folder.Otherwise, this might cause SecurityExceptions when using the JAR. --><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters><transformers combine.children="append"><transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"></transformer></transformers></configuration></execution></executions></plugin></plugins></build>
</project>

修改创建执行环境,flink程序,该程序的功能是监听端口,读取端口中输入的内容,重点是修改了env,这个执行环境,关于这个代码的详细使用跟解释请看该文章2024年最新Flink教程,从基础到就业,大家一起学习–入门篇_flink 入门-CSDN博客

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("172.0.0.1", 9999);//使用lambda表达式来实现SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {Tuple2<String, Integer> Tuple2_of = Tuple2.of(word, 1);out.collect(Tuple2_of);}}).returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

启动程序之后,在浏览器上输入。localhost:8081,进入到Flink Web UI页面

在idea中如果没有指定并行度,默认根据自身本地的电脑的线程数来的,source并行度为1,是因为我们只设置了一个数据源就是socket,所以并行度为1

1)并行子任务和并行度

当要处理的数据量非常大时,我们可以把一个算子操作,“复制”多份到多个节点,数据来了之后就可以到其中任意一个执行。这样一来,一个算子任务就被拆分成了多个并行的“子任务”(subtasks),再将它们分发到不同节点,就真正实现了并行计算。(例如一个sum算子,之前在做wrodcount程序的时候使用到了sum算子,但是如果数据量特别大的话,一个节点执行比较费时费力,那么就把谁分发到其他节点,其他节点一起来执行sum操作,就会快很多,这就是并行计算)

在Flink执行过程中,每一个算子(operator)可以包含一个或多个子任务(operator subtask),这些子任务在不同的线程、不同的物理机或不同的容器中完全独立地执行。

这个是一个普通的任务流程图,数据进来之后经过了Source算子,然后给map算子,再经过一些其他的算子处理,最后给sink算子进行一些输出,但是这个流程的话,如果遇到大批量的数据进来,效率就会很低,执行起来就会很慢,但是如果有多个Source算子,多个map算子,其他算子也有多个,多个算子一起处理同一批数据,那效率就提高了,多个人工作肯定会强于一个人工作

现在上图的并行度就是2,两个人同时工作,当数据进来之后,由两个Source算子进行接收数据,然后传递给两个map算子共同处理,map算子之后有一段交叉箭头,这是因为有keyBy操作,keyBy不是算子,是重分区,相同的分到一组,所以交叉箭头表示会交换数据,将相同的key分到一个算子中进行其他处理,然后处理完的数据统一交给sink,这个时候sink就一个,并行度为1,但是由于前面的数据处理过程是并行度2,两个算子并行执行,所以会快很多

同一个算子有几个子任务在工作就说明该算子的并行度为几,

一个特定算子的子任务(subtask)的个数被称之为其并行度(parallelism)。这样,包含并行子任务的数据流,就是并行数据流,它需要多个分区(stream partition)来分配并行任务。一般情况下,一个流程序的并行度,可以认为就是其所有算子中最大的并行度。一个程序中,不同的算子可能具有不同的并行度。

例如:如上图所示,当前数据流中有source、map、window、sink四个算子,其中sink算子的并行度为1,其他算子的并行度都为2。所以这段流处理程序的并行度就是2。

2)并行度的设置

在Flink中,可以用不同的方法来设置并行度,它们的有效范围和优先级别也是不同的。

(1)代码中设置

我们在代码中,可以很简单地在算子后跟着调用setParallelism()方法,来设置当前算子的并行度:

stream.map(word -> Tuple2.of(word, 1L)).setParallelism(2);

这种方式设置的并行度,只针对当前算子有效。

另外,我们也可以直接调用执行环境的setParallelism()方法,全局设定并行度:

env.setParallelism(2);

这样代码中所有算子,默认的并行度就都为2了。我们一般不会在程序中设置全局并行度,因为如果在程序中对全局并行度进行硬编码,会导致无法动态扩容。

这里要注意的是,由于keyBy不是算子,所以无法对keyBy设置并行度。

根据算子单独设置

现在把上面的flink程序中的flatmap算子的并行度修改成2

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {Tuple2<String, Integer> Tuple2_of = Tuple2.of(word, 1);out.collect(Tuple2_of);}}).setParallelism(2) // 设置flaotmat并行度为2.returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

然后启动flink程序,观看一下并行度的情况

可以看到,Flat Map的并行度已经变成2了,但是其他算子的并行度并没有改变,这个设置只针对Flat Map有效

在代码中进行全局设置
package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为3env.setParallelism(3);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {Tuple2<String, Integer> Tuple2_of = Tuple2.of(word, 1);out.collect(Tuple2_of);}}).returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

启动Flink程序,观看并行度的运行情况,source不用管,因为我们只有一个数据源,Flat Map跟sink的并行度都变成了3

全局设置跟算子单独设置的优先级

设置全局并行度为3,单独对Flat Map算子的并行度设置为2

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为3env.setParallelism(3);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<Tuple2<String, Integer>> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {Tuple2<String, Integer> Tuple2_of = Tuple2.of(word, 1);out.collect(Tuple2_of);}}).setParallelism(2) // Flat Map的并行度设置为2.returns(Types.TUPLE(Types.STRING,Types.INT)).keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

运行Flink程序,查看并行度情况

可以看到,Flat Map的并行度变成了2,Sink的并行度还是3,但是全局设置了并行度为3的,这说明算子的并行度的优先级比全局并行度的优先级高

(2)提交应用时设置

在使用flink run命令提交应用时,可以增加-p参数来指定当前应用程序执行的并行度,它的作用类似于执行环境的全局设置:

bin/flink run –p 2 –c com.atguigu.wc.SocketStreamWordCount

./FlinkTutorial-1.0-SNAPSHOT.jar

如果我们直接在Web UI上提交作业,也可以在对应输入框中直接添加并行度

(3)配置文件中设置

我们还可以直接在集群的配置文件flink-conf.yaml中直接更改默认并行度:

parallelism.default: 2

这个设置对于整个集群上提交的所有作业有效,初始值为1。无论在代码中设置、还是提交时的-p参数,都不是必须的;所以在没有指定并行度的时候,就会采用配置文件中的集群默认并行度。在开发环境中,没有配置文件,默认并行度就是当前机器的CPU核心数。

3)并行度的优先级

代码中算子设置并行度 > 代码中全局并行度 > 提交时UI页面或者执行命令中指定并行度 > 默认并行度

如果在算子中设置了不同的并行度,例如FlatMap设置的并行度为5,Map设置的并行度为3,那么整个Flink的并行度是5,Flink会取最大的并行度

算子链(Operator Chain)

在查看Web UI的时候可以看到这样的场景,为什么流程节点会这样分配呢,这个就涉及到了算子链

1)算子间的数据传输

一个数据流在算子之间传输数据的形式可以是一对一(one-to-one)的直通(forwarding)模式,也可以是打乱的重分区(redistributing)模式,具体是哪一种形式,取决于算子的种类。

在传输数据过程中只有 forward 是一对一,REBALANCE以及HASH这些都是重分区

(1)一对一(One-to-one,forwarding)

这种模式下,数据流维护着分区以及元素的顺序。比如图中的source和map算子,source算子读取数据之后,可以直接发送给map算子做处理,它们之间不需要重新分区,也不需要调整数据的顺序。这就意味着map 算子的子任务,看到的元素个数和顺序跟source 算子的子任务产生的完全一样,保证着“一对一”的关系。map、filter、flatMap等算子都是这种one-to-one的对应关系。这种关系类似于Spark中的窄依赖。(一对一就是数据源中进来的什么数据,发送给下一个节点的就是什么数据,不需要进行重新分区的动作,也不需要改变数据的顺序,原模原样的给到下一个节点,所以上面的第一个节点中socket数据源跟Flat map在一个节点里面,就是因为这两个是一对一的关系,socket直接原模原样的把数据给到Flat Map,前提是这两个一对一的并行度也都是一样的。)

(2)重分区(Redistributing)

在这种模式下,数据流的分区会发生改变。比如图中的map和后面的keyBy/window算子之间,以及keyBy/window算子和Sink算子之间,都是这样的关系。

每一个算子的子任务,会根据数据传输的策略,把数据发送到不同的下游目标任务。这些传输方式都会引起重分区的过程,这一过程类似于Spark中的shuffle。

2)合并算子链

在Flink中,并行度相同的一对一(one to one)算子操作,可以直接链接在一起形成一个“大”的任务(task),这样原来的算子就成为了真正任务里的一部分,如下图所示。每个task会被一个线程执行。这样的技术被称为“算子链”(Operator Chain)。

将数据流逻辑视图跟数据流并行视图分开看

先看数据流逻辑视图:这个图里面的并行度都是1,那么souce跟map都是一对一的关系,所以两个子任务合并成一个子任务,但是到下一个子任务的时候,因为要经过一个keyBy还有Window这些算子,这些算子会进行重分区,所以不能跟前面的子任务连接在一起,只能自己形成单独的子任务,然后就到了sink

数据流并行视图:这次前面的算子并行度是2,并行度为1的source跟map还是一对一的关系,所以自然两个子任务分配成一个子任务,病毒性为2的source跟map也是一对一的关系,也分配成一个子任务,本来四个子任务现在变成了两个,然后传递到下一个子任务的时候,因为遇到了keyBy这种需要重分区的算子,keyBy也不能是算子,然后就只能自己单独是一个子任务

上图中Source和map之间满足了算子链的要求,所以可以直接合并在一起,形成了一个任务;因为并行度为2,所以合并后的任务也有两个并行子任务。这样,这个数据流图所表示的作业最终会有5个任务,由5个线程并行执行。

将算子链接成task是非常有效的优化:可以减少线程之间的切换和基于缓存区的数据交换,在减少时延的同时提升吞吐量。

Flink默认会按照算子链的原则进行链接合并,如果我们想要禁止合并或者自行定义,也可以在代码中对算子做一些特定的设置:

// 禁用算子链,在指定需要解除算子链的算子后面加上该参数
.map(word -> Tuple2.of(word, 1L)).disableChaining();// 从当前算子开始新链
.map(word -> Tuple2.of(word, 1L)).startNewChain();//全局禁用算子链,使用这个,整个代码中的算子都不会链接起来
env.disableOperatorChaining();
(1)实战验证

把之前的流处理代码修改一下,修改一下FlatMap然后加入Map算子,该程序具体描述请看这篇文章2024年最新Flink教程,从基础到就业,大家一起学习–入门篇_flink从入门,进阶,实战-CSDN博客

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket_copy* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket_copy {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为1env.setParallelism(1);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现/*** 这里修改一下FlatMap 本来是直接在FlatMap中统计单词并且加上个数* 为了演示,这里不在FlatMap中将单词加入元祖并给一个1* 后面让map算子去处理,map算子在接收到FlatMap输出的数据之后(也就是每个单词)* 将单词添加到元祖中,并在第二个参数中加上1* returns 方法用于明确指定流中算子输出的结果类型* FlatMap要指定为String类型* map要指定为Types.TUPLE(Types.STRING,Types.INT)*/SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<String> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {out.collect(word);}}).returns(Types.STRING) // 这里注意要修改一下类型,不再是Types.TUPLE(Types.STRING,Types.INT),因为现在FlatMap输出的结果是String类型的.map(word->Tuple2.of(word,1))// map接收到每个FlatMap输出的单词后,将单词加入到元祖中,并且给一个值1.returns(Types.TUPLE(Types.STRING,Types.INT))//这里强调的是map的输出类型.keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

在本地启动程序,然后打开Flink Web UI localhost:8081

分析上图,第一个子任务中,数据源Socket跟FlatMap算子还有Map算子这三个子任务合并成一个子任务了,这是因为这三个的并行度都是1,并且数据不需要重分区

第二个子任务重,Keyed Aggregation就是sum,然后还有输出print,这两个子任务合并成一个子任务了

为什么中间使用一个箭头 HASH分割了呢,这是因为中间就是KeyBy,遇到keyBy之后就要进行重分区,所以划分成两个子任务了

(2)全局禁用算子链

禁用算子链可以在某一个算子后面单独设置,那么这个算子就会形成一个单独的子任务,不会跟其他算子合并成一个子任务,但是也是可以设置全局禁用算子链,就是每个算子就是一个子任务,不会链起来了

env.disableOperatorChaining();

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket_copy* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket_copy {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//全局禁用算子链env.disableOperatorChaining();//设置全局并行度为1env.setParallelism(1);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现/*** 这里修改一下FlatMap 本来是直接在FlatMap中统计单词并且加上个数* 为了演示,这里不在FlatMap中将单词加入元祖并给一个1* 后面让map算子去处理,map算子在接收到FlatMap输出的数据之后(也就是每个单词)* 将单词添加到元祖中,并在第二个参数中加上1* returns 方法用于明确指定流中算子输出的结果类型* FlatMap要指定为String类型* map要指定为Types.TUPLE(Types.STRING,Types.INT)*/SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<String> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {out.collect(word);}}).returns(Types.STRING) // 这里注意要修改一下类型,不再是Types.TUPLE(Types.STRING,Types.INT),因为现在FlatMap输出的结果是String类型的.map(word->Tuple2.of(word,1))// map接收到每个FlatMap输出的单词后,将单词加入到元祖中,并且给一个值1.returns(Types.TUPLE(Types.STRING,Types.INT))//这里强调的是map的输出类型.keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

本地idea启动Flink程序,打开Flink Web UI

这样可以看到,每一个算子都是一个子任务,哪怕是一对一都不会相互合并

注意看每个子任务中间的箭头链接线,FORWARD 就表示前后两个子任务是一对一的,HASH就是表示要重分区,HASH那边是进行了一个keyBy的操作,因为我们现在设置的是全局禁用重分区,所以哪怕两个子任务是FORWARD 一对一并行度也是1,也不会相同合并成一个子任务

(3)禁用算子链

禁用算子链就是在某一个算子后,设置禁用,这样该算子就会单独形成一个子任务,哪怕是一对一也不会跟前后的子任务合并到一起的

.map(word -> Tuple2.of(word, 1L)).disableChaining();

在FlatMap算子后面加上禁用算子链.disableChaining();

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket_copy* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket_copy {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为1env.setParallelism(1);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现/*** 这里修改一下FlatMap 本来是直接在FlatMap中统计单词并且加上个数* 为了演示,这里不在FlatMap中将单词加入元祖并给一个1* 后面让map算子去处理,map算子在接收到FlatMap输出的数据之后(也就是每个单词)* 将单词添加到元祖中,并在第二个参数中加上1* returns 方法用于明确指定流中算子输出的结果类型* FlatMap要指定为String类型* map要指定为Types.TUPLE(Types.STRING,Types.INT)*/SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<String> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {out.collect(word);}}).disableChaining() // 禁用算子链.returns(Types.STRING) // 这里注意要修改一下类型,不再是Types.TUPLE(Types.STRING,Types.INT),因为现在FlatMap输出的结果是String类型的.map(word->Tuple2.of(word,1))// map接收到每个FlatMap输出的单词后,将单词加入到元祖中,并且给一个值1.returns(Types.TUPLE(Types.STRING,Types.INT))//这里强调的是map的输出类型.keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

本地idea启动Flink程序,打开Flink Web UI

可以看到本来FlatMap应该跟数据源Socket还有Map合并成一个子任务的,但是因为在FlatMap算子上加入了禁用算子链,所有FlatMap前后的两个算子哪怕跟FlatMap并行度相同,还是一对一的,也不能合并成一个子任务,但是sum跟print却合并成了子任务,因为禁用算子链只会影响自己本身

(4)从当前算子开始新链

startNewChain();

如果不设置禁用算子和开启算子的话,默认启动后流程节点是这样的

现在在FlatMap后面加上开启新算子链,这就表示FlatMap会跟前面的子任务分开,会跟后面的一对一(相同并行度)重新分配成一个子任务

package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket_copy* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket_copy {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为1env.setParallelism(1);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现/*** 这里修改一下FlatMap 本来是直接在FlatMap中统计单词并且加上个数* 为了演示,这里不在FlatMap中将单词加入元祖并给一个1* 后面让map算子去处理,map算子在接收到FlatMap输出的数据之后(也就是每个单词)* 将单词添加到元祖中,并在第二个参数中加上1* returns 方法用于明确指定流中算子输出的结果类型* FlatMap要指定为String类型* map要指定为Types.TUPLE(Types.STRING,Types.INT)*/SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<String> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {out.collect(word);}}).startNewChain() // FlatMap开始新算子链.returns(Types.STRING) // 这里注意要修改一下类型,不再是Types.TUPLE(Types.STRING,Types.INT),因为现在FlatMap输出的结果是String类型的.map(word->Tuple2.of(word,1))// map接收到每个FlatMap输出的单词后,将单词加入到元祖中,并且给一个值1.returns(Types.TUPLE(Types.STRING,Types.INT))//这里强调的是map的输出类型.keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

在本地idea启动flink程序,打开Flink Web UI

可以看到Flat Map跟前面的Socket分开了,但是跟后面的一对一的算子会正常连接成一个子任务

(5)关于算子链以及使用场景

全局禁用算子链 env.disableOperatorChaining();

某个算子禁用算子链,该算子不参与链化 A.disableChaining(); 算子A不会与 前面 和 后面的算子串在一起

从某个算子开启新链条 A.startNewChain(); 算子A不与 前面串在一起 从算子A开始正常链化

使用算子链的场景一:

例如正常情况下 FlatMap跟Map会放到一个子任务中,也就是放到一个task slot中,如果并行度是2的话,那么并行度为1的FlatMap和Map放到一个槽中,并行度为2的FlatMap和Map放到一个槽中,如果遇到FlatMap跟Map这个算子中需要处理的逻辑特别的重,那么把这两个算子放到一个子任务也就是一个task slot中压力会特别的大,这个时候就需要分开算子链了

使用算子链的场景二:

例如正常情况下,FlatMap跟Map放到了一个子任务中,如果其中FlatMap出现了报错,那么就是整个FlatMap跟Map这个子任务出现报错,如果两个分开的话FlatMap报错那么FlatMap的子任务就会出现异常,这样便于观察和定位代码哪里出现了问题

任务槽(Task Slots)
1)任务槽(Task Slots)

Flink中每一个TaskManager都是一个JVM进程,它可以启动多个独立的线程,来并行执行多个子任务(subtask)。

很显然,TaskManager的计算资源是有限的,并行的任务越多,每个线程的资源就会越少。那一个TaskManager到底能并行处理多少个任务呢?为了控制并发量,我们需要在TaskManager上对每个任务运行所占用的资源做出明确的划分,这就是所谓的任务槽(task slots)。

每个任务槽(task slot)其实表示了TaskManager拥有计算资源的一个固定大小的子集。这些资源就是用来独立执行一个子任务的。

假如一个TaskManager有三个slot,那么它会将管理的内存平均分成三份,每个slot独自占据一份。这样一来,我们在slot上执行一个子任务时,相当于划定了一块内存“专款专用”,就不需要跟来自其他作业的任务去竞争内存资源了。

所以现在我们只要2个TaskManager,就可以并行处理分配好的5个任务了。

2)任务槽数量的设置

在Flink的/opt/module/flink-1.17.0/conf/flink-conf.yaml配置文件中,可以设置TaskManager的slot数量,默认是1个slot。

taskmanager.numberOfTaskSlots: 8

需要注意的是,slot目前仅仅用来隔离内存,不会涉及CPU的隔离(CPU是共用的)。在具体应用时,可以将slot数量配置为机器的CPU核心数,尽量避免不同任务之间对CPU的竞争。这也是开发环境默认并行度设为机器CPU数量的原因。

3)任务对任务槽的共享

默认情况下,Flink是允许子任务共享slot的。如果我们保持sink任务并行度为1不变,而作业提交时设置全局并行度为6,那么前两个任务节点就会各自有6个并行子任务,整个流处理程序则有13个子任务。如上图所示,只要属于同一个作业,那么对于不同任务节点(算子)的并行子任务,就可以放到同一个slot上执行。所以对于第一个任务节点source→map,它的6个并行子任务必须分到不同的slot上,而第二个任务节点keyBy/window/apply的并行子任务却可以和第一个任务节点共享slot。

当我们将资源密集型和非密集型的任务同时放到一个slot中,它们就可以自行分配对资源占用的比例,从而保证最重的活平均分配给所有的TaskManager。

slot共享另一个好处就是允许我们保存完整的作业管道。这样一来,即使某个TaskManager出现故障宕机,其他节点也可以完全不受影响,作业的任务可以继续执行。

当然,Flink默认是允许slot共享的,如果希望某个算子对应的任务完全独占一个slot,或者只有某一部分算子共享slot,我们也可以通过设置“slot共享组”手动指定:

.map(word -> Tuple2.of(word, 1L)).slotSharingGroup("1");

这样,只有属于同一个slot共享组的子任务,才会开启slot共享;不同组之间的任务是完全隔离的,必须分配到不同的slot上。在这种场景下,总共需要的slot数量,就是各个slot共享组最大并行度的总和。

代码案例
package wordcount;import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
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;/*** @ClassName flink_wc_socket_copy* @Description TODO* @Author 长风清留扬* @Date 2024/8/8 22:47* @Version 1.0*/
public class flink_wc_socket_copy {public static void main(String[] args) throws Exception {//创建执行环境//StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//常见执行环境,用于设置和初始化一个流处理环境(StreamExecutionEnvironment),特别是以本地模式(local mode)启动,并启用了一个Web UI以便监控和管理作业的执行。StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(new Configuration());//设置全局并行度为1env.setParallelism(1);//读取数据,从socket中读取数据DataStreamSource<String> socket_DS = env.socketTextStream("127.0.0.1", 9999);//使用lambda表达式来实现/*** 这里修改一下FlatMap 本来是直接在FlatMap中统计单词并且加上个数* 为了演示,这里不在FlatMap中将单词加入元祖并给一个1* 后面让map算子去处理,map算子在接收到FlatMap输出的数据之后(也就是每个单词)* 将单词添加到元祖中,并在第二个参数中加上1* returns 方法用于明确指定流中算子输出的结果类型* FlatMap要指定为String类型* map要指定为Types.TUPLE(Types.STRING,Types.INT)*/SingleOutputStreamOperator<Tuple2<String, Integer>> sum = socket_DS.flatMap((String value, Collector<String> out) -> {//拆分String[] words = value.split(" ");for (String word : words) {out.collect(word);}}).slotSharingGroup("test_slot") // 手动指定slot共享组,在flatMap后面加上这个代码,FlatMap就会进入到名称为 test_slot的共享组中.returns(Types.STRING) // 这里注意要修改一下类型,不再是Types.TUPLE(Types.STRING,Types.INT),因为现在FlatMap输出的结果是String类型的.map(word->Tuple2.of(word,1))// map接收到每个FlatMap输出的单词后,将单词加入到元祖中,并且给一个值1.returns(Types.TUPLE(Types.STRING,Types.INT))//这里强调的是map的输出类型.keyBy(value -> value.f0).sum(1);//输出结果sum.print();//执行流处理env.execute();}
}

这个代码中,在FlatMap算子后面加上了一个代码

.slotSharingGroup("test_slot")

表示给FlatMap手动开启一个共享组,正常来说如果没有这行代码的话,scoket,FlatMap,map,sum,print 这些算子都是应该在一个task slot里面,默认名称是default,但是现在单独给FlatMap开启了一个共享组,那么FlatMap就会进入到一个新的task slot中,这个task slot名称为test_slot

任务槽和并行度的关系

任务槽和并行度都跟程序的并行执行有关,但两者是完全不同的概念。简单来说任务槽是静态的概念,是指TaskManager具有的并发执行能力,可以通过参数taskmanager.numberOfTaskSlots进行配置;而并行度是动态概念,也就是TaskManager运行程序时实际使用的并发能力,可以通过参数parallelism.default进行配置。

个人理解: 例如一个酒店有三个单人床房间,那么这三个房间就是slot(任务槽),是一个静态的,因为只有三个房间不会发生变化,最多三个房间,那么这个时候来了两个人入住,占用了两个房间,这两个人就是并行度为2,同时两个在运行,然后又来了一个人入住,这个时候并行度为3,这三个人把三个房间已经占满了,三个同时在运行,所以并行度是动态的是会发生变化的,但是这个时候又来了一个人,但是现在三个房间已经满了,不能入住了,这个情况下就会报错,flink的机制是不会让这个人等着的,来了任务之后没有slot可以使用了,那么就会出现报错,不会说让这个没有slot使用的任务一直在等待着

**举例说明:**假设一共有3个TaskManager,每一个TaskManager中的slot数量设置为3个,那么一共有9个task slot,表示集群最多能并行执行9个同一算子的子任务。

而我们定义word count程序的处理操作是四个转换算子:

source→ flatmap→ reduce→ sink

当所有算子并行度相同时,容易看出source和flatmap可以合并算子链,于是最终有三个任务节点。

任务槽与并行度的关系1

可以看到现在启动了一个Flink程序,程序中有Source,Flatmap,reduce,sink,由于并行度为1,而且任务槽为默认的default,那么这些算子就会共享一个任务槽,哪怕我还有八个任务槽是空闲状态,他们也会只占用一个

任务槽与并行度的关系2

上图可以看到,现在并行度是2,那么每个算子都会有两个子任务在同时运行,Flink程序相同的算子是不能在一个Slot中的,所以就会分布在不同的slot中,可以把九个slot比做是九个房间,source->flatmap->reduce->sink他们是一个人,住在一个房间里面,但是现在并行度是2,还有一个跟他们相同的人也需要住一个房间,所以就占用了两个slot

任务槽与并行度的关系3

以上图来看,现在并行度为9,那么相同算子会有九个任务在并行执行,九个人就需要九个房间

任务槽与并行度的关系4

以上图来看,现在设置全局并行度为9,那么现在每个算子都会有九个在这个九个任务槽中执行,但是单独给输出的sink设置并行度为1,那么sink只会出现在一个任务槽中

通过这个例子也可以明确地看到,整个流处理程序的并行度,就应该是所有算子并行度中最大的那个,这代表了运行程序需要的slot数量。

案例演示1

使用Standalone 会话模式部署

详细的Standalone 会话模式说明可以看这篇文章

2024年最新Flink教程,从基础到就业,大家一起学习–Flink集群部署-CSDN博客

2024年最新Flink教程,从基础到就业,大家一起学习–flink部署和集群部署(从本地测试到公司生产环境如何部署项目源码)_码界探索 flink csdn-CSDN博客

(1)配置slot数量

首先修改三台TaskManager的slot数量,在hadoop102,hadoop103,hadoop104这三台服务器上分别有一台TaskManager,所以一共是三台,现在修改配置文件,将三台TaskManager的slot数量都配置成3,那么每一台TaskManager有三个slot,一共就是9个slot

hadoop102,hadoop103,hadoop104这三台虚拟机上都有TaskManager,所以三台虚拟机都应该配置一下,配置slot的配置大约在91行的附近

//进入到Flink目录下
cd /opt/module/flink-1.17.0
//修改配置文件
vim conf/flink-conf.yaml
//将默认的slot数量从1改成3,那么这台TaskManager种就有三个slot
taskmanager.numberOfTaskSlots: 3

(2)在hadoop102节点服务器上执行start-cluster.sh启动Flink集群:
(3)访问本地Flink Web UI

浏览器输入网址:hadoop102:8081

可以看到,现在一共有3台Task Managers启动了,每台TaskManager有3个slot,总共就是9个slot,现在空闲的有9个slot

Task Managers:3台TaskManager运行中

Total Task Slots:3台TaskManager一共有9个slot

Available Task Slots: 共有9个Slots空闲中

(4)提交Flink任务

将原来的wordcount代码运行,并且指定全局并行度为4,看看会占用几个slot

bin/flink run -m hadoop102:8081 -p 4 -c wordcount.flink_wc_socket flink_flink-1.0-SNAPSHOT.jar
  • bin/flink:这是Flink安装目录下的bin子目录中的flink脚本,用于执行Flink的各种命令。
  • run:这是flink脚本的一个子命令,用于提交作业到Flink集群执行。
  • -m hadoop102:8081:这个参数指定了Flink集群的Master节点(JobManager)的地址和端口。在这个例子中,Master节点位于hadoop102这台机器上,并且监听在8081端口上。这是Flink集群管理界面(Web UI)和作业提交的默认端口。
  • -p 就是通过命令行指定Flink程序的全局并行度,这个给一个4 表示现在这个Flink程序的并行度是4
  • -c wordcount.flink_wc_socket:这个参数指定了作业的主类(Main Class)的全限定名。在这个例子中,wordcount.flink_wc_socket是包含main方法的类的全限定名,这个类是作业的入口点。Flink会加载这个类,并执行其中的main方法来启动作业。
  • ./flink_flink-1.0-SNAPSHOT.jar:这是要提交的Flink作业的JAR包路径。在这个例子中,JAR包名为flink_flink-1.0-SNAPSHOT.jar,位于当前目录下(由./表示)。这个JAR包包含了作业的所有依赖和编译后的类文件,是Flink作业执行的必要组件。

(5)再次查看Flink Web UI

浏览器输入网址:hadoop102:8081

查看Flink Web UI,看看slot的占用情况

Task Managers:3台TaskManager运行中

Total Task Slots:3台TaskManager一共有9个slot

Available Task Slots: 共有5个Slots空闲中

现在可以看到一共九个slot现在占用了4个还剩下5个,这是因为我们代码中的算子都是默认在一个slot中的,但是因为在启动Flink程序的之后,指定全局并行度为4,那么就会占用4个slot并行执行,一个Job中相同的算子不能在一个slot中

(6)看一下job任务节点图

FlatMap跟sum还有print的并行度都是4,Keyed Aggregation就是sum,数据源socket的并行度是1,这个是因为我们只监听了一个端口,只有一个数据来源,所以是1,那么实际的图表应该是这样的

案例演示2

如果启动Flink程序的并行度大于slot数量会出现什么情况

(1)关闭案例1的Flink程序

关闭案例1的Flink程序,保证现在还有9个slot处在空闲状态

(2)使用脚本启动Flink程序

现在还是通过脚本启动Flink程序,但是指定全局并行度为10,让并行度大于总共的slot数量

bin/flink run -m hadoop102:8081 -p 10 -c wordcount.flink_wc_socket flink_flink-1.0-SNAPSHOT.jar
(3)查看脚本执行情况

出现了报错,报错信息:

Caused by: org.apache.flink.runtime.jobmanager.scheduler.NoResourceAvailableException: Could not acquire the minimum required resources.

表示无法获得所需的最低资源,最低资源就是slot,flink不会让没有slot可用的进程进入等待状态,要么都有slot使用,要么就都别用

案例演示3

现在使用yarn模式来启动flink程序

如果是yarn模式,会动态申请TaskManager,申请的TaskManager数量 = job并行度 / 每个TaskManager的slot数,向上去整,比如Session会话模式,一开始启动后 是 0 个Task Manager 0 个 slot,提交一个Job,并行度为10,那么 10/3向上取整=4,yarn就要申请4个Task Manager

但是yarn怎么知道每个TaskManager指定的slot数量呢,其实yarn也是会读取配置文件,刚才我们配置每个TaskManager的slot数量为3,yarn也会遵循这个规则。

(1)启动一个yarn会话

-d表示后台启动

(2)查看yarn会话并且查看flink集群

浏览器输入:hadoop102:8088

现在可以看到TaskManager和slot数量都是0,因为没有任务, yarn也没有申请资源

(3)脚本启动Flink程序
bin/flink run -p 10 -c wordcount.flink_wc_socket flink_flink-1.0-SNAPSHOT.jar

这里不需要-m去指定了,会自动识别到yarn的

(4)查看Flink Web UI

可以看到现在Task Manager为4,yarn申请了4个Task Manager,因为我们在配置文件中配置了,每个Task Manager最多有3个slot,但是现在这个job任务需要10个slot,显然3个Task Manager已经不能满足要求了,Yarn就又去申请了一个Task Manager,会在某一个节点再申请一个Task Manager,那么现在就是4台Task Manager了,然后每台Task Manager有3个slot,那么一共就是12个slot,10个并行度用了10个还剩下2个slot空闲

总结

1、slot特点: 1)均分隔离内存,不隔离CPU 2)可以共享: 同一个job中,不同算子的子任务 才可以共享同一个slot,同时在运行的前提是,属于同一个slot共享组,默认都是 default 2、slot数量与并行度的关系 1)slot是一种静态的概念,表示最大的并发上限 并行度是一种动态的概念,表示实际运行占用了几个 2)要求:slot数量 >= job并行度(算子最大并行度)Job才能运行undefined 注意:如果是yarn模式,会动态申请TaskManager 申请的TaskManager数量 = job并行度 / 每个TaskManager的slot数,向上去整 比如Session会话模式,一开始启动后 是 0 个Task Manager 0 个 slot 提交一个Job,并行度为10,那么 10/3向上取整=4,yarn就要申请4个Task Manager

作业提交流程
Standalone会话模式作业提交流程

1.3-1.4就是优化算子链的过程,把能合并到一起的算子的子任务合并到一起,作业的执行流程图也会通过通信系统以及分发器给到JobMaster, JobMaster获取到流程图之后会进一步的转化,转化成执行图,最终执行起来之后就是到了6.2,就会有一个物理流图的生成,这个就是我们在Flink Web UI上看到的

逻辑流图/作业图/执行图/物理流图

我们已经彻底了解了由代码生成任务的过程,现在来做个梳理总结。

逻辑流图(StreamGraph)→ 作业图(JobGraph)→ 执行图(ExecutionGraph)→ 物理图(Physical Graph)。

逻辑流图**(StreamGraph)**

这是根据用户通过 DataStream API编写的代码生成的最初的DAG图,用来表示程序的拓扑结构。这一步一般在客户端完成。

逻辑流图就是客户端解析我们提交上去的代码,其实就是讲我们代码里面的算子一个个摆出来,形成一个流式的图,一个个算子单独的摆出来,然后会把每个算子的并行度标出来,上图可以看到,每一个算子都摆出来了,每一个黄色的圈就是一个算组,并且通过箭头将算子之间的关系也表现出来,算子下面有算子的并行度

作业图**(JobGraph)**

StreamGraph经过优化后生成的就是作业图(JobGraph),这是提交给 JobManager 的数据结构,确定了当前作业中所有任务的划分。主要的优化为:将多个符合条件的节点链接在一起合并成一个任务节点,形成算子链,这样可以减少数据交换的消耗。JobGraph一般也是在客户端生成的,在作业提交时传递给JobMaster。

我们提交作业之后,打开Flink自带的Web UI,点击作业就能看到对应的作业图。

作业图就是对逻辑流图进行优化,根据一对一或者重分区来进行优化,可以看到KeyedAggregation跟sink之间是Forward关系就是一对一关系,并且并行度也是一样的,所以就合并到一起了,成一个一个子任务,这个就是作业图

执行图(最重要的图)(ExecutionGraph)

JobMaster收到JobGraph后,会根据它来生成执行图(ExecutionGraph)。ExecutionGraph是JobGraph的并行化版本,是调度层最核心的数据结构。与JobGraph最大的区别就是按照并行度对并行子任务进行了拆分,并明确了任务间数据传输的方式。

当把作业图提交给JobMaster之后JobMaster会转换成执行图,其实就是把算子进行展开,算子的并行度是几,就生成几个算子,例如FlatMap的并行度是2,那么就生成两个FlatMap,因为后面Flat Map1要在一个slot中,FlatMap2要在一个slot中

物理流图**(Physical Graph)**

JobMaster生成执行图后,会将它分发给TaskManager;各个TaskManager会根据执行图部署任务,最终的物理执行过程也会形成一张“图”,一般就叫作物理图(Physical Graph)。这只是具体执行层面的图,并不是一个具体的数据结构。

物理图主要就是在执行图的基础上,进一步确定数据存放的位置和收发的具体方式。有了物理图,TaskManager就可以对传递来的数据进行处理计算了。

其实执行图跟物理流图都差不多了,已经将算子的并行度都分配好了,多个并行度就占用多个slot,基本上没什么变化,只不过一些名称会变得不一样

Yarn应用模式作业提交流程

yarn应用模式中也是就没有客户端的什么事情了,在执行run-application这些命令之后,直接就会去Yarn的ResourceManager发起请求,之后会选择一个NodeManager节点启动一个容器,容器里面运行 ApplicationMaster,这个ApplicationMaster其实就是Flink中的JobMaster

然后通信系统先启动分发器,然后再启动资源管理器,这个资源管理器是Flink的资源管理器,然后分发器启动JobMaster,JobMaster会开始生成逻辑流图、作业流图、执行流图,因为不需要客户端了, 所以在客户端进行的操作现在都是JobMaster来做

生成执行流图之后,JobManager会向资源管理器请求slot资源,但是现在Flink的资源管理器现在不是真正管理资源的了,因为是yarn模式,现在所有的资源都是归yarn管理,所以这里的资源管理器相当于一个中间件,应该4申请资源,flink的资源管理器会向yarn的ResourceManager申请资源,

然后yarn会去启动自己的NodeManager,NodeManager中会启动容器,容器里面运行TaskManager,然后TaskManager中的slot数也是根据我们在flink配置文件中指定的来的,配置文件中配置了几个,每个TaskManager中就只能有几个slot,yarn模式也不例外

然后TaskManager会去告诉Flink的资源管理器,注册slot,告诉资源管理器自己启动成功,然后JobMaster就开始分配slot,然后TaskManager就向JobMaster提供需要的slot资源,然后JobMaster就会根据之前生成的执行流图把子任务分发出去,安排在不同的slot中

然后TaskManager在工作起来之后就会生成物理流图。

本篇结束!感谢观看
转载:https://cloud.tencent.com/developer/article/2468191

相关文章:

Flink 快速入门

本文涉及到大量的底层原理知识&#xff0c;包括运行机制图解都非常详细&#xff0c;还有一些实战案例&#xff0c;所以导致本篇文章会比较长&#xff0c;内容比较多&#xff0c;由于内容太多&#xff0c;很多目录可能展示不出来&#xff0c;需要去细心的查看&#xff0c;非常适…...

MySQL 8.0 OCP 英文题库解析(五)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题31~40 试题31:…...

lovart design 设计类agent的系统提示词解读

文章目录 lovart 设计agent介绍角色定义工作规范工具调用任务复杂度指南任务移交指南其他ref lovart 设计agent介绍 lovart作为设计agent&#xff0c;产品功能包括&#xff1a; 全链路设计能力&#xff1a;可以快速生成完整的品牌视觉方案&#xff0c;包括标志、配色、品牌规范…...

C++11特性

一.C的发展历史 C11是C的第二个主要版本&#xff0c;从C98起的重要更新&#xff0c;引入了大量更改&#xff0c;从C11起C规律的进行每3年更新一次。 二.列表初始化 2.1 C98和C11中的 { } 传统的C98中使用 { } 来进行列表初始化&#xff0c;结构体函数体都使用此类方法&…...

鸿蒙PC操作系统:从Linux到自研微内核的蜕变

鸿蒙PC操作系统是否基于Linux内核,需要结合其技术架构、发展阶段和官方声明综合分析。以下从多个角度展开论述: 一、鸿蒙操作系统的多内核架构设计 多内核混合架构 根据资料,鸿蒙操作系统(HarmonyOS)采用分层多内核架构,内核层包含Linux内核、LiteOS-m内核、LiteOS-a内核…...

用 RefCounted + WeakPtr 构建线程安全的异步模块

在 Chromium 的多线程异步编程中&#xff0c;合理管理对象生命周期非常关键。本文深入介绍 base::RefCountedThreadSafe 和 base::WeakPtr 的组合使用方法&#xff0c;并通过示例分析其使用要点及易踩的坑。 &#x1f331; 基础概念回顾 1. RefCountedThreadSafe<T> 是 …...

ElasticSearch 8.x 快速上手并了解核心概念

目录 核心概念概念总结 常见操作索引的常见操作常见的数据类型指定索引库字段类型mapping查看索引库的字段类型最高频使用的数据类型 核心概念 在新版Elasticsearch中&#xff0c;文档document就是一行记录(json)&#xff0c;而这些记录存在于索引库(index)中, 索引名称必须是…...

2025.5.19总结

工作&#xff1a;今天回归了3个问题单&#xff0c;测需求提交两个问题。然后再工作中慢慢有了自己的一些成就感&#xff0c;觉得工作越来越有干劲&#xff0c;因为感觉自己在工作上能做得越来越好&#xff0c;无论是在沟通方面&#xff0c;还是与同事的关系上&#xff0c;感觉都…...

C++(25): 标准库 <deque>

目录 1、 核心概念 2. 基本语法 3. 特点 4. 特有成员函数 5. 内存与性能 6. 示例代码 7. 成员函数列表 8. 使用场景 9. 注意事项 1、 核心概念 双端队列(Double-Ended Queue,deque) 是一种允许在队列头部和尾部高效插入和删除元素的线性数据结构,同时支持随机访问。…...

[ 计算机网络 ] | 宏观谈谈计算机网络

&#xff08;目录占位&#xff09; 网络间通信&#xff0c;本质是不同的两个用户通信&#xff1b;本质是两个不同主机上的两个进程间通信。 因为物理距离的提升&#xff0c;就衍生出了很多问题。TCP/IP协议栈 / OSI七层模型&#xff0c;将协议分层&#xff0c;每一层都是为了…...

会议动态|第十五届亚太燃烧学术年会精彩探析

ASPACC 2025第十五届亚太燃烧学术年会5月19日在新加坡隆重召开&#xff0c;本届盛会&#xff0c;以“构建零碳和可持续未来”为主题&#xff0c;汇聚了来自亚太的2000余位专家学者进行学术交流。会议聚焦燃烧反应动力学、火焰传播、燃烧效率等方向。 千眼狼在会议上展示了高速摄…...

Dify-3:系统架构

系统架构 概述了 Dify 的系统架构&#xff0c;解释主要组件如何协同工作以提供大语言模型&#xff08;LLM&#xff09;应用开发平台。内容涵盖高层架构、部署选项、核心子系统和外部集成。 1. 整体架构 Dify 采用基于微服务的架构&#xff0c;将前端 Web 应用与后端 API 服务…...

使用 docker-volume-backup 备份 Docker 卷

docker-volume-backup 是一个用于备份 Docker 卷的工具&#xff0c;在 Windows 10 上使用它&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 确保 Docker 环境已安装并正常运行 在 Windows 10 上&#xff0c;你需要安装 Docker Desktop for Windows。可以从 Docker 官方网…...

分布式与集群:概念、区别与协同

分布式与集群:概念、区别与协同 在分布式系统与云计算领域,分布式(Distributed)和集群(Cluster)是两个高频出现的核心概念。它们常被混淆,但本质上属于不同维度的设计思想。本文将从定义、分类、实际应用及协同关系四个层面,结合 Dubbo、Git、Hadoop 等典型案例,系统…...

Matlab简单优化模型应用

一、目的 掌握优化模型的建立方法,能够借助Matlab工具对建立的优化模型进行求解。 二、内容与设计思想 1、分析&#xff1a;某石油设备制造厂每月需要100套压缩机用于维护和运营石油开采设备。这些零件由工厂内部生产&#xff0c;每月生产500套&#xff0c;每批压缩机的生产…...

板凳-------Mysql cookbook学习 (四)

综合对比与选择建议 维度 PHP Java Python Ruby Perl 学习门槛 低&#xff08;适合新手&#xff09; 高&#xff08;语法复杂&#xff09; 低&#xff08;语法简洁&#xff09; 中&#xff08;需理解 Rails 理念&#xff09; 中&#xff08;特殊语法&#xf…...

C语言学习笔记之条件编译

编译器根据条件的真假决定是否编译相关的代码 常见的条件编译有两种方法&#xff1a; 一、根据宏是否定义&#xff0c;其语法如下&#xff1a; #ifdef <macro> …… #else …… #endif例子&#xff1a; #include <stdio.h>//def _DEBUG_ //定义_DEBUG_ int main(…...

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…...

Android设备 显示充电速度流程

整体逻辑&#xff1a;设备充电速度的判断 系统通过读取充电器的最大电流&#xff08;Current&#xff09;与最大电压&#xff08;Voltage&#xff09;&#xff0c;计算最大充电功率&#xff08;Wattage&#xff09;&#xff0c;以此判断当前是慢充、普通充还是快充&#xff1a…...

megatron——EP并行

1、专家并行&#xff08;Expert Parallelism, EP&#xff09;适用场景 定义&#xff1a; 专家并行是指在混合专家模型&#xff08;Mixture of Experts, MoE&#xff09;中&#xff0c;将不同的专家&#xff08;即子模型&#xff09;分配到不同的设备上&#xff0c;每个设备只负…...

如何轻松删除电脑上的文件(无法恢复文件)

如果您想清理电脑上的存储空间&#xff0c;您可能需要轻松删除电脑上的文件以释放空间。此外&#xff0c;如果您打算出售或捐赠您的旧电脑&#xff0c;永久删除您的文件至关重要&#xff0c;这可以保护您的隐私。无论如何&#xff0c;您需要一种有效且可靠的方法来从计算机中删…...

搭建一个永久免费的博客

搭建永久免费的博客&#xff08;1&#xff09;基本介绍 HugoStackGitHub GitHub GitHub GitHub Build and ship software on a single, collaborative platform GitHub 下载安装git Git - Downloads Edge插件authenticator 2fa client Settings->Password and auth…...

计算机底层的多级缓存以及缓存带来的数据覆盖问题

没有多级缓存的情况 有多级缓存的情况 缓存带来的操作覆盖问题 锁总线带来的消耗太大了。...

ICRA 2024 PROGrasp——实用的人机交互物体抓取系统

在机器人抓取任务中&#xff0c;自然语言理解能够显著改善人机交互体验&#xff0c;尤其是在需要机器人根据人类指令进行环境交互的场景中。然而&#xff0c;现有的抓取系统往往要求用户明确指定目标对象的类别&#xff0c;限制了交互的自然性和灵活性。为了解决这一问题&#…...

【Vue篇】潮汐中的生命周期观测站​

目录 引言 一、Vue生命周期 二、Vue生命周期钩子 三、、生命周期钩子实战 1.在created中发送数据 2.在mounted中获取焦点 四、综合案例-小黑记账清单 1.需求图示&#xff1a; 2.需求分析 3.思路分析 4.代码 5. 总结 引言 &#x1f4ac; 欢迎讨论&#xff1a;如果…...

【OpenCV基础2】图像运算、水印、加密、摄像头

目录 一、图像运算 1、利用“” 2、cv2.add() 3、掩膜异或 二、摄像头 1、读取、视频流保存 2、人脸识别 三、数字水印 1、水印嵌入 ​2、水印提取 四、图像加密 一、图像运算 1、利用“” import cv2 利用""方法将两幅图像相加img1 cv2.imread(project…...

第 25 届中国全电展即将启幕,构建闭环能源生态系统推动全球能源转型

由 AI 算力爆发引发的能源消耗剧增&#xff0c;与碳中和目标、能源安全需求及电网转型压力形成叠加效应&#xff0c;使全球能源体系面临前所未有的挑战。在此背景下&#xff0c;第 25 届中国全电展&#xff08;EPOWER EXPO&#xff09;将于 2025 年 6 月 11 日至 13 日在上海新…...

vue3:十三、分类管理-表格--编辑、新增、详情、刷新

一、效果 实现封装表格的新增、编辑、详情查看,表格刷新功能 实现表格组件中表单的封装 1、新增 如下图,新增页面显示空白的下拉,文本框,文本域,并实现提交功能 2、编辑 如下图,点击行数据,可将行数据展示到编辑弹窗,并实现提交功能 3、详情 如下图,点击行数据,…...

一周快讯 | 银发文娱旅游一周新鲜事

​ 银发文娱旅游一周新鲜事 一周银发文娱旅游产业资讯速览 星期一 5月19日 1 企业动态 同方全球人寿等共建一站式康养服务生态 东秀星健康养老产业等合作赋能康养产业&#xff0c;开发“旅居养老”项目 欧莱雅等合作将推出银发族形象管理课程 2 行业风向 总投资10亿&a…...

C++寻位映射的奇幻密码:哈希

文章目录 1.什么是哈希&#xff1f;2.哈希的常见实现方法2.1 直接定址法2.2 除留余数法 3.哈希冲突4.哈希冲突的解决4.1 闭散列4.1.1 线性探测4.1.1.1 哈希表的基本数据结构4.1.1.2 哈希表的key转换4.1.1.3 哈希表的插入4.1.1.4 哈希表的查找4.1.1.5 哈希表的删除 4.1.2 二次探…...

Spring Boot 集成 druid,实现 SQL 监控

文章目录 背景Druid 简介监控统计 StateFilter其它 Filter详细步骤第 1 步:添加依赖第 2 步:添加数据源配置【通用部分】第 3 步:添加监控配置【关键部分】第 3 步:访问 druid 页面参考背景 😂 在 Code Review 过程中发现,经常有开发会忘记给表加索引。这就导致,生产运…...

从零开始学习three.js(21):一文详解three.js中的矩阵Matrix和向量Vector

一、三维世界的数学基石 在Three.js的三维世界里&#xff0c;所有视觉效果的实现都建立在严密的数学基础之上。其中向量&#xff08;Vector&#xff09; 和矩阵&#xff08;Matrix&#xff09; 是最核心的数学工具&#xff0c;它们就像构建数字宇宙的原子与分子&#xff0c;支…...

无需笔墨之功,锦绣SQL自成桥——QuickAPI古法炼数据秘术

楔子&#xff1a;锦绣SQL&#xff0c;化身为桥 昔有匠人苦修代码之术&#xff0c;欲通数据库与前朝之界&#xff0c;然笔耕不辍&#xff0c;耗时弥久。今有秘器名曰QuickAPI&#xff0c;但凭三寸SQL文&#xff0c;顷刻间筑起数据虹桥。纵使不谙代码之道者&#xff0c;亦可挥毫…...

模块与包的导入

一、导入官方库 我们复盘下学习python的逻辑&#xff0c;所谓学习python就是学习python常见的基础语法学习你所处理任务需要用到的第三方库 类别典型库解决的问题学习门槛基础工具os、sys、json操作系统交互、序列化数据&#xff08;如读写 JSON 文件&#xff09;低科学计算n…...

智能文档抽取技术可以应用于哪些场景?

近日&#xff0c;合合信息编撰并发布了《2025智能文档技术与应用白皮书》。该书中不仅深度解析技术原理与创新突破&#xff0c;更聚焦金融、法律、制造等行业的典型场景&#xff0c;结合典型案例揭示技术如何赋能合同智能审查、票据自动化处理、知识库构建等业务场景&#xff0…...

实践促成长:成都理工大学华清远见成都中心实训

2025年5月, 华清远见成都中心迎来了成都理工大学大数据管理与应用专业23级以及电子商务22级的同学们&#xff0c;以实践为导向、以提升能力为目标的校企合作实训活动在此展开&#xff0c;为同学们开启了一段充满挑战与收获的学习之旅。 华清远见成都中心为两个专业的同学们量身…...

北京本地 SEO 推广:从技术成本到效果转化的深度拆解

在数字化营销的浪潮中&#xff0c;北京本地企业对 SEO 推广的需求日益增长。然而&#xff0c;SEO 推广服务的价格参差不齐&#xff0c;效果也难以预估。本文将从技术实现、成本构成等角度&#xff0c;深入剖析北京本地 SEO 推广服务的价格与效果&#xff0c;baidu0048为企业选择…...

JavaScript 中的五种继承方式进行深入对比

文章目录 前言JavaScript 五种继承方式对比原型链继承构造函数继承组合继承寄生组合继承ES6 class extends 继承五种继承方式对比表前言 对 JavaScript 中的五种继承方式进行深入对比:原型链继承、构造函数继承、组合继承、寄生组合继承、以及 ES6 的 class extends。 内容将…...

CAU数据库class2 SQL语言

​ SQL分类 DDL 数据库操作 查询数据库&#xff1a; 查询所有数据库 show databases; 查询名字里有t的数据库 show databases like %t%;查询名字以t为结尾的数据库 show databases like %t;查看数据库name是怎么创建出来的 show create database name;创建数据库 创建…...

软考教材重点内容 信息安全工程师 25章 移动安全 26章 大数据安全

第 25 章移动应用安全需求分析与安全保护工程 移动互联网技术基本组成如图 25-1 所示&#xff0c;包括三个部分:一是移动应用&#xff0c;简称 App;二是通信网络&#xff0c;包括无线网络、移动通信网络及互联网;三是应用服务端&#xff0c;由相关的服务器构成&#xff0c;负责…...

有关Groutine无限创建的分析

有关Groutine无限创建的分析 文章目录 有关Groutine无限创建的分析从操作系统分析进程、线程、协程的区别进程内存线程内存执行单元 cpu切换成本协程切换成本线程切换成本内存占用 Go程是否可以无限创建不控制go程创建引发的问题简单方式控制go程创建channel有buffersync.WaitG…...

FANUC发那科焊接机器人智能气阀

在现代工业生产中&#xff0c;焊接技术的发展日新月异&#xff0c;其中发那科&#xff08;FANUC&#xff09;焊接机器人以其高精度和稳定性受到了广泛应用。而智能气阀作为发那科焊接机器人的重要组成部分&#xff0c;在提升焊接效率和质量方面发挥着不可忽视的作用。 工作原理…...

软件架构风格系列(7):闭环控制架构

文章目录 引言一、闭环控制架构&#xff1a;让系统学会“自我调节”的魔法&#xff08;一&#xff09;从温控系统理解核心原理&#xff08;二&#xff09;核心组件解析 二、架构设计图&#xff1a;闭环控制的“四大核心环节”三、Java实战&#xff1a;手写一个智能温控系统&…...

Java合并两个列表到目标列表,并且进行排序

可以通过使用addAll()方法将两个列表合并到目标列表中。以下是实现代码&#xff1a; java 复制 下载 List<LedgerRecord> rkRecordList warehouseMapper.selectLedgerRkRecordByMaterialNo(materialNo); List<LedgerRecord> ckRecordList warehouseMapper.se…...

关于在Unity项目中使用Post Processing插件打包到web端出现的问题

关于在Unity项目中使用Post Processing插件打包到web端出现的问题 解决方法&#xff1a;是不激活摄像机上的Post Processing有关组件&#xff0c;拉低场景中的Directional Light平行光的强度进行web端打包。 &#xff08;烘焙灯光时是可以激活。&#xff09; web端支持这个Pos…...

智象科技:自动化模块驱动IT运维效能升级

智象自动化模块概览 智象科技的一站式IT运维平台中的自动化模块&#xff0c;是企业数字化转型的强大助推器。该模块集成了IT运维作业的流程编排、脚本编排&#xff0c;各类运维资源配置项目和脚本的合规巡检&#xff0c;以及基础信息、监控指标的巡检配置等自动化管理&#xff…...

GPU状态监控

GPU 状态监控 对比&#xff1a; GPU项目名称项目名称单机多 GPUGPU状态监控以时间为横轴展示GPU被占用的动态过程&#xff0c;但不显示具体时间单机多 GPUGPU 实时监控服务多卡GPU统一展示&#xff0c;数据简洁清晰多机多 GPU服务器集群监控面板可以同时监控多个服务器上的GPU…...

VS2017编译openssl3.0.8

openssl是一个功能丰富且自包含的开源安全工具箱。它提供的主要功能有:SSL协议实现(包括SSLv2、SSLv3和TLSv1)、大量软算法(对称/非对称/摘要)、大数运算、非对称算法密钥生成、ASN.1编解码库、证书请求(PKCS10)编解码、数字证书编解码、CRL编解码、OCSP协议、数字证书验证、P…...

构建高效移动端网页调试流程:以 WebDebugX 为核心的工具、技巧与实战经验

现代前端开发早已不仅仅局限于桌面浏览器。随着 Hybrid 应用、小程序、移动 Web 的广泛应用&#xff0c;开发者日常面临的一个关键挑战是&#xff1a;如何在移动设备上快速定位并解决问题&#xff1f; 这不再是“打开 DevTools 查查 Console”的问题&#xff0c;而是一个关于设…...

前缀和——和为K的子数组

作者感觉本题稍稍有点难度&#xff0c;看了题解也思考了有一会TWT 显然&#xff0c;暴力我们是不可取的&#xff0c;但这里我们可以采取一种新的遍历数组形式&#xff0c;从后向前&#xff0c;也就是以i位置为结尾的所有子数组&#xff0c;这个子数组只统计i位置之前的。 然后…...