Hadoop 2.x设计理念解析

目录

一、背景

二、整体架构

三、组件详解

3.1 yarn

3.2 hdfs

四、计算流程

4.1 上传资源到 HDFS

4.2  向 RM 提交作业请求

4.3 RM 调度资源启动 AM

4.4 AM运行用户代码

4.5 NodeManager运行用户代码

4.6 资源释放

五、设计不足


一、背景

有人可能会好奇,为什么要学一个十年前的东西呢?

Hadoop 2.x虽然是十年前的,但hadoop生态系统中的一些组件如今还在广泛使用,如hdfs和yarn,当今流行spark和flink都依赖这些组件

通过学习它们的历史设计,首先可以让我们对它们的了解更加深刻,通过了解软件的演变的过程也能对我们改进自有的系统做启发

之前我们分析了Hadoop 1.x Hadoop 1.x设计理念解析-CSDN博客,说明了其中的一些问题,现在来看2.x

二、整体架构

从网上找了一张图:

yarn统计集群资源情况,分配资源Container给hadoop使用

hdfs作为数据中转,负责jar包、中间数据的中转

图中没体现yarn和hdfs高可用的实现,具体高可用实现会在下面的组件详解中提及

三、组件详解

强烈建议:刚了解这块的看组件详解可能没那么好理解,建议直接看下面的第四章计算流程,看完计算流程后,有一个大概的了解了,再来学习组件详解

3.1 yarn

1. ResourceManager(RM)

核心职责

  • 全局资源管理:管理整个集群的资源(CPU、内存等),负责资源分配和调度。

  • 应用生命周期管理:接收客户端提交的应用请求,启动 ApplicationMaster(AM),监控应用状态。

  • 高可用支持:通过主备(Active/Standby)架构避免单点故障,依赖 ZooKeeper 实现自动故障转移。

子模块

  • Scheduler(调度器)

    • 纯调度器,仅负责资源分配(不跟踪应用状态)。

    • 支持多种调度策略:容量调度(Capacity Scheduler)、公平调度(Fair Scheduler)。

  • ApplicationsManager

    • 管理应用提交、启动 AM、记录应用元数据(如用户、队列信息)。

高可用机制

  • 主备 RM:通过 ZooKeeper 选举 Active RM,状态持久化到 HDFS 或 ZooKeeper。

  • 快速故障切换:Standby RM 在 Active RM 宕机后秒级接管。

2. NodeManager(NM)

核心职责

  • 单节点资源管理:管理单个物理节点上的资源(如 CPU、内存、磁盘),向 RM 汇报资源状态。

  • 容器(Container)生命周期管理

    • 启动、监控、销毁容器(Container)。

    • 执行来自 AM 的任务指令(如启动 Map/Reduce 任务)。

  • 本地化服务:缓存应用依赖的 JAR 包、配置文件等,加速任务启动。

关键机制

  • 心跳机制:定期向 RM 发送心跳,汇报节点资源使用情况和容器状态。

  • 资源隔离:通过 Linux Cgroups 或 Docker 实现 CPU、内存隔离,避免任务间资源争抢。

  • 健康检查:监控节点硬件(如磁盘损坏、内存不足),异常时主动报告 RM。

3. Container(容器)

核心概念

  • 资源封装单元:代表集群中可分配的资源(如 2 CPU 核心 + 4GB 内存)。

  • 任务执行环境:在 NM 上启动的进程,运行具体任务(如 MapTask、ReduceTask)。

4. ApplicationMaster(AM是Container的一种)

核心职责

  • 应用级资源协商:向 RM 申请资源(Container),并协调任务的执行。

  • 任务容错:监控任务状态,失败时重新申请资源并重试。

  • 应用进度汇报:向 RM 报告应用进度(如 MapReduce 的 Map 完成百分比)。

特点

  • 应用专属:每个应用(如 MapReduce 作业、Spark 作业)有独立的 AM。

  • 灵活性:AM 由用户程序实现(如 MapReduce 的 MRAppMaster),支持自定义资源请求策略。

容错机制

  • RM 托管状态:AM 定期向 RM 发送心跳,RM 故障切换后重启 AM 并恢复状态。

  • 检查点(Checkpoint):部分框架(如 Flink)支持将状态持久化到 HDFS,故障后从检查点恢复。

