引言

在分布式消息系统中,请求处理机制是连接客户端与服务端的"神经中枢"。无论是生产者发送消息、消费者拉取数据,还是集群内部的元数据同步,都依赖于高效的请求处理流程。Apache Kafka作为高性能消息队列的代表,其请求处理机制经过多版本迭代,形成了一套兼顾吞吐量与可靠性的成熟架构。

Kafka的客户端(生产者、消费者)与Broker之间、Broker与Broker之间的所有交互,均通过请求/响应(Request/Response)模式完成。从客户端视角看,常见的请求包括生产消息的PRODUCE请求、消费消息的FETCH请求、获取集群信息的METADATA请求等;从Broker视角看,还存在大量内部请求,如副本同步的FETCH请求、Leader选举的LeaderAndIsr请求等。总之,Kafka定义了很多类似的请求格式,而这些请求均通过TCP网络以Socket的方式进行通讯的

理解Kafka的请求处理流程,不仅能帮助我们排查生产环境中的性能瓶颈(如请求超时、处理延迟),更能为集群调优提供理论依据。本文将从请求处理的基本方案入手,深入剖析Kafka基于Reactor模式的实现细节,详解延时请求处理机制,并探讨数据类与控制类请求的分离设计,最终给出实用的优化建议。

请求处理的基础方案:从 naive 到高效

在设计请求处理机制时,最简单的思路往往存在明显缺陷。Kafka在演进过程中,摒弃了两种基础方案,最终选择了Reactor模式。理解这两种方案的局限性,能更好地体会Kafka设计的精妙之处。

方案一:顺序处理请求

顺序处理是最直观的方案:Broker以串行方式接收并处理请求,一个请求处理完毕后再处理下一个。

其伪代码如下:

while (true) {Request request = accept(connection);  // 接收请求handle(request);                       // 处理请求
}

优点:实现简单,无需考虑线程安全问题。

缺陷:吞吐量极低。每个请求必须等待前一个请求完成,无法利用多核CPU资源,在高并发场景下会导致请求堆积,延迟飙升。

这种方案仅适用于请求频率极低的场景(如单机工具类应用),完全无法满足Kafka的高吞吐需求。

方案二:每个请求一个线程

为解决顺序处理的性能问题,另一种方案是为每个请求创建独立线程异步处理,伪代码如下:

while (true) {Request request = accept(connection);  // 接收请求new Thread(() -> handle(request)).start();  // 新线程处理
}

优点:并发处理请求,吞吐量较顺序处理有显著提升。

缺陷:资源消耗极大。线程创建与销毁的开销昂贵,在高并发下(如每秒数万请求),线程数量会急剧膨胀,导致CPU上下文切换频繁、内存占用过高,甚至可能压垮整个服务。

这种方案适用于请求频率低、处理逻辑复杂的场景,但仍不符合Kafka的高性能需求。

为何选择Reactor模式?

Kafka最终采用Reactor模式,其核心思想是事件驱动+线程池:通过一个或多个线程监听事件(如请求到达),并将事件分发到工作线程池处理。

这种模式的优势在于:

  1. 高效利用线程资源:通过线程池复用线程,避免频繁创建销毁线程的开销。

  2. 支持高并发:事件驱动模型可同时处理大量连接,适合Kafka的多客户端场景。

  3. 灵活扩展:可根据请求类型和系统负载动态调整线程池大小。

Reactor模式是高性能网络编程的经典范式,被广泛应用于Netty、Nginx等框架,Kafka对其进行了针对性优化,形成了独特的请求处理架构。

Kafka的Reactor模式实现:从请求接收到响应返回

Kafka的Broker端请求处理架构基于Reactor模式扩展,主要包含SocketServerAcceptor线程网络线程池IO线程池等组件。这些组件协同工作,完成请求的接收、分发、处理与响应。

核心组件与职责

1. SocketServer:请求处理的"总调度室"

SocketServer是Kafka Broker处理网络请求的入口组件,负责管理所有网络连接和线程资源。它包含两个关键部分:

  • Acceptor线程:监听客户端连接,接收新请求并分发到网络线程。

  • 网络线程池:处理请求的初步解析,将请求放入共享队列。

