使用Kafka Streams开发流处理应用

使用Kafka Streams开发流处理应用

  • 一、简介
    • 1.1 Kafka Streams定义
    • 1.2 Kafka Streams的优势
    • 1.3 Kafka Streams应用场景
  • 二、环境搭建
    • 2.1 安装Kafka
    • 2.2 安装Kafka Streams
    • 2.3 构建Kafka集群
  • 三、Kafka Streams编程API介绍
    • 3.1 Kafka Streams主要API
    • 3.2 应用程序的配置和参数
    • 3.3 Topology的定义和构建
    • 3.4 各种数据处理操作的使用(map、filter、flatmap等)
  • 四、流处理实战案例
    • 4.1 流处理应用的开发步骤
      • 步骤一:创建 Kafka Streams 实例
      • 步骤二:定义输入与输出主题
      • 步骤三:启动 Kafka Streams 实例
    • 4.2 事件日志监控案例
      • 场景描述:
      • 解决方案:
    • 4.3 用户行为统计案例
      • 场景描述:
      • 解决方案:
  • 五、性能优化
    • 5.1 如何评估Kafka Streams应用的性能
      • 5.1.1 吞吐量
      • 5.1.2 延迟
      • 5.1.3 内存占用
    • 5.2 优化并行度和吞吐量
      • 5.2.1 调整线程池大小
      • 5.2.2 调整partition数量
      • 5.2.3 使用压缩算法
    • 5.3 实现数据压缩
  • 六、在生产中的应用
    • 6.1 高可用性集群部署
    • 6.2 监控和报警
    • 6.3 日志管理

一、简介

1.1 Kafka Streams定义

Kafka Streams是一款开源、分布式和水平扩展的流处理平台,其在Apache Kafka之上进行构建,借助其高性能、可伸缩性和容错性,可以实现高效的流处理应用程序。

1.2 Kafka Streams的优势

Kafka Streams的优势包括:

  • 基于Kafka生态系统,可以更轻松地集成到已有Kafka环境中。
  • 容易部署和管理,可以通过Docker等容器技术轻松实现自动化部署和运维。
  • 对于流式数据处理任务,Kafka Streams相比其他框架具有更高的性能。

1.3 Kafka Streams应用场景

Kafka Streams主要用于以下应用场景:

  • 实时数据处理:通过实时地流式计算,对数据进行快速分析和处理。
  • 流式ETL:将数据从一个数据源抽取到另一个数据源,或将数据进行转换、清洗和聚合操作。
  • 流-表格Join:将一条流数据与一个表进行关联查询,实现实时查询和联合分析。

二、环境搭建

2.1 安装Kafka

在官网下载Kafka的二进制包,解压后即可使用。安装过程可以参考官方文档。

2.2 安装Kafka Streams

在Maven或Gradle项目的pom.xml或build.gradle文件中添加以下依赖即可安装Kafka Streams:

<dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-streams</artifactId><version>2.8.0</version>
</dependency>

2.3 构建Kafka集群

构建Kafka集群可以使用Docker Compose等工具实现自动化部署。对于测试环境,可以使用单台机器构建一个多节点Kafka集群;对于生产环境,需要根据业务需求和QPS等指标确定集群规模。

三、Kafka Streams编程API介绍

3.1 Kafka Streams主要API

Kafka Streams是一个Java API,它允许用户使用简单的Java函数对流式数据进行转换和处理。Kafka Streams主要包括以下API:

  • StreamBuilder:用于为Kafka流构建拓扑结构。
  • KStream和KTable:可以将Kafka主题中的消息转换为键值对流或表。
  • GlobalKTable:类似于KTable,但在所有分区中都具有全局状态。
  • Serializer和Deserializer:用于序列化和反序列化Java对象以便写入和读取Kafka流。
  • Processor和Transformer:用于自定义操作和转换流。

3.2 应用程序的配置和参数

在Kafka Streams应用程序中,可以使用以下几种参数来配置应用程序的行为:

  • Bootstrapping Servers:指定Kafka集群的引导服务器。
  • 应用程序ID:每个应用程序必须具有唯一的ID。
  • Serde配置:用于指定如何序列化和反序列化记录键和记录值。
  • 缓存大小控制:用于控制应用程序的本地缓存大小。
  • 规则配置:用于指定消耗和生产数据的语义。