3.2 hdfs

在Hadoop 1.x 架构中,hdfs的NameNode(NM)只有一个,当NameNode挂了之后,容易造成数据丢失,所以在Hadoop 2.x架构中,NameNode变成了多个,通过zk进行选主,架构如下:

可以看到上图中除了NameNode,还有JournalNodes(JN)

JournalNodes集群用于存储NameNode的EditLog(记录文件系统元数据变更的日志)

JN的作用是保证NM的master和stand by 之间的数据一致性,因为NameNode多了之后,主备之间需要数据同步,一条NameNode EditLog变更内容,需要被需确保大多数节点(Quorum)成功写入,才算变更成功,Standby NameNode定期从JournalNodes读取EditLog,并应用到自身内存中的元数据(FsImage)

这样当主NM挂了之后,不会因为数据变更没及时同步给stand by节点,导致数据丢失

四、计算流程

4.1 上传资源到 HDFS

  • 客户端将作业的 JAR 包、配置文件(如 mapred-site.xml)和输入数据分片(InputSplit)上传到 HDFS

  • 例如,JAR 包会被上传到 HDFS 路径 /user/hadoop/jobs/myjob.jar

4.2  向 RM 提交作业请求

  • 客户端通过 YARN RPC 协议 向 RM 提交作业请求,包含以下信息:

    • ApplicationMaster(AM)的入口类(如 org.apache.hadoop.mapreduce.v2.app.MRAppMaster)。

    • HDFS 上的资源路径(JAR、配置文件等)。

    • 作业配置参数(如 Map/Reduce Task 的内存、CPU 需求)。

4.3 RM 调度资源启动 AM

  • RM 根据集群资源状态,选择一个 NodeManager(NM)节点,分配一个 Container(初始资源,如 1GB 内存、1 核 CPU)。

  • 向该 NM 发送指令,启动 AM 进程。

Q:Container的本质是什么?

在hadoop、spark、flink情况下,Container是一个JVM进程

Q:Container 什么时候被创建?

Q:NM如何启动的Container?

通过类似如下代码:

java \
-Djava.net.preferIPv4Stack=true \
-Xmx1024m \
-Djava.io.tmpdir=/tmp/hadoop-tmp \
-Dlog4j.configuration=container-log4j.properties \
-Dyarn.app.container.log.dir=<日志目录路径> \
-classpath <Hadoop类路径>:<用户Jar路径> \
org.apache.hadoop.mapred.YarnChild \
<作业ID> <任务ID> <用户类名>

根据如上代码可以看到,实际入口是Hadoop的YarnChild,而非用户类,通过反射机制实例化用户编写的Mapper/Reducer,每个Task在独立JVM中运行,避免相互影响

Q:NM如何限制jvm进程的内存和cpu核数?

内存通过xmx限制,cpu核数通过linux指令限制

Q:NodeManager 怎么来的,用户在机器上启动的应用么?

  1. 集群管理工具:如 Apache Ambari、Cloudera Manager 等工具统一部署和启动。

  2. 手动脚本:在传统 Hadoop 部署中,通过 yarn-daemon.sh start nodemanager 命令手动启动

4.4 AM运行用户代码

  1. 初始化与注册

    • AM 启动后,向 RM 注册自身,并申请运行 Map/Reduce Task 所需的资源(Container)。

  2. 从 HDFS 加载用户代码

    • AM 从 HDFS 下载作业的 JAR 包和配置文件到本地。

    • 使用 分布式缓存(Distributed Cache) 机制,将依赖文件(如 JAR、配置文件)分发到所有任务节点。

  3. 申请资源并启动任务

    • AM 向 RM 发送资源请求(如申请 10 个 Container 运行 Map Task)。

    • RM 分配 Container 后,AM 与目标 NodeManager 通信,触发 Container 的启动

am请求分配Container代码示例:

// 伪代码示例(类似YARN API)
ResourceRequest request = ResourceRequest.newInstance(Priority.HIGH,          // 优先级"node_hostname",        // 目标节点(或*表示任意)Resource.newInstance(1024, 4), // 1GB内存 + 4个vCore5                       // 需要5个这样的Container
);
amClient.addResourceRequest(request);

