Flink 优化 (五) --------- Job 优化

news2024/11/15 6:22:28

目录

  • 一、使用 DataGen 造数据
    • 1. DataStream 的 DataGenerator
    • 2. SQL 的 DataGenerator
  • 二、算子指定 UUID
  • 三、链路延迟测量
  • 四、开启对象重用
  • 五、细粒度滑动窗口优化


一、使用 DataGen 造数据

开发完 Flink 作业,压测的方式很简单,先在 kafka 中积压数据,之后开启 Flink 任务,出现反压,就是处理瓶颈。相当于水库先积水,一下子泄洪。

数据可以是自己造的模拟数据,也可以是生产中的部分数据。造测试数据的工具:DataFactory、datafaker 、DBMonster、Data-Processer 、Nexmark、Jmeter 等。

Flink 从 1.11 开始提供了一个内置的 DataGen 连接器,主要是用于生成一些随机数,用于在没有数据源的时候,进行流任务的测试以及性能测试等。

1. DataStream 的 DataGenerator

import com.fancy.flink.tuning.bean.OrderInfo;
import com.fancy.flink.tuning.bean.UserInfo;
import org.apache.commons.math3.random.RandomDataGenerator;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.datagen.DataGeneratorSource;
import org.apache.flink.streaming.api.functions.source.datagen.RandomGenerator;
import org.apache.flink.streaming.api.functions.source.datagen.SequenceGenerator;
public class DataStreamDataGenDemo {
	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		conf.set(RestOptions.ENABLE_FLAMEGRAPH, true);
		StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(conf);
		env.setParallelism(1);
		env.disableOperatorChaining();
		SingleOutputStreamOperator<OrderInfo> orderInfoDS = env
			.addSource(
				new DataGeneratorSource<>(new RandomGenerator<OrderInfo>() {
					@Override
					public OrderInfo next() {
						return new OrderInfo(
							random.nextInt(1, 100000),
							random.nextLong(1, 1000000),
							random.nextUniform(1, 1000),
							System.currentTimeMillis()
						);
					}
			})).returns(Types.POJO(OrderInfo.class));
		SingleOutputStreamOperator<UserInfo> userInfoDS = env
			.addSource(
				new DataGeneratorSource<UserInfo>(
					new SequenceGenerator<UserInfo>(1, 1000000) {
						RandomDataGenerator random = new RandomDataGenerator();
						@Override
						public UserInfo next() {
							return new UserInfo(
								valuesToEmit.peek().intValue(),
								valuesToEmit.poll().longValue(),
								random.nextInt(1, 100),
								random.nextInt(0, 1)
							);
						}
					}
			))
			.returns(Types.POJO(UserInfo.class));
		orderInfoDS.print("order>>");
		userInfoDS.print("user>>");
		env.execute();
	}
}

2. SQL 的 DataGenerator

import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.RestOptions;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
public class SQLDataGenDemo {
	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		conf.set(RestOptions.ENABLE_FLAMEGRAPH, true);
		StreamExecutionEnvironment env = 
		StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(conf);
		env.setParallelism(1);
		env.disableOperatorChaining();
	
		StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);
		String orderSql = "CREATE TABLE order_info (\n" +
			" id INT,\n" +
			" user_id BIGINT,\n" +
			" total_amount DOUBLE,\n" +
			" create_time AS localtimestamp,\n" +
			" WATERMARK FOR create_time AS create_time\n" +
			") WITH (\n" +
			" 'connector' = 'datagen',\n" +
			" 'rows-per-second'='20000',\n" +
			" 'fields.id.kind'='sequence',\n" +
			" 'fields.id.start'='1',\n" +
			" 'fields.id.end'='100000000',\n" +
			" 'fields.user_id.kind'='random',\n" +
			" 'fields.user_id.min'='1',\n" +
			" 'fields.user_id.max'='1000000',\n" +
			" 'fields.total_amount.kind'='random',\n" +
			" 'fields.total_amount.min'='1',\n" +
			" 'fields.total_amount.max'='1000'\n" +
			")";
	String userSql="CREATE TABLE user_info (\n" +
			" id INT,\n" +
			" user_id BIGINT,\n" +
			" age INT,\n" +
			" sex INT\n" +
			") WITH (\n" +
			" 'connector' = 'datagen',\n" +
			" 'rows-per-second'='20000',\n" +
			" 'fields.id.kind'='sequence',\n" +
			" 'fields.id.start'='1',\n" +
			" 'fields.id.end'='100000000',\n" +
			" 'fields.user_id.kind'='sequence',\n" +
			" 'fields.user_id.start'='1',\n" +
			" 'fields.user_id.end'='1000000',\n" +
			" 'fields.age.kind'='random',\n" +
			" 'fields.age.min'='1',\n" +
			" 'fields.age.max'='100',\n" +
			" 'fields.sex.kind'='random',\n" +
			" 'fields.sex.min'='0',\n" +
			" 'fields.sex.max'='1'\n" +
			")";
		tableEnv.executeSql(orderSql);
		tableEnv.executeSql(userSql);
		tableEnv.executeSql("select * from order_info").print();
		// tableEnv.executeSql("select * from user_info").print();
	}
}