2. Acceptor线程:请求分发的"交通警察"

Acceptor线程是单线程的,其主要职责是:

  • 监听指定端口(如默认9092)的TCP连接请求。

  • 通过轮询(Round-Robin)策略将请求公平地分发到网络线程池中的线程,避免请求处理倾斜。

轮询策略的优势在于实现简单且公平,确保每个网络线程处理的请求量大致均衡。

3. 网络线程池:请求的"初步处理中心"

网络线程池由num.network.threads参数控制(默认3个线程),其职责包括:

  • 从Acceptor线程接收请求,进行初步解析(如验证请求格式)。

  • 将解析后的请求放入共享请求队列,等待IO线程处理。

  • 接收IO线程返回的响应,并将其发送回客户端。

网络线程不执行具体的业务逻辑,仅负责请求的转发,因此非常轻量。

4. IO线程池:请求处理的"主力部队"

IO线程池由num.io.threads参数控制(默认8个线程),是执行请求处理逻辑的核心组件:

  • 从共享请求队列中取出请求,执行具体处理(如PRODUCE请求写入磁盘、FETCH请求读取数据)。

  • 处理完成后,将响应放入对应网络线程的响应队列

  • 对于无法立即处理的请求(如延时请求),将其暂存到Purgatory组件。

IO线程直接操作磁盘和页缓存,其数量应根据CPU核心数和IO密集程度调整(如SSD可适当增加线程数)。

5. 共享请求队列与响应队列:请求流转的"缓冲区"

  • 共享请求队列:所有网络线程共享的队列,用于暂存待处理的请求。其作用是平衡网络线程与IO线程的处理速度,避免IO线程忙碌时网络线程阻塞。

  • 响应队列:每个网络线程专属的队列,用于存放IO线程返回的响应。由于每个请求由固定的网络线程负责回传,响应队列无需共享,减少了线程同步开销。

请求处理全流程:以PRODUCE请求为例

假设生产者发送一条消息到Kafka Broker,请求处理流程如下:

  1. 请求接收:Acceptor线程监听端口,接收到PRODUCE请求后,通过轮询策略将其分配给某个网络线程。

  2. 初步解析:网络线程解析请求内容(如主题、分区、消息体),验证格式合法性后,将请求放入共享请求队列。

  3. 业务处理:IO线程从共享队列取出请求,执行消息写入逻辑:

    • 检查分区Leader副本是否在当前Broker。

    • 将消息追加到分区的日志文件(先写入页缓存,再异步刷盘)。

    • 若设置acks=all,等待ISR中所有副本同步完成。

  4. 响应生成:IO线程处理完成后,生成包含"成功/失败"状态的响应,放入对应网络线程的响应队列。

  5. 响应发送:网络线程从响应队列取出响应,通过TCP连接发送回生产者客户端。

整个流程通过多线程协作实现了高并发处理,每个组件专注于单一职责,避免了资源竞争和性能瓶颈。

线程池参数调优建议

Kafka的请求处理性能与线程池参数密切相关,合理配置可显著提升吞吐量:

  • num.network.threads:默认3,建议根据客户端连接数调整。连接数多时(如 thousands)可增至5-8。

  • num.io.threads:默认8,建议设置为CPU核心数的1-2倍(如16核CPU可设为16-32)。IO密集型场景(如机械硬盘)可适当增加,CPU密集型场景(如压缩消息处理)可减少。

调优原则:通过压测观察线程使用率(如通过JMX监控kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent),确保线程不过载(使用率70%以下为宜)。

延时请求处理:Purgatory组件的"等待艺术"

并非所有请求都能被立即处理,某些请求需要满足特定条件后才能完成(如acks=all的PRODUCE请求需等待所有ISR副本同步)。Kafka通过Purgatory组件(意为"炼狱")管理这类延时请求,确保其在条件满足时被唤醒处理。

延时请求的典型场景

  1. acks=all的PRODUCE请求:需等待ISR中所有副本确认接收消息,若副本同步未完成,请求被暂存到Purgatory。

  2. 带条件的FETCH请求:消费者设置fetch.min.bytes(如1KB),若服务器缓存的消息不足,请求会被延迟处理,直到数据量满足条件或超时。

  3. LeaderAndIsr请求:副本同步过程中,若Leader副本未就绪,请求会被暂时挂起。