3.3 Topology的定义和构建

Topology是Kafka Streams应用程序中数据流的物理表示。它是由Processors和State Stores组成的拓扑结构。每个Processor表示一个数据流操作,而State Store表示一个具有本地状态的存储设备。Toplogy的构建可以使用StreamBuilder API进行操作。

3.4 各种数据处理操作的使用(map、filter、flatmap等)

Kafka Streams API提供了各种常见的数据处理操作,以便处理流数据。以下是一些基本的数据处理操作:

  • map:用于将一个记录键值对转换为另一个键值对。
  • filter:用于根据某些条件过滤掉记录。
  • flatMap:用于将一个键值对映射为多个键值对。
  • groupByKey:按键对记录进行分组。
  • reduceByKey:用于针对相同键的记录进行聚合操作。
  • aggregateByKey:用于针对相同键的记录进行聚合,并将结果插入到全局状态存储中。

示例代码如下:

//定义并构建拓扑结构
StreamBuilder builder = new StreamBuilder();
KStream<String, String> textLines = builder.stream("TextLinesTopic");
KStream<String, String> wordCounts = textLines.flatMapValues(textLine -> Arrays.asList(textLine.split("\\W+"))).groupBy((key, word) -> word).count();
wordCounts.to("WordsWithCountsTopic", Produced.with(stringSerde, longSerde));//进行map操作
KStream<String, String> upperCaseLines = textLines.map((key, value) -> KeyValue.pair(key, value.toUpperCase()));//进行filter操作
KStream<String, String> shortLines = textLines.filter((key, value) -> value.length() < 10);//进行reduceByKey操作
KTable<String, Long> wordCountTable = textLines.flatMapValues(textLine -> Arrays.asList(textLine.toLowerCase().split("\\W+"))).groupBy((key, word) -> word).count(Materialized.as("wordCountStore"));

四、流处理实战案例

4.1 流处理应用的开发步骤

步骤一:创建 Kafka Streams 实例

Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "my-stream-processing-application");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
StreamsBuilder builder = new StreamsBuilder();
KafkaStreams streams = new KafkaStreams(builder.build(), props);
  • props.put(StreamsConfig.APPLICATION_ID_CONFIG, "my-stream-processing-application") 指定流处理应用的唯一标识符。
  • props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092") 指定 Kafka 集群的开头地址。
  • StreamsBuilder builder = new StreamsBuilder() 创建 StreamsBuilder 实例,并用其构建 TOPOLOGY。

步骤二:定义输入与输出主题

final String inputTopic = "streams-input";
final String outputTopic = "streams-output";
KStream<String, String> inputStream = builder.stream(inputTopic);
KStream<String, String> outputStream = inputStream.mapValues(value -> value.toUpperCase());
outputStream.to(outputTopic);
  • builder.stream(inputTopic) 从名为 inputTopic 的主题中读取消息,返回类型为 KStream<String, String>
  • inputStream.mapValues(value -> value.toUpperCase())inputStream 中的每条消息进行处理并将结果写入 outputStream
  • outputStream.to(outputTopic)outputStream 中的所有消息写入名为 outputTopic 的主题。

步骤三:启动 Kafka Streams 实例

streams.start();
  • streams.start() 启动 Kafka Streams 实例,并开始处理消息。

4.2 事件日志监控案例

场景描述:

假设我们的后端服务正在每分钟以一个 JSON 对象形式向 Kafka 主题发出 HTTP 请求日志信息,其中数据格式为:

{"timestamp" : "2019-01-02T13:54:34.123Z","method": "GET","endpoint": "http://localhost:8080/api/v1/users","status_code": 200,"response_time': 23.4
}

现在我们需要实时地可视化用户请求日志,更新格式如下:

{“time”:2019-01-02 14:30:22,“users”: [{“Country”:CA, ”Count”: 60},{“Country”:US, “Count”: 38},{“Country”:CN, “Count”: 6},]
}

解决方案:

使用 Kafka Streams 构建一个流处理应用来预处理请求日志条目。根据所需对日志进行聚合和转换(比如按国家进行分组和计数),并将结果写出到名为 Kafka 主题的输出主题中。最后,一旦流处理应用中有新输出条目写出,就可以从输出主题中读取并使用任何可用于可视化的工具进行消费。