二、算子指定 UUID

对于有状态的 Flink 应用,推荐给每个算子都指定唯一用户 ID(UUID)。 严格地说,仅需要给有状态的算子设置就足够了。但是因为 Flink 的某些内置算子(如 window)是有状态的,而有些是无状态的,可能用户不是很清楚哪些内置算子是有状态的,哪些不是。所以从实践经验上来说,我们建议每个算子都指定上 UUID。

默认情况下,算子 UID 是根据 JobGraph 自动生成的,JobGraph 的更改可能会导致UUID 改变。手动指定算子 UUID ,可以让 Flink 有效地将算子的状态从 savepoint 映射到作业修改后 (拓扑图可能也有改变) 的正确的算子上。比如替换原来的 Operator 实现、增加新的Operator、删除Operator等等,至少我们有可能与Savepoint中存储的Operator状态对应上。这是 savepoint 在 Flink 应用中正常工作的一个基本要素。Flink 算子的 UUID 可以通过 uid(String uid) 方法指定,通常也建议指定 name。

#算子.uid("指定 uid")
.reduce((value1, value2) -> Tuple3.of("uv", value2.f1, value1.f2 + value2.f2))
.uid("uv-reduce").name("uv-reduce")

1)提交案例:未指定 uid

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.UvDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

触发保存点:

//直接触发
flink savepoint <jobId> [targetDirectory] [-yid yarnAppId] #on yarn 模式需要指定-yid 参数
//cancel 触发
flink cancel -s [targetDirectory] <jobId> [-yid yarnAppId] #on yarn 模式需要指定-yid 参数
bin/flink cancel -s hdfs://hadoop1:8020/flink-tuning/sp 98acff568e8f0827a67ff37648a29d7f -yid application_1640503677810_0017

修改代码,从 savepoint 恢复:

bin/flink run \
-t yarn-per-job \
-s hdfs://hadoop1:8020/flink-tuning/sp/savepoint-066c90-6edf948686f6 \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.UvDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

报错如下:

Caused by: java.lang.IllegalStateException: Failed to rollback to checkpoint/savepoint 
hdfs://hadoop1:8020/flink-tuning/sp/savepoint-066c90-6edf948686f6. Cannot map checkpoint/savepoint state for operator  ddb598ad156ed281023ba4eebbe487e3 to the new program, 
because the operator is not available in the new program. If you want to allow to skip this, you can set the --allowNonRestoredSt ate option on the CLI.

临时处理:在提交命令中添加–allowNonRestoredState (short: -n)跳过无法恢复的算子。

2)提交案例:指定 uid

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.UidDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

触发保存点:

//cancel 触发 savepoint
bin/flink cancel -s hdfs://hadoop1:8020/flink-tuning/sp 272e5d3321c5c1481cc327f6abe8cf9c -yid application_1640268344567_0033
修改代码,从保存点恢复:
bin/flink run \
-t yarn-per-job \
-s hdfs://hadoop1:8020/flink-tuning/sp/savepoint-272e5d-d0c1097d23e0 \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.UidDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

三、链路延迟测量

对于实时的流式处理系统来说,我们需要关注数据输入、计算和输出的及时性,所以处理延迟是一个比较重要的监控指标,特别是在数据量大或者软硬件条件不佳的环境下。Flink提供了开箱即用的 LatencyMarker 机制来测量链路延迟。开启如下参数:

metrics.latency.interval: 30000 #默认 0,表示禁用,单位毫秒

监控的粒度,分为以下 3 档:

➢ single:每个算子单独统计延迟;
➢ operator(默认值):每个下游算子都统计自己与 Source 算子之间的延迟;
➢ subtask:每个下游算子的 sub-task 都统计自己与 Source 算子的 sub-task 之间的延迟。