Purgatory的工作原理

Purgatory本质是一个延时请求管理器,其核心机制包括:

  1. 请求暂存:当请求无法立即处理时,IO线程将其放入Purgatory的优先级队列(按超时时间排序)。

  2. 条件监听:Purgatory为每个请求注册触发条件(如ISR副本同步完成、消息量达标)。

  3. 定时检查与唤醒:Purgatory定期(默认100ms)检查队列中的请求,若条件满足或超时,将请求取出并交由IO线程继续处理。

  4. 响应生成:处理完成后,响应被放入网络线程的响应队列,最终返回给客户端。

这种机制避免了IO线程的阻塞等待,提高了线程利用率。例如,对于acks=all的请求,IO线程无需原地等待副本同步,而是将请求交给Purgatory后继续处理其他请求,待同步完成后再唤醒处理。

延时请求的超时配置

延时请求的最大等待时间由相关参数控制,例如:

  • request.timeout.ms:客户端设置的请求超时时间(默认30秒),若超过此时长请求未完成,客户端会认为失败。

  • replica.lag.time.max.ms:副本同步的最大延迟时间(默认10秒),影响ISR集合调整,间接影响acks=all请求的处理。

合理设置超时参数可平衡可靠性与延迟:核心业务可适当延长超时时间(如60秒),非核心业务可缩短(如10秒)以快速失败。

数据类与控制类请求:分离处理的必要性

Kafka的请求按功能可分为数据类请求控制类请求,两者特性差异显著,需要不同的处理策略。社区在2.3版本实现了两类请求的分离处理,解决了此前混合处理的性能问题。

两类请求的核心差异

类型示例特点处理优先级
数据类请求PRODUCE、FETCH频率高、处理耗时(IO密集)、影响用户体验
控制类请求LeaderAndIsr、StopReplica频率低、处理快(CPU密集)、影响集群稳定性

控制类请求虽然数量少,但直接影响集群元数据(如Leader副本切换、副本下线),若处理不及时,可能导致数据类请求失效或做无用功。

混合处理的问题:控制类请求被"饿死"

在2.3版本之前,两类请求共用一套处理组件,可能出现以下问题:

  1. 控制类请求延迟:当Broker积压大量PRODUCE请求时,LeaderAndIsr等控制类请求需排队等待,导致集群状态更新不及时。例如,Leader副本故障后,新Leader选举的请求被延迟,会造成分区长时间不可用。

  2. 数据类请求无效化:若控制类请求(如LeaderAndIsr)处理延迟,期间处理的PRODUCE请求可能因Leader切换而失效,导致客户端重试,浪费资源。

  3. 主题删除卡顿:删除主题时,StopReplica请求若被数据类请求阻塞,会导致主题删除操作长时间无响应。

分离处理的实现:两套组件,独立端口

为解决上述问题,Kafka 2.3版本引入了请求分离机制,核心设计是:

  1. 两套独立组件:为数据类和控制类请求分别创建网络线程池和IO线程池,避免资源竞争。

  2. 独立端口监听:通过listeners配置不同端口(如9092处理数据请求,9093处理控制请求),客户端根据请求类型连接对应端口。

  3. 隔离处理流程:两类请求的接收、解析、处理完全隔离,控制类请求无需等待数据类请求完成。

这种设计确保了控制类请求的优先处理,提升了集群的稳定性和响应速度。

为何不采用优先级队列?

社区曾考虑过用优先级队列(控制类请求优先级高)处理两类请求,但最终否决,原因是:

  1. 队列满时失效:当请求队列已满,即使控制类请求优先级高,也无法放入队列,仍会被阻塞。

  2. 实现复杂:优先级队列需要额外的同步机制,可能引入性能开销。

  3. 隔离性不足:共用队列仍可能受数据类请求的突发流量影响。

相比之下,两套组件的方案虽然增加了资源占用,但实现简单、隔离性强,更符合Kafka的设计哲学。

实战:请求处理相关的问题排查与优化

理解请求处理机制后,可针对性地排查生产环境中的常见问题,并通过参数调优提升性能。