4.3 用户行为统计案例

场景描述:

假设我们正在使用 Kafka 主题从一个移动应用收集用户事件。每个事件都必须记录三个主要属性:事件发生的时间戳、时间戳对应的小时和用户类型。

{ "timestamp": 1517791088000, "hour_of_day": 7, "user_type": "bronze" 
}

现在,我们需要实时地聚合这些事件以了解用户行为,例如每小时访问的总用户数和所有不同金属等级的用户数。

解决方案:

使用 Kafka Streams 构建一个流处理应用,该应用将源主题中的事件作为输入,并组合输出结果到目标主题中。

KStream<String, String> input stream = builder.stream("user_events");
KTable<Windowed<String>, Long> hourlyUserCounts = inputstream.map((key, value) -> new KeyValue<>(parseTimestamp(value).toString("yyyyMMddHH"), value)).groupByKey().count(TimeWindows.of(Duration.ofHours(1)));
KTable<Windowed<String>, Long> userCountsByType = inputstream.groupByKey().count().groupBy((key, value) -> key.split(":")[0], Grouped.with(Serdes.String(), Serdes.Long())).reduce((v1, v2) -> v1 + v2);hourlyUserCounts.toStream().to("hourly_user_counts", Produced.with(stringSerde, longSerde):
userCountsByType.toStream().to("user_counts_by_type", Produced.with(stringSerde, longSerde));

以上示例代码将接收到的用户事件数据(即 user_events 主题中的消息)转换成 yyyyMMddHH 为时间窗口的键,然后进行聚合计数。最终,以类似方式对所有用户进行计数并编写将其写出到另外两个主题的代码。

五、性能优化

5.1 如何评估Kafka Streams应用的性能

评估Kafka Streams应用的性能需要关注以下几个方面:

5.1.1 吞吐量

吞吐量是指Kafka Streams应用在单位时间内处理的消息数量。可以通过以下指标来评估吞吐量:

  • 输入速率:Kafka集群每秒发送到Kafka Streams应用的消息数量。
  • 处理时延:从消息到达Kafka Streams应用到处理完成所需的时间。
  • 处理速率:Kafka Streams应用每秒处理的消息数量。

5.1.2 延迟

延迟是指Kafka Streams应用处理消息所需的时间。可以通过以下指标来评估延迟:

  • 最大延迟:Kafka Streams应用处理消息所需的最长时间。
  • 平均延迟:Kafka Streams应用处理消息所需的平均时间。

5.1.3 内存占用

内存占用是指Kafka Streams应用使用的内存数量。可以通过以下指标来评估内存占用:

  • 堆内存使用率:Java堆空间已使用的比例。
  • 非堆内存使用率:Java非堆空间已使用的比例。
  • GC时间:Java垃圾回收所需的时间。

5.2 优化并行度和吞吐量

为了提高Kafka Streams应用的并行度和吞吐量,可以采用以下优化方式:

5.2.1 调整线程池大小

Kafka Streams应用使用线程池处理消息,可以通过增加线程池大小来提高并行度和吞吐量。

// 创建线程池,指定线程池大小为10
ExecutorService executorService = Executors.newFixedThreadPool(10);// 提交任务到线程池
for (int i = 0; i < 1000; i++) {executorService.submit(new Runnable() {public void run() {// 处理消息的逻辑}});
}

5.2.2 调整partition数量

将topic划分成多个partition可以提高Kafka Streams应用的并行度和吞吐量。可以通过以下指令调整partition数量:

bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic mytopic --partitions 10

5.2.3 使用压缩算法

使用压缩算法可以减少Kafka Streams应用传输过程中的数据量,从而提高吞吐量和降低延迟。可以在Kafka Streams应用中配置压缩算法:

// 创建Streams配置对象
Properties streamsConfig = new Properties();
// 配置默认的压缩算法为gzip
streamsConfig.put(StreamsConfig.COMPRESSION_TYPE_CONFIG, "gzip");

5.3 实现数据压缩

为了在Kafka Streams应用中实现数据压缩,可以使用Gzip压缩算法对消息进行压缩和解压缩:

import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;public class GzipUtils {public static String compress(String str) {try {if (str == null || str.length() == 0) {return str;} else {ByteArrayOutputStream out = new ByteArrayOutputStream();GZIPOutputStream gzip = new GZIPOutputStream(out);gzip.write(str.getBytes());gzip.close();byte[] compressed = out.toByteArray();out.close();return Base64.getEncoder().encodeToString(compressed);}} catch (IOException e) {throw new RuntimeException(e);}}public static String uncompress(String str) {try {if (str == null || str.length() == 0) {return str;} else {byte[] compressed = Base64.getDecoder().decode(str);ByteArrayInputStream in = new ByteArrayInputStream(compressed);GZIPInputStream gzip = new GZIPInputStream(in);ByteArrayOutputStream out = new ByteArrayOutputStream();byte[] buffer = new byte[4096];int bytesRead = -1;while ((bytesRead = gzip.read(buffer)) > 0) {out.write(buffer, 0, bytesRead);}gzip.close();in.close();out.close();return new String(out.toByteArray(), "UTF-8");}} catch (IOException e) {throw new RuntimeException(e);}}}

使用示例:

// 压缩字符串
String compressedStr = GzipUtils.compress("hello world");
// 解压缩字符串
String uncompressedStr = GzipUtils.uncompress(compressedStr);

注释:以上代码实现了Gzip算法的压缩和解压缩功能。压缩时使用java.util.zip.GZIPOutputStream对消息进行压缩,解压缩时使用java.util.zip.GZIPInputStream对消息进行解压缩,并使用java.util.Base64对压缩后的字节数组进行编码和解码。

六、在生产中的应用

Kafka Streams是一个分布式流处理框架,能够轻松地处理实时数据。在生产中应用Kafka Streams时,需要注意以下几个方面。

6.1 高可用性集群部署

为了确保Kafka Streams在生产环境中的高可用性,我们需要将其部署在一个高可用性集群中。这意味着Kafka Streams需要有多个实例运行,即多个Kafka Streams应用程序实例。这些实例应该被分布在多个物理机或虚拟机上,以避免单点故障。

以下是一个基于Java的Kafka Streams高可用性集群部署示例:

    Properties properties = new Properties();properties.setProperty(StreamsConfig.APPLICATION_ID_CONFIG, "my-kafka-streams-app");properties.setProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");properties.setProperty(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());properties.setProperty(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());KafkaStreams streams = new KafkaStreams(topology, properties);streams.start();

6.2 监控和报警

在生产环境中,当Kafka Streams应用程序出现故障或异常时,我们需要及时得到通知并采取相应的措施。因此,对Kafka Streams进行监控是非常重要的。

例如,我们可以使用Kafka Streams提供的StreamsConfig.STATE_DIR_CONFIG属性将状态存储在本地文件系统中,以便在发生错误时进行还原。此外,我们还可以使用一些开源监控工具,如Prometheus和Grafana,来监控Kafka Streams应用程序的运行状况,并发送报警信息。

以下是一个基于Java的Kafka Streams监控和报警示例:

    Properties properties = new Properties();properties.setProperty(StreamsConfig.APPLICATION_ID_CONFIG, "my-kafka-streams-app");properties.setProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");properties.setProperty(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());properties.setProperty(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());properties.setProperty(StreamsConfig.STATE_DIR_CONFIG, "/tmp/kafka-streams");KafkaStreams streams = new KafkaStreams(topology, properties);streams.start();// 使用Prometheus和Grafana进行监控并发送报警信息MonitoringInterceptorUtils monitoringInterceptorUtils = new MonitoringInterceptorUtils();monitoringInterceptorUtils.register(streams);

6.3 日志管理

在生产环境中,我们需要对Kafka Streams应用程序的日志进行管理。如果我们不谨慎处理日志,那么将可能对性能产生负面影响,并导致无法排查问题。

为了管理Kafka Streams应用程序的日志,我们可以将其记录到文件或日志收集系统(如ELK或Graylog)中,以便更好地进行分析和调试。

以下是一个基于Java的Kafka Streams日志管理示例:

    Properties properties = new Properties();properties.setProperty(StreamsConfig.APPLICATION_ID_CONFIG, "my-kafka-streams-app");properties.setProperty(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");properties.setProperty(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());properties.setProperty(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());properties.setProperty(StreamsConfig.STATE_DIR_CONFIG, "/tmp/kafka-streams");KafkaStreams streams = new KafkaStreams(topology, properties);streams.start();// 将日志记录到文件中Appender fileAppender = RollingFileAppender.newBuilder().setName("fileLogger").setFileName("/tmp/kafka-streams.log").build();fileAppender.start();LoggerContext context = (LoggerContext) LogManager.getContext(false);Configuration config = context.getConfiguration();config.addAppender(fileAppender);AppenderRef ref = AppenderRef.createAppenderRef("fileLogger", null, null);AppenderRef[] refs = new AppenderRef[] {ref};LoggerConfig loggerConfig = LoggerConfig.createLogger(false, Level.INFO, "my.kafkastreams", "true", refs, null, config, null);loggerConfig.addAppender(fileAppender, null, null);config.addLogger("my.kafkastreams", loggerConfig);context.updateLoggers();

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

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

相关文章

FFmpeg 命令行实现居中高清上下模糊播放效果

FFmpeg 命令行实现居中高清上下模糊播放效果。 1、16:9 的横屏原视频&#xff0c;以 16:9 竖屏上下模糊播放 以该效果播放视频的命令如下&#xff1a; ffplay -i horizontal_test_video_169.mp4 -vf \ "split[a][b]; \ [a]crop(ih/16*9):ih,scaleiw/10:-1,gblursigma5…

分布式 - 负载均衡Nginx:Nginx常见问题总结(一)

文章目录 01. 什么是Nginx&#xff1f;02. 为什么要用Nginx?03. 为什么Nginx性能这么高&#xff1f;04. Nginx 为什么不使用多线程&#xff1f;05. Nginx 请求处理流程是什么&#xff1f;06. Nginx怎么处理请求的&#xff1f;07. 什么是正向代理和反向代理&#xff1f;08. 反向…

Python爬虫学习笔记(七)————Selenium

目录 1.什么是selenium&#xff1f; 2.为什么使用selenium&#xff1f; 3.selenium安装 4.selenium的使用步骤 5.selenium的元素定位 6.访问元素信息 7.交互 1.什么是selenium&#xff1f; &#xff08;1&#xff09;Selenium是一个用于Web应用程序测试的工具。 &#…

深度理解 Spring AOP

一、什么是AOP(面向切面编程)&#xff1f;&#x1f349; AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程&#xff0c;是通过预编译方式 和运行期 动态代理 实现程序功能的统一维护的一种技术。 AOP &#xff08;面向切面编程&#xff09;是 OOP&a…

代码随想录额外题目| 数组02 ●189旋转数组 ●724寻找数组中心索引

#189旋转数组 很快写出来但是用了个新数组&#xff0c;不好 void rotate(vector<int>& nums, int k) {vector<int> res(nums.size(),0);for(int i0;i<nums.size();i){int newiik;if(newi>nums.size()-1) newinewi%nums.size();res[newi]nums[i];}numsr…

【react】react18的学习(十二)– 底层原理(二)之 迭代器 iterator

迭代器iterator 是一种 ES6 规范&#xff0c;具有这种机制的数据结构才可以使用for of循环&#xff1a;返回每一项的值&#xff1b; 原型链具有Symbol.iterator属性的数据结构都具备&#xff1b;如数组、部分类数组、字符串等&#xff1b; 普通对象就不能用&#xff1b; for-…

vue组件(个人学习笔记三)

目录 友情提醒第一章、vue的组件1.1&#xff09;什么是SPA应用1.2&#xff09;vue的组件简介1.3&#xff09;vue工程中的main.js文件 第二章、Vue组件的使用2.1&#xff09;一般组件的自定义2.2&#xff09;注册组件&#xff1a;全局注册和局部注册2.2.1&#xff09;全局注册&a…

和chatgpt学架构04-路由开发

目录 1 什么是路由2 如何设置路由2.1 安装依赖2.2 创建路由文件2.3 创建首页2.4 编写HomePage2.5 更新路由配置2.6 让路由生效 3 测试总结 要想使用vue实现页面的灵活跳转&#xff0c;其中路由配置是必不可少的&#xff0c;我们在做开发的时候&#xff0c;先需要了解知识点&…

Vue--》打造个性化医疗服务的医院预约系统(二)

今天开始使用 vue3 + ts 搭建一个医院预约系统的前台页面,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GithHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关…

基于Python+多层RNN+Tensorflow藏头诗与歌词智能生成-深度学习算法应用(含全部工程源码)+训练数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境Tensorflow 环境PyCharm环境 模块实现古诗生成1. 数据预处理2. 模型构建3. 模型训练及保存4. 使用模型生成古诗5. 产生藏头诗6. 用词云展示生成的古诗 歌词生成1. 数据预处理2. 模型构建3. 模型训练并保存4. 生成…

基于Kitti数据集的智能驾驶目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于Kitti数据集的智能驾驶目标检测系统可用于日常生活中检测与定位行人&#xff08;Pedestrian&#xff09;、面包车&#xff08;Van&#xff09;、坐着的人&#xff08;Person Sitting&#xff09;、汽车&#xff08;Car&#xff09;、卡车&#xff08;Truck…

【字符流】案例:文件到集合

案例&#xff1a;文件到集合 1.需求&#xff1a; 把文本文件中的数据读取到集合&#xff0c;并遍历集合。要求&#xff1a;文件中的每一行数据是一个集合元素 2.思路 创建字符缓冲输入流对象创建ArrayList集合对象调用字符缓冲输入流对象的方法读数据把读取到的字符串数据存…

Java IO

stream 流 是一种抽象概念&#xff0c;它代表了数据的无结构化传递。按照流的方式进行输入输出&#xff0c;数据被当成 无结构的字节序或字符序列。从流中取得数据的操作称为提取操作&#xff0c;而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO 流。换…

通过Docker+frp实现内网穿透

通过Dockerfrp实现内网穿透 这篇文章是最近又折腾了一个局域网的小网站&#xff0c;在这边记录一下内网穿透的方法&#xff0c;还是老方法frp&#xff0c;只不过之前用的是.start.sh&#xff0c;这次使用的是Docker 配置内网穿透一定要有一个公网服务器&#xff0c;也就是能访问…

spring复习:(50)@Configuration注解配置的singleton的bean是什么时候被创建出来并缓存到容器的?

一、主类&#xff1a; 二、配置类&#xff1a; 三、singleton bean的创建流程 运行到context.refresh(); 进入refresh方法&#xff1a; 向下运行到红线位置时&#xff1a; 会实例化所有的singleton bean.进入finisheBeanFactoryInitialization方法&#xff1a; 向下拖动代…

JavaWeb课程设计项目实战(06)——项目编码实践3

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 在本教程教程中&#xff0c;我们实现学生列表的显示。 Student 请在bean包下创建Student类&#xff0c;代码如下&#xff1a; package com.cn.bean; /*** 本文作者&#…

数据科学团队的角色分工

描述数据科学团队中角色分工常用下列维度。进一步以数据可视化直观表达的能力雷达图: ML Ops - 机器学习运维 Data Pipelines - 数据流水线 Database - 数据库 Data Viz - 数据可视化 Storytelling - 数据讲故事 Business Insights - 业务洞察 Reporting - 报告 Experimentatio…

【100天精通python】Day10:函数的创建和调用,参数传递,返回值,变量作用域以及匿名函数

目录 1. 函数的创建和调用 1.1 函数的创建 1.2 调用函数 2 参数传递 2.1 传递方式 2.2 形参和实参 2.3 位置参数 2.4 关键字参数 2.5 可变参数 2.6 为参数设置默认值 3 返回值 4 变量的作用域 4.1 局部变量 4.2 嵌套变量 4.3 全局变量 5 匿名函数&#xff0…

X86设备启动过程

文章目录 一、电源自检二、BIOS自检三、引导设备选择四、主引导记录4.1 0x7c0 五、加载操作系统 x86计算机启动过程&#xff0c;主要分为这几个阶段&#xff1a;电源自检、BIOS自检、引导设备的选择、主引导记录、加载操作系统。 一、电源自检 当我们按下开关键后&#xff0c;…

uni-app image加载错误 404 替换为默认图片

双层v-for 使用item修改 aitem.cat_icon || defaultPic绑定图片src属性为aitem.cat_icon 如果aitem.cat_icon的值为空字符串或undefined&#xff0c;那么默认图片defaultPic被显示出来当图片加载错误时,触发handleImageError方法,将aitem传进去 <!-- 页面--><view …