metrics.latency.granularity: operator #默认 operator

一般情况下采用默认的 operator 粒度即可,这样在 Sink 端观察到的 latency metric就是我们最想要的全链路(端到端)延迟。subtask 粒度太细,会增大所有并行度的负担,不建议使用。

LatencyMarker 不会参与到数据流的用户逻辑中的,而是直接被各算子转发并统计。
为了让它尽量精确,有两点特别需要注意:

➢ 保证 Flink 集群内所有节点的时区、时间是同步的:ProcessingTimeService 产生时间戳最终是靠 System.currentTimeMillis()方法,可以用 ntp 等工具来配置。

➢ metrics.latency.interval 的时间间隔宜大不宜小:一般配置成 30000(30 秒)左右。一是因为延迟监控的频率可以不用太频繁,二是因为 LatencyMarker 的处理也要消耗一定性能。

提交案例:

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-Dmetrics.latency.interval=30000 \
-c com.fancy.flink.tuning.UidDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

可以通过下面的 metric 查看结果:

flink_taskmanager_job_latency_source_id_operator_id_operator_subtask_index_latency端到端延迟的 tag 只有 murmur hash 过的算子 ID (用 uid()方法设定的) ,并没有算子名称,(https://issues.apache.org/jira/browse/FLINK-8592) 并且官方暂时不打算解决这个问题,所以我们要么用最大值来表示,要么将作业中 Sink 算子的 ID 统一化。比如使用了 Prometheus 和 Grafana 来监控,效果如下:

在这里插入图片描述

四、开启对象重用

在这里插入图片描述
当调用了 enableObjectReuse 方法后,Flink 会把中间深拷贝的步骤都省略掉,SourceFunction 产生的数据直接作为 MapFunction 的输入,可以减少 gc 压力。但需要特别注意的是,这个方法不能随便调用,必须要确保下游 Function 只有一种,或者下游的 Function 均不会改变对象内部的值。否则可能会有线程安全的问题。

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-Dpipeline.object-reuse=true \
-Dmetrics.latency.interval=30000 \
-c com.fancy.flink.tuning.UidDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar

五、细粒度滑动窗口优化

1)细粒度滑动的影响

当使用细粒度的滑动窗口(窗口长度远远大于滑动步长)时,重叠的窗口过多,一个数据会属于多个窗口,性能会急剧下降。

在这里插入图片描述
我们经常会碰到这种需求:以 3 分钟的频率实时计算 App 内各个子模块近 24 小时的PV 和 UV。我们需要用粒度为 1440 / 3 = 480 的滑动窗口来实现它,但是细粒度的滑动窗口会带来性能问题,有两点:

➢ 状态
对于一个元素,会将其写入对应的(key, window)二元组所圈定的 windowState 状态中。如果粒度为 480,那么每个元素到来,更新 windowState 时都要遍历 480 个窗口并写入,开销是非常大的。在采用 RocksDB 作为状态后端时,checkpoint 的瓶颈也尤其明显。

➢ 定时器
每一个(key, window)二元组都需要注册两个定时器:一是触发器注册的定时器,用于决定窗口数据何时输出;二是 registerCleanupTimer()方法注册的清理定时器,用于在窗口彻底过期(如 allowedLateness 过期)之后及时清理掉窗口的内部状态。细粒度滑动窗口会造成维护的定时器增多,内存负担加重。

2)解决思路
DataStreamAPI中,自己解决(https://issues.apache.org/jira/browse/FLINK-7001)。我们一般使用滚动窗口+在线存储+读时聚合的思路作为解决方案:

(1)从业务的视角来看,往往窗口的长度是可以被步长所整除的,可以找到窗口长度和窗口步长的最小公约数作为时间分片 (一个滚动窗口的长度) ;

(2)每个滚动窗口将其周期内的数据做聚合,存到下游状态或打入外部在线存储 (内存数据库如 Redis,LSM-based NoSQL 存储如 HBase) ;

(3)扫描在线存储中对应时间区间 (可以灵活指定) 的所有行,并将计算结果返回给前端展示。

3)细粒度的滑动窗口案例

提交案例:统计最近 1 小时的 uv,1 秒更新一次(滑动窗口)

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.SlideWindowDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \
--sliding-split false

在这里插入图片描述
4)时间分片案例