常见问题与解决方案

1. 请求超时(Request Timeout)

现象:客户端频繁报TimeoutException,如"Timeout after 30000ms of waiting for the response"。 可能原因

  • 网络线程或IO线程池过载,请求处理延迟。

  • 共享请求队列满,新请求无法入队。

  • 延时请求在Purgatory中等待超时。

解决方案

  • 监控线程池使用率(如kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent),若空闲率低(<20%),增加num.network.threadsnum.io.threads

  • 调整queue.buffering.max.bytes增大请求队列容量(默认64MB)。

  • 检查Purgatory中延时请求的触发条件(如ISR副本是否正常同步)。

2. 处理延迟飙升(High Request Latency)

现象:请求平均处理时间(如kafka.network:type=RequestMetrics,name=TotalTimeMs,request=Produce)突然增大。 可能原因

  • IO线程处理瓶颈(如磁盘IO繁忙)。

  • 大量延时请求占用Purgatory资源。

  • 控制类请求与数据类请求混合处理导致阻塞(2.3版本前)。

解决方案

  • 更换更快的存储介质(如机械硬盘换SSD)。

  • 减少acks=all的使用场景,或降低min.insync.replicas

  • 升级Kafka至2.3+版本,启用请求分离机制。

3. 连接数过高(Too Many Connections)

现象:Broker报"too many open files"错误,或netstat显示大量ESTABLISHED连接。 可能原因

  • 客户端未正确关闭连接,导致连接泄漏。

  • 网络线程数不足,无法及时处理连接释放。

解决方案

  • 客户端设置合理的connections.max.idle.ms(默认9分钟),自动关闭闲置连接。

  • 增加num.network.threads,提高连接处理能力。

  • 调整操作系统文件描述符限制(如ulimit -n 65535)。