如果资源足够,RM会:

  • 根据集群的当前资源使用情况(由NodeManager定期上报)和调度策略(如Capacity Scheduler、Fair Scheduler),决定是否满足AM的请求。

  • 若资源足够,调度器将分配Container,生成Container对象,包含:

    • Container ID。

    • 分配的节点(NodeManager地址)。

    • 资源规格(如内存、CPU)。

然后通过心跳机制(AM定期轮询或事件驱动)将分配的Container信息返回给AM

AM收到ResourceManager分配的Container列表后,会向对应的NodeManager发送启动Container的指令

4.5 NodeManager运行用户代码

  1. 创建 Container 进程

    • NodeManager 收到 AM 的启动指令后,在本地创建一个 独立的 JVM 进程(如 Map Task)。

    • 该进程的入口类是用户编写的 Mapper 或 Reducer 实现类。

  2. 加载用户代码

    • Container 进程从 HDFS 或本地缓存(通过 Distributed Cache)加载 JAR 包和依赖。

    • 使用 URLClassLoader 动态加载用户类(如 MyMapper.class)。

  3. 执行任务逻辑

    • 调用用户实现的 map() 或 reduce() 方法处理数据。

    • 输出结果写入 HDFS 或中间存储。

Q:AM收到ResourceManager分配的Container列表后,会向对应的NodeManager发送启动Container的指令。 NodeManager如何确认收到的命令是否合法?

在安全集群(启用Kerberos)中,所有组件(包括AM、RM、NM)必须通过Kerberos认证才能通信:

  • 初始化认证:AM在向RM注册时,需提供有效的Kerberos票据(Ticket)以证明身份。

  • 服务票据:AM与NM通信时,会使用Kerberos获取NM的服务票据,确保通信双方身份合法。

即使通过Kerberos认证,YARN还需进一步限制AM的操作权限。为此,RM在分配Container时会生成容器令牌,作为AM向NM启动Container的“临时授权凭证”。

容器令牌的生成与传递

  1. RM生成容器令牌

    • 当RM的调度器为AM分配Container时,会为该Container生成一个唯一的容器令牌

    • 令牌包含以下信息:

      • Container ID。

      • 资源分配详情(如内存、CPU)。

      • 有效时间窗口(如过期时间)。

      • NM的地址(确保令牌仅能被目标NM使用)。

      • 数字签名(由RM的密钥签名,防篡改)。

  2. AM获取令牌

    • RM将分配的Container列表及对应的容器令牌返回给AM(通过心跳响应)。

    • AM需在启动Container时将此令牌提交给NM。

NodeManager验证容器令牌

当NM收到AM的StartContainerRequest时,会执行以下验证:

  1. 验证令牌签名

    • 使用RM的公钥验证令牌的签名,确保令牌未被篡改。

  2. 检查令牌有效期

    • 确保令牌未过期(如过期则拒绝请求)。

  3. 匹配目标NM

    • 确认令牌中的NM地址与当前NM的地址一致,防止令牌被转发到其他节点。

  4. 核对Container ID和资源规格

    • 检查请求的Container ID和资源是否与令牌中的分配一致。

  5. 权限校验

    • 确保AM有权操作该Container(例如,令牌中的用户与AM的身份一致)。

Q:任务进程(如MapTask)会定期向AM发送心跳,报告进度(如完成50%)。 MapTask 和 AM在两个不同的container中,它们如何知道对方地址并交互的?

步骤1:AM启动并注册

  1. MRAppMaster(AM)启动后,绑定到一个可用端口(如0.0.0.0:0由系统自动分配)。

  2. 向ResourceManager注册,提交自身的RPC地址(如am-host:4321)。

步骤2:启动MapTask

  1. AM向ResourceManager申请Container资源。

  2. 在Container启动参数中,设置环境变量MAPREDUCE_JOB_APPLICATION_MASTER_ADDR=am-host:4321