提交案例:统计最近 1 小时的 uv,1 秒更新一次(滚动窗口+状态存储)

bin/flink run \
-t yarn-per-job \
-d \
-p 5 \
-Drest.flamegraph.enabled=true \
-Dyarn.application.queue=test \
-Djobmanager.memory.process.size=1024mb \
-Dtaskmanager.memory.process.size=2048mb \
-Dtaskmanager.numberOfTaskSlots=2 \
-c com.fancy.flink.tuning.SlideWindowDemo \
/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \
--sliding-split true

在这里插入图片描述
Flink 1.13 对 SQL 模块的 Window TVF 进行了一系列的性能优化,可以自动对滑动窗口进行切片解决细粒度滑动问题。

在这里插入图片描述
https://nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/table/sql/queries/window-tvf/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/411017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全景图像畸变校正

1.简介 理想的相机基本上是小孔成像的&#xff0c;在小孔成像模型中&#xff0c;如果焦距一定&#xff0c;那么图像传感器像素平面的面积直接决定了相机视场角的大小&#xff0c;超过这个视场角范围的物体不会被镜头获取到。因此基于透镜成像原理的相机&#xff0c;视场角无法…

JAVA解析XML时的内存消耗问题

问题出现 最近一个项目中&#xff0c;有个需求功能是从外部传入的XML读取数据&#xff0c;然后写入到数据库中。 写完之后&#xff0c;有在本地电脑上的Tomcat跑了一下&#xff0c;正常读取到XML中的数据&#xff0c;并整理好后&#xff0c;插入了数据库保存了。但是线上运行的…

C语言文件操作复习回顾(2)

TIPS 文件的顺序读写&#xff1a;fgetc, fputc, fputs&#xff08;一行字符串的输出\n注意一下&#xff09;, fgets&#xff08;一行字符串的输入\n三特性&#xff09;&#xff0c;fprintf&#xff08;格式化字符串的输出联想printf很简单&#xff09;&#xff0c;fscanf&…

pyspark 实验二,rdd编程

1.环境准备 start-all.sh启动Hadoop ./bin start-all.sh 启动spark 上传数据集 1.求该系总共多少学生 linessc.textFile("file:///home/data.txt") res lines.map(lambda x:x.split(",")).map(lambda x:x[0]) sumres.distinct() sum.cont() 2.求该系设置…

【MybatisPlus快速入门】—— 进阶入门

进阶篇 1.1 映射专题 Mybatis 框架之所以能够简化数据库操作&#xff0c;是因为他内部的映射机制&#xff0c;通过自动映射&#xff0c;进行数据的封装&#xff0c;我们只要符合映射规则&#xff0c;就可以快速高效的完成SQL操作的实现既然 MybatisPlus 是基于Mybatis 的增强…

程序员如何能提高自己的编程水平?

这些实用的小建议&#xff0c;能帮你迅速地提高编程水平&#xff1a; 不要做无意义的奋斗 拒绝喊口号和无意义的奋斗&#xff0c;包括但不限于&#xff1a; ①做了计划表却从未有执行的一天&#xff1b; ②每天都是最早来、最晚走&#xff0c;但是工作进度趋近于0&#xff1b…

ASP.NET Core MVC 从入门到精通之接化发(一)

随着技术的发展&#xff0c;ASP.NET Core MVC也推出了好长时间&#xff0c;经过不断的版本更新迭代&#xff0c;已经越来越完善&#xff0c;本系列文章主要讲解ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容&#xff0c;适用于初学者&#xff0c;在校毕业生&#xff0c…

PathCore:IAD文献解读

论文链接&#xff1a;[Towards Total Recall in Industrial Anomaly Detection]Towards Total Recall in Industrial Anomaly Detection &#xff1a;数据集&#xff0c; &#xff1a;标签 : 在ImageNet上预训练后的网络 第 张图 网络中第 层 1. Locall…

Sentinel学习笔记

Sentinel 官方文档&#xff1a; https://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 SpringCloud Alibaba&#xff1a; https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel 是什么…

丝滑的打包部署,一套带走~

以下文章来源于悟空聊架构 &#xff0c;作者悟空聊架构 本文主要内容如下&#xff1a; 目录 一、背景 Docker打包部署方案 项目背景&#xff1a;新项目的后端框架是刚起步&#xff0c;搭建的是一套微服务框架&#xff0c;基础服务有网关 Gateway&#xff0c; Nacos 注册中心…