性能优化最佳实践

  1. 线程池参数调优

    • 网络线程数:根据客户端连接数调整,每1000个连接对应1-2个线程。

    • IO线程数:设置为CPU核心数的1-1.5倍,避免线程过多导致的上下文切换。

  2. 请求队列配置

    • queued.max.requests:控制共享请求队列的最大请求数(默认500),过小可能导致请求被拒绝,过大会增加内存占用,建议根据内存大小调整(如1000-2000)。

  3. 分离数据与控制请求

    • 升级至Kafka 2.3+,配置listeners分离端口(如PLAINTEXT://:9092,CONTROL://:9093),并通过inter.broker.listener.name指定控制请求端口。

  4. 监控关键指标

    • 请求吞吐量:kafka.network:type=RequestMetrics,name=RequestsPerSec

    • 平均处理时间:kafka.network:type=RequestMetrics,name=TotalTimeMs

    • 线程池空闲率:kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent

总结

Kafka的请求处理机制是其高性能、高可靠性的基石,通过Reactor模式实现了高并发处理,通过Purgatory组件管理延时请求,通过请求分离机制保障了集群稳定性。

其设计理念可总结为:

  1. 职责分离:Acceptor、网络线程、IO线程各司其职,避免功能耦合。

  2. 异步非阻塞:通过事件驱动和线程池,最大化利用系统资源。

  3. 动态调整:线程池大小、队列容量等参数可根据负载动态优化。

  4. 隔离优先:数据类与控制类请求分离,确保核心控制流程不受业务流量影响。

  • Acceptor线程:采用轮询的方式将入站请求公平地发到所有网络线程中。
  • 网络线程池:处理数据类请求。网络线程拿到请求后,将请求放入到共享请求队列中。
  • IO线程池:处理控制类请求。从共享请求队列中取出请求,执行真正的处理。如果是PRODUCE生产请求,则将消息写入到底层的磁盘日志中;如果是FETCH请求,则从磁盘或页缓存中读取消息。
  • Purgatory组件:用来缓存延时请求。延时请求就是那些一时未满足条件不能立刻处理的请求。

理解这套机制,不仅能让我们更好地使用Kafka,更能为分布式系统的请求处理设计提供借鉴。在实际应用中,需结合业务场景合理配置参数,并通过监控及时发现并解决性能瓶颈,让Kafka在高并发场景下持续稳定运行。

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

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

相关文章

区块链技术如何确保智能合约的安全性和可靠性?

智能合约作为区块链上自动执行的可编程协议&#xff0c;其安全性和可靠性直接决定了区块链应用的信任基础。区块链通过底层技术架构、密码学工具和机制设计的多重保障&#xff0c;构建了智能合约的安全防线。以下从技术原理、核心机制和实践保障三个维度展开分析&#xff1a;一…

2020 年 NOI 最后一题题解

问题描述2020 年 NOI 最后一题是一道结合图论、动态规划与状态压缩的综合性算法题&#xff0c;题目围绕 "疫情期间的物资配送" 展开&#xff0c;具体要求如下&#xff1a;给定一个有向图 G (V, E)&#xff0c;其中节点代表城市&#xff0c;边代表连接城市的道路。每个…

加密与安全

目录 一、URL编码&#xff1a; 二、Base64编码&#xff1a; 三、哈希算法&#xff1a; 四、Hmac算法&#xff1a; 五、对称加密算法&#xff1a; 一、URL编码&#xff1a; URL编码是浏览器发送数据给服务器时使用的编码&#xff0c;它通常附加在URL的参数部分。之所以需要…

EasyExcel 公式计算大全

EasyExcel 是基于 Apache POI 的封装&#xff0c;主要专注于简化 Excel 的读写操作&#xff0c;对于公式计算的支持相对有限。以下是 EasyExcel 中处理公式计算的全面指南&#xff1a;1. 基本公式写入1.1 写入简单公式Data public class FormulaData {ExcelProperty("数值…

2025年AI+数模竞赛培训意见征集-最后一轮

在过去几天的“AI时代下2025年数模竞赛培训课程需求调研紧急征集”我们收到了大量老师、学生的反馈。我们通过大家的实际需求&#xff0c;编写了下述2025年AI时代下最新的数学建模竞赛教学课程课程表&#xff0c;具体授课内容以及相关课件、支撑材料都将会免费发布&#xff0c;…

Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗

Qwen2 RotaryEmbedding 位置编码仅仅是第一层有吗,还是全部层都有 Qwen2 模型中的 Rotary Embedding(旋转位置编码)是应用于所有 Transformer 层 的,而非仅第一层。 1. Transformer 架构的核心逻辑 Qwen2 基于 Decoder-only Transformer 架构,而位置编码(如 Rotary Emb…

CNN卷积神经网络之LeNet和AlexNet经典网络模型(三)

CNN卷积神经网络之LeNet和AlexNet经典网络模型&#xff08;三&#xff09; 文章目录CNN卷积神经网络之LeNet和AlexNet经典网络模型&#xff08;三&#xff09;深度学习两大经典 CNN 模型速览1. LeNet-5&#xff1a;CNN 的开山之作&#xff08;1998&#xff09;2. AlexNet&#…

江协科技STM32 12-2 BKP备份寄存器RTC实时时钟

这一节我们要讲的主要内容是RTC实时时钟&#xff0c;实时时钟本质上是一个定时器&#xff0c;但是这个定时器是专门用来产生年月日时分秒&#xff0c;这种日期和时间信息的。所以学会了STM32的RTC就可以在STM32内部拥有一个独立运行的钟表。想要记录或读取日期和时间&#xff0…

【10】大恒相机SDK C++开发 ——对相机采集的原图像数据IFrameData裁剪ROI 实时显示在pictureBox中,3种方法实现(效率不同)

文章目录1 在回调函数中实现2 独立封装调用2.1 获取图像宽、高、pBuffer、channel2.2 内存图像数据截取ROI并显示2.3 回调函数调用3 for循环嵌套 方法24 for循环嵌套 方法35 按行复制数据提高效率&#xff0c;但很耗内存6 unsafe代码 解释及注意事项 看我另一篇文章7 ConvertTo…

ubuntu22.04系统入门 linux入门(二) 简单命令 多实践以及相关文件管理命令

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址&#xff1a;星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 之所以推荐给大家使用&#xff0c;是因为上面的云主机目前是免费使用的…

分布式ID方案(标记)

一、参考文章-标记 分布式ID方案有哪些&#xff1f;雪花算法如何搞定时钟回拨和动态机器ID&#xff1f; 二、应用 1.百度 uid-generator github项目地址 原理参考 2.百度 uid-generator 扩展应用 灯官网 灯 项目代码 lamp-util 单元模块 lamp-util 单元模块子模块 lamp-…

std::map 加锁

在并发环境下使用std::map&#xff0c;必须采取同步措施。 在并发环境下对 std::map 进行不加锁的读写操作会导致严重的线程安全问题&#xff0c;主要会产生以下几种问题&#xff1a; ⚠️ 主要风险与后果数据竞争&#xff08;Data Race&#xff09; 当多个线程同时修改同一个键…

学习笔记090——Ubuntu 中 UFW 防火墙的使用

文章目录1、允许特定的端口访问2、允许特定 IP 访问某个端口3、允许某个范围的端口4、查看 UFW 状态5、重新加载 UFW6、启用 UFW7、关闭 UFW1、允许特定的端口访问 # 允许 TCP 端口&#xff08;例如 80&#xff09;&#xff1a; sudo ufw allow 80/tcp# 允许 UDP 端口&#xf…

移动端 WebView 内存泄漏与性能退化问题如何排查 实战调试方法汇总

在混合 App 应用中&#xff0c;WebView 页面常承载复杂业务逻辑与交互。随着用户使用时间增长&#xff0c;特别在切换多个页面或反复打开界面后&#xff0c;常常会出现性能下降、页面卡顿、甚至白屏崩溃等现象。这通常是因为页面存在内存泄漏、事件监听未解绑或垃圾回收阻塞导致…

JSON 对象在浏览器中顺序与后端接口返回不一致的问题

一、问题描述 后端接口返回一个字典表的JSON对象&#xff0c;页面展示排序与预期排序不一致。 在浏览器调试面板Response中看到接口原始响应字符串&#xff0c;是期望顺序&#xff1a;在Preview中看到&#xff0c; key “22” 被提到最前&#xff0c;顺序发生变化&#xff1a;页…

Spring MVC数据传递全攻略

Spring MVC数据传递一、前端到后端的数据传递1. 使用 RequestParam 传递简单参数2. 使用 PathVariable传递路径参数3. 使用RequestBody传递 JSON 数据二、后端到前端的数据传递1. 使用Model或 ModelAndView传递数据到前端2. 使用HttpServletResponse直接写回数据3.使用Response…

仓库管理系统-12-前端之头部区域Header基于嵌套路由访问个人中心

文章目录 1 个人中心 1.1 DateUtils.vue(子组件) 1.2 Home.vue(父组件) 1.3 router/index.js(嵌套路由) 1.4 index.vue(路由占位符) 2 Header.vue 2.1 页面布局 2.2 toUser方法 2.3 初始加载 2.4 Header.vue 头部区域Header中有一个个人中心下拉菜单,点击个人中心选项,通过嵌…

【智能协同云图库】第七期:基于AI调用阿里云百炼大模型,实现AI图片编辑功能

摘要&#xff1a;AI 高速发展赋能传统业务&#xff0c;图库网站亦有诸多 AI 应用空间。以 AI 扩图功؜能为例&#xff0c;让我们来学习如何在项目⁠中快速接入 AI 绘图大模型。‏用户可以选择一张已上传的图片&#xff0c;‌通过 AI 扩图得到新的图片&#xff0c;希望可以帮到大…

Notepad++插件安装

方式一&#xff1a;自动安装&#xff08;有些notepad并不好用&#xff0c;推荐方式二&#xff09;工具栏-》插件-》插件管理如下点击安装后会提示&#xff0c;后端安装&#xff0c;安装成功后自动启动&#xff0c;本人使用的v8.6.4的版本&#xff0c;插件基本都无法自动安装&am…

git pull和git fetch的区别

git pull和git fetch是git版本控制系统中的两个基本命令&#xff0c;它们都用于从远程仓库更新本地仓库的信息&#xff0c;但执行的具体操作不同。git fetch:git fetch下载远程仓库最新的内容到你的本地仓库&#xff0c;但它并不自动合并或修改你当前的工作。它取回了远程仓库的…