步骤3:MapTask向AM发送心跳

  1. MapTask进程启动后,读取环境变量获取AM的地址。

  2. 通过Hadoop RPC客户端,连接到am-host:4321

  3. 调用AM的RPC接口(如AMProtocol#statusUpdate),发送心跳信息:

4.6 资源释放

子任务的释放:

  • AM的核心职责

    • 管理应用程序的整个生命周期(如Map阶段和Reduce阶段的协调)。

    • 主动申请和释放资源:AM根据任务进度动态管理Container,当MapTask完成后,AM会主动释放这些Container的资源,以便进入Reduce阶段。

  • 具体流程

    1. 监控任务状态:AM持续监控所有MapTask的进度,当所有MapTask均完成后,AM标记Map阶段结束。

    2. 释放Container

      • AM向对应的NodeManager发送StopContainerRequest,要求停止MapTask占用的Container。

      • AM通过心跳机制通知ResourceManager的调度器(Scheduler),这些Container已释放,资源可重新分配。

    3. 进入Reduce阶段:AM开始申请新的Container资源以启动ReduceTask。

AM的释放:

正常释放流程

  1. AM完成工作:当ApplicationMaster完成其分配的任务后,它会主动向ResourceManager(RM)发送完成通知。

  2. 注销AM

    • AM调用AMRMClient.unregisterApplicationMaster()方法

    • 该方法向RM发送FinishApplicationMasterRequest请求

  3. RM处理请求

    • RM接收到请求后,将AM状态标记为已完成

    • RM通知NodeManager(NM)释放AM容器

  4. 容器清理

    • NM接收到释放指令后,停止AM进程

    • 清理容器的工作目录

    • 释放分配给该容器的资源

  5. 状态更新

    • RM更新应用程序状态为FINISHED

    • 资源调度器回收分配给该AM的所有资源

异常释放流程

如果AM异常终止,释放流程会有所不同:

  1. 心跳超时

    • RM通过心跳机制检测AM是否存活

    • 如果超过yarn.am.liveness-monitor.expiry-interval-ms(默认60000ms)未收到心跳,RM认为AM失效

  2. 标记失败

    • RM将AM状态标记为FAILED

    • 触发失败处理机制

  3. 容器清理

    • RM通知NM强制终止AM容器

    • NM执行kill操作并清理资源

  4. 重试机制

    • 根据yarn.resourcemanager.am.max-attempts配置决定是否重试

    • 如果允许重试,RM会启动新的AM容器

五、设计不足

Hadoop 2.x 计算理念,相对于spark计算理念的不足,这里直接上deepseek的回答了:

1. 基于磁盘的计算模型导致性能瓶颈

  • Hadoop:MapReduce的每个中间阶段(Map和Reduce)都需要将数据写入磁盘(HDFS),导致频繁的I/O操作。在迭代计算(如机器学习算法)或需要多阶段处理的任务中,反复读写磁盘的开销极大,性能显著下降。

  • Spark:通过内存计算(In-Memory Processing)和弹性分布式数据集(RDD)的缓存机制,减少磁盘I/O。中间结果优先保留在内存中,适合迭代和交互式任务,性能通常比Hadoop快10-100倍。

2. 任务调度的延迟较高

  • Hadoop:每个MapReduce作业(Job)启动时都需要重新申请资源,且单个Job内分为Map和Reduce两个阶段,任务调度(Task Scheduling)粒度较粗。对于多阶段任务(如多个Job串联的场景),需要重复调度和资源分配,增加了整体延迟。

  • Spark:采用DAG(有向无环图)调度器,将整个计算流程分解为多个Stage,并在一个作业(Job)内自动优化执行顺序,避免多次任务调度。此外,通过细粒度的任务划分,减少资源浪费。

3. 编程模型不够灵活

  • Hadoop:MapReduce的编程模型强制开发者将逻辑拆分为MapReduce两个阶段,对复杂计算(如多表关联、图算法)的支持较差,代码冗长且难以复用。

  • Spark:提供更丰富的API(如mapfilterjoinreduceByKey等)和高级抽象(RDD、DataFrame、Dataset),支持函数式编程和复杂流水线操作。开发者可以更灵活地表达计算逻辑,代码量显著减少。

4. 对实时和流式处理支持有限

  • Hadoop:原生设计面向批处理,延迟通常在分钟到小时级别。虽然可以通过附加框架(如Storm)实现流处理,但需要额外的系统集成。

  • Spark:通过Spark Streaming(微批处理)和Structured Streaming(准实时流处理)直接支持流式计算,并与批处理API保持统一,简化开发流程。

5. 资源利用效率较低

  • Hadoop:MapReduce在任务执行期间资源分配较为静态,任务结束后资源立即释放,难以共享复用。在YARN的调度下,资源分配粒度较粗。

  • Spark:通过动态资源分配和内存缓存机制,允许不同任务共享数据缓存,资源利用率更高。同时支持在内存中缓存中间数据,减少重复计算。

6. 对复杂计算场景的支持不足

  • Hadoop:对机器学习、图计算等需要多次迭代的场景支持较弱(需多次启动作业),需要依赖其他生态工具(如Mahout)。

  • Spark:通过内置库(如MLlib、GraphX)直接支持机器学习、图计算等复杂场景,利用内存计算加速迭代过程

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

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

相关文章

串口屏调试 1.0

http://wiki.tjc1688.com 先把商家的链接贴过来 淘晶驰T1系列3.2寸串口屏tft液晶屏显示屏HMI触摸屏超12864液晶屏 这是主包的型号 打开这个玩意 有十个基本的功能区 新建工程 在界面的右边&#xff0c;指令一定要写在page前面&#xff0c;这里的波特率等等什么的都可以…

《设计数据密集型应用》——阅读小记

设计数据密集型应用 这本书非常推荐看英语版&#xff0c;如果考过了CET-6就可以很轻松的阅读这本书。 当前计算机软件已经不是单体的时代了&#xff0c;分布式系统&#xff0c;微服务现在是服务端开发的主流&#xff0c;如果没有读过这本书&#xff0c;则强力建议读这本书。 …

【SpringMVC】详解cookie,session及实战

目录 1.前言 2.正文 2.1cookie与session概念 2.2返回cookie参数 2.3设置session 3.小结 1.前言 哈喽大家好吖&#xff0c;今天继续来给大家来分享SpringMVC的学习&#xff0c;今天主要带来的是cookie与session的讲解以及通过postman和fiddler来实战&#xff0c;废话不多…

令狐冲的互联网大厂Java面试之旅

场景描绘&#xff1a;互联网大厂Java面试 在某个阳光明媚的上午&#xff0c;令狐冲来到了风清扬所在的互联网大厂&#xff0c;准备迎接他的Java开发工程师面试。风清扬是一位以严谨和深厚技术功底著称的面试官&#xff0c;令狐冲稍显紧张&#xff0c;但他相信自己的准备。 第…

照片to谷歌地球/奥维地图使用指南

软件介绍 照片to谷歌地球/奥维地图是一款由WTSolutions开发的跨平台图片处理工具&#xff0c;能够将带有GPS信息的照片导入Google Earth&#xff08;谷歌地球&#xff09;或奥维地图。该软件支持Windows、Mac、iOS、Linux和Android系统&#xff0c;无需下载安装&#xff0c;直…

客户端建立一个连接需要占用客户端的端口吗

客户端建立TCP连接时需占用本地端口&#xff0c;具体机制如下&#xff1a; 一、端口占用的必要性 四元组唯一性‌ TCP连接通过‌源IP、源端口、目标IP、目标端口‌四元组唯一标识。客户端发起连接时&#xff0c;必须绑定本地端口以完成通信标识。 动态端口分配‌ 客户端操作…

【生存技能】ubuntu 24.04 如何pip install

目录 原因解决方案说明 在接手一个新项目需要安装python库时弹出了以下提示: 原因 这个报错是因为在ubuntu中尝试直接使用 pip 安装 Python 包到系统环境中&#xff0c;ubuntu 系统 出于稳定性考虑禁止了这种操作 这里的kali是因为这台机器的用户起名叫kali,我也不知道为什么…

智能时代下,水利安全员证如何引领行业变革?

当 5G、AI、物联网等技术深度融入水利工程&#xff0c;传统安全管理模式正经历颠覆性变革。在这场智能化浪潮中&#xff0c;水利安全员证扮演着怎样的角色&#xff1f;又将如何重塑行业人才需求格局&#xff1f; 水利工程智能化转型对安全管理提出新挑战。无人机巡检、智能监测…

TDengine 在智能制造中的核心价值

简介 智能制造与数据库技术的深度融合&#xff0c;已成为现代工业技术进步的一个重要里程碑。随着信息技术的飞速发展&#xff0c;智能制造已经成为推动工业转型升级的关键动力。在这一进程中&#xff0c;数据库技术扮演着不可或缺的角色&#xff0c;它不仅承载着海量的生产数…

微调ModernBERT为大型语言模型打造高效“过滤器”

ModernBERT&#xff08;2024 年 12 月&#xff09;是最近发布的小型语言模型&#xff0c;由 Answer.AI、LightOn 和 HuggingFace 共同开发。它利用了现代优化技术&#xff0c;如用于 8,192 token 上下文窗口的 RoPE 和 GeGLU layers&#xff0c;在保持效率的同时提升性能。jina…

电网拓扑分析:原理与应用

在现代电力系统中&#xff0c;电网拓扑分析是一项至关重要的技术&#xff0c;它为电力系统的安全、稳定和高效运行提供了坚实的基础。电网拓扑描述了电力系统中各元件&#xff08;如发电机、变压器、输电线路、负荷等&#xff09;之间的连接关系&#xff0c;通过拓扑分析&#…

OSPF案例

拓扑图&#xff1a; 要求&#xff1a; 1&#xff0c;R5为ISP&#xff0c;其上只能配置IP地址&#xff1b;R4作为企业边界路由器&#xff0c; 出口公网地址需要通过PPP协议获取&#xff0c;并进行chap认证 2&#xff0c;整个OSPF环境IP基于172.16.0.0/16划分&#xff1b;…

2D横板跳跃游戏笔记(查漏补缺ing...)

1.Compression&#xff08;压缩质量&#xff09;&#xff1a;可以改为None&#xff0c;不压缩的效果最好&#xff0c;但占用内存 2.Filter Mode&#xff08;过滤模式&#xff09;&#xff1a;可以选择Point&#xff08;no filter&#xff09; 3.Pixels Per Unit&#xff1a;是…

MAD-TD: MODEL-AUGMENTED DATA STABILIZES HIGH UPDATE RATIO RL

ICLR 2025 spotlight paper 构建能够在少量样本下学习出优良策略的深度强化学习&#xff08;RL&#xff09;智能体一直是一个极具挑战性的任务。为了提高样本效率&#xff0c;近期的研究尝试在每获取一个新样本后执行大量的梯度更新。尽管这种高更新-数据比&#xff08;UTD&am…

Dia浏览器:AI驱动浏览网页,究竟怎么样?(含注册申请体验流程)

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、Dia浏览器简介1. 什么是Dia浏览器2. 开发背景与公司简介3. 与传统浏览器的区别 …

SSL/TLS 证书与数字签名:构建互联网信任的详解

在浩瀚的数字世界中&#xff0c;信任是安全通信的基石。当我们通过浏览器访问一个 HTTPS 网站、进行在线支付&#xff0c;或者下载一个重要的软件更新时&#xff0c;我们如何能确信自己正在与合法的、未被仿冒的对方进行交互&#xff1f;我们又如何能保证传输的数据没有被中途窃…

近日部署跑通的若干多模态模型总结与论文概述

CLIP模型概述与落地测试 CLIP模型全称是Contrastive Language-Image Pretraining​​&#xff08;对比语言图像预训练&#xff09;。是OpenAI于2021年提出的多模态预训练模型&#xff0c;通过对比学习对齐图像和文本的表示&#xff0c;实现零样本&#xff08;zero-shot&#x…

Web3 初学者的第一个实战项目:留言上链 DApp

目录 &#x1f4cc; 项目简介&#xff1a;留言上链 DApp&#xff08;MessageBoard DApp&#xff09; &#x1f9e0; 技术栈 &#x1f536; 1. Solidity 智能合约代码&#xff08;MessageBoard.sol&#xff09; &#x1f537; 2. 前端代码&#xff08;index.html script.js…

LeetCode 270:在二叉搜索树中寻找最接近的值(Swift 实战解析)

文章目录 摘要描述题解答案题解代码分析示例测试及结果时间复杂度空间复杂度总结 摘要 在日常开发中&#xff0c;我们经常需要在一组有序的数据中快速找到最接近某个目标值的元素。LeetCode 第 270 题“Closest Binary Search Tree Value”正是这样一个问题。本文将深入解析该…

Kotlin高阶函数多态场景条件判断与子逻辑

Kotlin高阶函数多态场景条件判断与子逻辑 fun main() {var somefun: (Int, Float) -> Longval a 4val b 5fsomefun multi()//if 某条件println(somefun.invoke(a, b))//if 某条件somefun add()println(somefun.invoke(a, b)) }fun multi(): (Int, Float) -> Long {re…