为什么stm32gpio引脚的翻转速度最大只有18Mhz

(1). GPIO 引脚速度&#xff1a;GPIO_Speed_2MHz (10MHz, 50MHz) ; 又称输出驱动电路的响应速度&#xff1a;&#xff08;芯片内部在I/O口的输出部分安排了多个响应速度不同的输出驱动电路&#xff0c;用户可以根据自己的需要选择合适的驱动电路&#xff0c;通过选择速度来选择…

史上最全测试开发工具视频教程详解(含自动化、性能、接口、抓包)

目录 一、UI自动化测试工具 1. uiautomator2 2. Appium 3. ATX-Test 4. Airtest 5. ATXServer2 6. STF 7. Appetizer 二、APP稳定性测试工具 8. UICrawler 9. Maxim 10. AppCrawler 三、APP性能测试工具 11. SoloPi 12. GT 四、抓包工具 13. AnyProxy …

pytorch安装教程(二)

一直用的pytorch1.2&#xff0c;有点老了&#xff0c;想换个新版本&#xff0c;换成了pytorch2.0. torch安装 安装过程最重要的就是cuda、cudnn的版本和pytorch对应。 因为要在GPU上跑代码。 删除老旧torch 我用的软件是anaconda&#xff0c;因为可以创建虚拟环境。 步骤&…

LAZADA将缩短履约时效,卖家发货倍感压力

Lazada的跨境卖家们&#xff0c;恐怕又要头疼了。 近日&#xff0c; Lazada官方宣布&#xff0c;为了提升消费者体验&#xff0c;平台将调整商家履约订单时效。从2023年5月4日起生成的订单履约时效将有所更新。 具体而言&#xff0c;内地、香港和Laz Go Global的履约节点为“点…

Qt Quick - MessageDialog 消息提示框

MessageDialog 使用总结一、概述二、使用1. 例子一2. 例子二三、常用属性一、概述 MessageDialog 最基本的用例是弹出警告框。它还允许用户根据启用的按钮以各种方式进行响应。对话框最初是不可见的。你需要首先按需设置属性&#xff0c;然后将visible设置为true或调用open()。…

FIFO设计笔记(双口RAM、同步FIFO、异步FIFO)Verilog及仿真

文章目录0、前言0.1、FIFO0.2、FIFO与RAM1、异步双口RAM1.1、原理1.2、Verilog代码1.3、tb仿真2、FIFO设计前瞻知识2.1、格雷码2.1.1、二进制转格雷码Verilog代码tb仿真2.1.2、格雷码转二进制Verilog代码tb仿真2.2、独热码3、同步FIFO3.1、同步FIFO设计原理3.1.1、设计原理3.1.…

SpringBoot中操作Redis通过所有可能的key查询存在的key并解析为对象实体的通用方法

场景 SpringBoot中操作Redis的特殊操作-批量查询(通过key的集合批量查杜绝模糊搜索)、查询并解析对象list&#xff1a; SpringBoot中操作Redis的特殊操作-批量查询(通过key的集合批量查杜绝模糊搜索)、查询并解析对象list_霸道流氓气质的博客-CSDN博客 在上面讲操作redis中特…

【未来已来】人人都说GPT,人人都怕GPT,人人都用GPT

文章目录前言一、GPT是什么&#xff1f;二、当GPT和AI遇到摄影总结前言 ChatGPT是由美国OpenAI研发的能够通过自然语言驱动的人工智能技术工具&#xff0c;因为它强大的执行力和任务处理能力&#xff0c;一经亮相就引起了极大的关注。与之类似&#xff0c;在图像智能生成方面&…

Java 自学 - 接口与继承 接口

设计 Java 的接口 在设计 LOL 的时候&#xff0c;进攻类英雄有两种&#xff0c;一种是进行物理系攻击&#xff0c;一种是进行魔法系攻击 这时候&#xff0c;就可以使用接口来实现这个效果。 接口就像是一种约定&#xff0c;我们约定某些英雄是物理系英雄&#xff0c;那么他们…

交接机的基本原理

第七章&#xff1a;交接机的基本原理 在网络中传输数据时需要遵循一些标准&#xff0c;以太网协议定义了数据帧在以太网上的传输标准&#xff0c;了解以太网协议是充分理解数据链路层通信的基础。以太网交换机是实现数据链路层通信的主要设备&#xff0c;了解以太网交换机的工作…