引言

在Kafka集群中,有一个组件堪称"隐形的指挥官"——它默默协调着Broker的加入与退出,管理着主题的创建与删除,掌控着分区领导者的选举,它就是控制器(Controller)。想象一个拥有100台Broker的大型Kafka集群:当某台Broker突然宕机,谁来检测并触发分区领导者切换?当管理员创建新主题时,谁来分配分区并同步元数据到所有节点?答案正是控制器。

控制器是Kafka集群的核心协调组件,它通过ZooKeeper实现分布式协调,确保集群在各种场景下(如Broker故障、主题变更)都能有序运行。每个Kafka集群在任意时刻有且仅有一个活跃控制器,这一特性既是其设计的精妙之处,也暗藏单点故障的风险——为此,Kafka设计了完善的故障转移机制,确保控制器失效时能快速恢复。

ZooKeeper:控制器的"分布式数据库"

Kafka控制器重度依赖ZooKeeper实现分布式协调,理解ZooKeeper的核心功能是掌握控制器工作原理的前提。

ZooKeeper的核心功能

ZooKeeper是一个高可用的分布式协调服务,为Kafka提供以下关键能力:

  1. 树形数据模型 ZooKeeper的存储结构类似文件系统,以"/"为根目录,每个节点(znode)可存储少量元数据(默认最大1MB)。Kafka在ZooKeeper中创建了大量znode,用于存储集群元数据,如/brokers/ids存储所有Broker的ID,/topics存储主题信息,/controller标识当前控制器等。

  2. znode的持久性分类

    • 持久性znode:创建后永久存在,除非手动删除(如/topics/test存储主题test的元数据)。

    • 临时znode:与创建者的会话绑定,会话结束后自动删除(如/brokers/ids/0代表Broker 0,若其宕机,该节点会被自动删除)。

  3. Watch通知机制 客户端可注册监听znode的变更(创建、删除、数据修改),当事件发生时,ZooKeeper会实时通知客户端。控制器正是通过Watch机制感知集群变化,如监听/brokers/ids节点发现新Broker加入,监听/controller节点检测控制器故障。

Kafka在ZooKeeper中的关键节点

Kafka在ZooKeeper中创建了多个关键节点,控制器通过这些节点实现集群管理:

节点路径功能描述存储内容示例备注
Broker 相关
/brokers/ids注册 Broker 信息,记录每个 Broker 的网络地址、端口等子节点为 broker.id,内容如:{"host":"10.0.0.1","port":9092,"jmx_port":9999}Broker 启动时创建,通过心跳维持;下线后节点自动删除。
/brokers/topics存储所有 Topic 的元数据(分区、副本分布)子节点为 topic 名称,下含 partitions/[partition-id]/replicas(副本列表)Topic 创建时生成,删除时递归删除子节点;记录分区的副本分布。
Controller 相关
/controller记录当前集群的 Controller Broker ID(集群核心协调者)内容为数字(如 "2",对应 Broker.id=2)Controller 选举后更新;是集群元数据、分区 leader 等的核心管理者。
/controller_epochController 的纪元(版本号),防止旧 Controller 的无效操作内容为数字(如 "5",每次 Controller 变更时纪元 +1)配合 /controller 实现“脑裂”防护:旧纪元的请求会被忽略。
Consumer 相关(旧版架构)
/consumers/<group_name>/offsets(旧版)存储消费者组的消费偏移量(新版已迁移到 Kafka 内部主题 __consumer_offsets子节点为 {topic}-{partition},内容为偏移量(如 "12345"新版 Kafka 建议禁用该方式,改用内部主题存储偏移(更可靠、可扩展)。
/consumers/<group_name>/ids(旧版)存储消费者组内每个消费者实例的信息子节点为消费者实例 ID,内容如:{"client_id":"consumer-1","host":"10.0.0.2"}消费者加入组时注册,离开时删除;属于旧版 Consumer Group 架构(已被取代)。
/consumers/<group_name>/owners(旧版)记录消费者组中各分区的所有者(哪个消费者实例消费该分区)子节点为 {topic}-{partition},内容为消费者实例 ID(如 "consumer-1-1"消费分配(如 Range、RoundRobin)后更新;旧版协调器(ZooKeeper-based)使用。
配置相关
/config存储 主题/ Broker /用户 的自定义配置(支持动态更新)子节点 topics/[topic-name],内容如:{"retention.ms":"86400000"}配置变更时,会触发 /config/changes 的通知。
/config/changes配置变更的通知节点,用于监听配置变化事件可能存储变更版本(如 "v1")或作为 Watcher 触发标记(空节点)Kafka 组件(如 Broker)通过 Watcher 感知配置变化,实现动态更新。
管理操作相关
/admin/delete_topics标记待删除的主题,触发 Controller 的主题删除流程子节点为 topic 名称(如 "test-topic"),内容为空或标记时间提交删除请求后创建,Controller 处理完删除逻辑后移除该节点。
/admin/reassign_partitions存储 分区重分配任务(手动/自动 Rebalance)内容为 JSON,如:{"partitions":[{"topic":"test","partition":0,"replicas":[1,2]}}管理员提交任务后创建,任务完成后由 Controller 清理。
/admin/preferred_replica_selection存储 优先副本选举任务(平衡 Leader 负载,切到优先副本)内容为 JSON,如:{"partitions":[{"topic":"test","partition":0}]}提交后由 Controller 处理,选举完成后删除节点。
变更通知相关
/air_change_notification集群状态变更通知(如 Topic 创建、Broker 上下线、配置变更等)可能存储事件序列(如 "event-1")或作为 Watcher 触发节点(空节点)Kafka 组件(如 Broker、Controller)监听该节点,感知集群状态变化。

这些节点构成了Kafka集群的"元数据中心",控制器通过读写这些节点实现对集群的协调管理。

控制器的选举:从"竞选"到"就职"

Kafka集群启动时,控制器并非天生存在,而是通过"竞选"产生。这一过程依赖ZooKeeper的分布式锁特性,确保最终只有一个Broker成为控制器。

选举机制:谁先创建节点谁当选

控制器的选举逻辑简洁而高效:

  1. 竞选触发:每个Broker启动时,会尝试向ZooKeeper创建/controller临时节点。

  2. 竞争结果:由于ZooKeeper保证节点的唯一性,第一个成功创建/controller节点的Broker将成为控制器。

  3. 身份确认:成功创建节点的Broker会将自己的ID写入节点数据(如{"brokerid":3,"epoch":0}),其他Broker发现/controller已存在,会放弃竞选并监听该节点的变化。

这种"先到先得"的机制确保了选举的高效性,通常在集群启动后几秒内即可完成。

纪元(Epoch):避免"脑裂"的安全机制

为防止旧控制器故障后仍发送指令(即"脑裂"),Kafka引入控制器纪元(Controller Epoch)

  • 每次选举产生新控制器时,纪元值递增(如从0→1→2)。

  • 控制器发送的所有指令都携带纪元值,其他Broker只接受纪元值大于当前已知值的指令。

  • 纪元值存储在/controller_epoch节点,确保集群全局一致。

例如,若旧控制器(纪元1)在故障后仍发送指令,新控制器(纪元2)已将纪元值同步给所有Broker,旧指令会因纪元值过小被忽略,避免混乱。

控制器的五大核心职责:集群的"管理员"

控制器是Kafka集群的"大管家",承担着主题管理、集群成员维护等关键职责,这些职责的正常履行直接决定了集群的可用性。

主题管理:创建、删除与分区扩容

当用户执行kafka-topics.sh脚本创建主题时,实际工作由控制器完成:

  1. 创建主题:控制器接收请求后,在ZooKeeper的/brokers/topics下创建主题节点,写入分区数、副本数等配置。

  2. 分配分区:根据副本分配策略(如机架感知),将分区的副本分配到不同Broker。

  3. 同步元数据:通过LeaderAndIsr请求通知相关Broker创建分区目录,并将元数据同步到所有Broker。

删除主题和增加分区的流程类似:控制器监听/admin/delete_topics节点触发删除,或直接更新主题节点数据触发分区扩容,再同步给全集群。

分区重分配:负载均衡的"调度师"

当集群中Broker负载不均时,管理员可通过kafka-reassign-partitions.sh脚本触发分区重分配,这一过程由控制器主导:

  1. 接收任务:控制器读取/admin/reassign_partitions节点中的重分配计划。

  2. 协调迁移:向涉及的Broker发送指令,先创建新副本,同步数据,再切换领导者,最后删除旧副本。

  3. 更新元数据:重分配完成后,更新ZooKeeper中的分区副本信息,并同步给所有Broker。

这一机制确保了分区迁移过程中服务不中断,是Kafka弹性扩缩容的核心支撑。

Preferred领导者选举:负载均衡的"平衡器"

Kafka创建分区时,会将第一个副本设为"Preferred领导者"(优先领导者)。若因故障导致领导者切换为其他副本,可能造成Broker负载不均。控制器通过Preferred领导者选举修复这一问题:

  1. 触发条件:管理员执行kafka-preferred-replica-election.sh脚本,或控制器检测到负载失衡。

  2. 选举流程:控制器向目标分区的Preferred领导者副本发送LeaderAndIsr请求,将其切换为领导者(前提是该副本在ISR中)。

  3. 同步结果:更新分区元数据,并通知所有Broker。

这一功能确保了长期运行后,领导者副本仍能均匀分布在集群中。

集群成员管理:Broker的"花名册"

控制器通过监听ZooKeeper的/brokers/ids节点,实时掌握集群成员变化:

  • 新Broker加入:新Broker启动时会在/brokers/ids下创建临时节点,控制器通过Watch机制感知后,将其加入集群,并分配现有主题的副本。

  • Broker主动关闭:Broker关闭前会删除自身在/brokers/ids下的节点,控制器收到通知后,触发受影响分区的领导者重选举。

  • Broker宕机:Broker与ZooKeeper的会话超时后,临时节点被自动删除,控制器检测到后,立即启动故障转移(如将宕机Broker上的领导者副本切换到其他存活副本)。

这一机制确保了集群在Broker动态变化时仍能保持可用性。

数据服务:元数据的"分发中心"

控制器保存着集群最完整的元数据,并定期同步给其他Broker:

  • 元数据内容:所有主题的分区信息(领导者、ISR集合)、Broker列表、运维任务(如正在重分配的分区)等。

  • 同步机制:其他Broker定期向控制器发送Metadata请求,获取最新元数据并更新本地缓存。

  • 主动推送:当元数据发生重大变更(如领导者切换),控制器会主动向相关Broker发送通知。

这一机制确保了全集群元数据的一致性,是生产者和消费者正确工作的前提。

控制器的"记忆":数据存储与初始化

控制器之所以能高效履行职责,得益于其缓存的完整元数据。这些数据既来自ZooKeeper,也包含运行时的动态信息。

核心数据内容

控制器的缓存(ControllerContext)包含以下关键信息:

  • 主题与分区数据:所有主题的列表、每个分区的副本分布、领导者副本ID、ISR集合等。

  • Broker信息:存活Broker的ID、地址、端口,以及正在关闭的Broker列表。

  • 运维任务状态:正在进行重分配的分区、正在执行Preferred选举的分区等。

  • ZooKeeper节点缓存:部分znode的内容缓存,减少对ZooKeeper的直接访问。

这些数据构成了控制器管理集群的"决策依据"。

数据项名称功能描述存储/维护方式核心作用与关联机制
Broker 状态类
当前存活 Broker 列表记录集群中活跃 Broker(通过心跳维持,未超时)内存维护,基于 Broker 心跳包更新(超时则移除)Controller 决策(如选举 leader、分配副本)的“可用节点池”,排除故障 Broker。
正在关闭中 Broker 列表标记主动优雅关闭的 Broker(非崩溃,需安全迁移副本)Broker 发起关闭请求时创建标记,副本迁移完成后清除区别于崩溃 Broker,优先触发副本迁移,保证数据不丢失(如 Broker.shutdown 流程)。
分区与副本类
分配给每个分区的副本列表每个分区的 AR(Assigned Replicas):该分区的所有副本所在 Broker ID 集合存储于分区元数据(如 [1,2,3],对应 Broker ID)副本分配的基准(创建 Topic 时确定),故障时从 AR 中选举新 leader,重分配时参考。
每个分区的 leader 和 ISR 信息分区的 主副本(leader) + 同步副本(ISR,In-Sync Replicas)leader 是单个 Broker ID,ISR 是 Broker ID 列表(实时更新,基于副本同步状态)读写路由:仅 leader 处理客户端请求;选举约束:故障时仅从 ISR 中选新 leader(保证数据一致)。
某个 Broker 上的所有分区反向索引:某 Broker 承载的所有分区<topic>-<partition> 集合)内存映射表(Broker ID → 分区列表),基于分区的 leader/副本分布动态更新负载均衡分析(如 Broker 负载过高触发重分配),故障转移时快速定位受影响分区。
某组 Broker 上的所有副本统计指定 Broker 组承载的所有副本(跨 Topic、分区)动态计算(遍历所有分区的 AR,筛选属于目标 Broker 组的副本)副本迁移规划(如 Broker 扩容/缩容时,确定源和目标 Broker 的副本分布)。
某个 Topic 的所有副本该 Topic 下所有分区的 AR 集合(每个分区的副本列表)遍历该 Topic 的分区元数据,收集每个分区的 AR 数组Topic 级副本管理(如修改副本数、检查分布均衡性),批量操作的基础。
某个 Topic 的所有分区该 Topic 的分区集合(含分区 ID、leader、ISR、AR 等元数据)Topic 元数据中维护分区列表,关联每个分区的详细信息读写请求路由(定位分区 leader),分区级操作(重分配、选举)的入口。
当前存活的所有副本筛选所在 Broker 存活的副本(排除 Broker 下线的副本)动态计算(基于“存活 Broker 列表”过滤副本的所在 Broker)选举新 leader 时,仅从存活副本中选择(避免选到故障 Broker 的副本,提升可靠性)。
某组分区下的所有副本指定分区集合(如某 Topic 的部分分区)的 AR 集合遍历目标分区,收集每个分区的 AR 数组批量操作(如批量重分配、批量选举)时,高效获取副本分布,减少重复计算。
任务与生命周期类
正在进行 preferred leader 选举的分区记录正在执行优先副本选举的分区(将 leader 切回 AR 第一个副本,平衡负载)<topic>-<partition> 为键,存储选举任务状态(如开始时间、目标副本)确保选举过程原子性(避免重复触发),完成后更新 leader 信息,平衡集群负载。
正在进行重分配的分区列表标记处于副本重分配任务的分区(如扩缩容、副本迁移)存储重分配的目标 AR(如 {"new_replicas": [2,3,4]}保证重分配过程的一致性(避免中断或重复操作),完成后更新分区的 AR 和 ISR。
Topic 列表集群中所有存在的 Topic 元数据(含分区数、配置、状态等)基于 ZooKeeper(旧版)或 KRaft 日志(新版)同步,内存维护Controller 处理 Topic 创建、删除、配置变更的核心依据,关联所有分区操作。
移除某个 Topic 的所有信息标记待删除的 Topic,触发元数据清理(分区、副本、配置等)临时标记(如 ZooKeeper 的 /admin/delete_topics 节点)或 KRaft 删除日志触发递归清理:删除分区元数据、通知 Broker 卸载数据、更新 Topic 列表。

数据初始化流程

当控制器首次启动或故障转移后,需要从ZooKeeper加载元数据初始化缓存,流程如下:

  1. 读取基础节点:从/brokers/ids获取所有存活Broker,从/brokers/topics获取所有主题和分区信息。

  2. 构建分区映射:为每个分区记录副本分布、当前领导者和ISR(从/brokers/topics/<topic>节点读取)。

  3. 加载运维任务:从/admin/reassign_partitions等节点读取正在执行的任务。

  4. 同步到本地缓存:将所有信息整合到ControllerContext,供后续操作使用。

初始化完成后,控制器即可正常履行职责,这一过程通常耗时几秒(取决于集群规模)。

故障转移:控制器的"自愈"能力

尽管控制器是单点,但Kafka通过故障转移机制确保其高可用——当控制器宕机,集群能在几十秒内自动选举新控制器,恢复正常运行。

故障检测:ZooKeeper的"心跳"

控制器与ZooKeeper保持长连接,其存活状态通过以下机制检测:

  1. 临时节点关联:控制器创建的/controller节点是临时节点,与控制器的ZooKeeper会话绑定。

  2. 会话超时检测:若控制器宕机,ZooKeeper会在会话超时后(默认6秒)删除/controller节点。

  3. Watch通知:所有Broker都监听/controller节点,当节点被删除,它们会立即感知到控制器故障。

新控制器选举与初始化

控制器故障后,故障转移流程自动启动:

  1. 重新竞选:所有存活Broker检测到/controller节点被删除,立即尝试创建该节点,第一个成功的成为新控制器。

  2. 纪元更新:新控制器将/controller_epoch的值加1(如从1→2),确保旧控制器的指令失效。

  3. 元数据加载:新控制器从ZooKeeper读取所有元数据,初始化本地缓存(流程同5.2节)。

  4. 通知集群:新控制器向所有Broker发送UpdateMetadata请求,宣告自己的身份并同步最新元数据。

整个过程通常在10-30秒内完成,期间集群可能暂时无法处理主题创建、分区重分配等操作,但已有的生产者和消费者不受影响(依赖本地缓存的元数据)。

内部设计演进:从"混乱"到"有序"

Kafka控制器的内部设计经历了重大重构,从多线程同步模型演进为单线程事件驱动模型,显著提升了稳定性和性能。

0.11版本前:多线程模型的困境

早期控制器采用多线程设计,存在明显缺陷:

  • 线程泛滥:为每个Broker创建专属通信线程,为主题删除、分区重分配等任务创建独立线程,集群规模大时线程数激增。

  • 线程安全风险:多线程共享ControllerContext缓存,需大量使用ReentrantLock同步,导致性能低下且易出现死锁。

  • ZooKeeper同步操作:所有ZooKeeper读写都是同步的,当元数据变更频繁时,ZooKeeper易成为瓶颈。

这些问题导致早期版本控制器Bug频发,如主题删除卡住、重分配失败等。

0.11版本后:单线程+事件队列的革新

为解决多线程的弊端,0.11版本重构了控制器设计:

  1. 单线程事件处理:引入一个事件处理线程,所有集群变更(如Broker加入、主题创建)都被建模为事件,放入事件队列由该线程串行处理。

  2. 异步ZooKeeper操作:将ZooKeeper的同步API改为异步API,避免线程阻塞,写入性能提升10倍。

  3. 无锁设计:由于事件串行处理,ControllerContext缓存仅被一个线程访问,无需锁机制,消除了死锁风险。

这一设计显著提升了控制器的稳定性,社区报告的控制器相关Bug数量大幅减少。

2.2版本后:请求优先级的区分

Kafka 2.2版本进一步优化了控制器请求的处理:

  • 请求分类:将控制器发送的指令(如StopReplica删除副本、LeaderAndIsr切换领导者)标记为高优先级。

  • 独立处理通道:高优先级请求绕过普通请求队列,直接进入专门的处理流程,避免被普通请求(如Produce)阻塞。

例如,删除主题时,控制器发送的StopReplica请求会被优先处理,避免主题已删除但仍有消息写入的荒诞场景。

实战运维:控制器的监控与问题排查

控制器的稳定性直接决定集群可用性,掌握其监控与故障处理技巧是Kafka运维的核心能力。

关键监控指标

通过JMX或Prometheus监控以下指标,实时掌握控制器状态:

  • activeControllerCount:集群中活跃控制器的数量,正常应为1。若大于1,表明出现"脑裂",需立即处理。

  • controllerEpoch:当前控制器纪元值,应单调递增(每次故障转移后+1)。

  • controllerZooKeeperRequestRate:控制器向ZooKeeper的请求速率,突增可能意味着元数据频繁变更。

  • partitionCount:控制器管理的分区总数,间接反映集群负载。

常见问题与解决方案

问题1:控制器故障后集群无法自动恢复

现象:控制器宕机后,新控制器选举失败,activeControllerCount为0。 可能原因

  • ZooKeeper集群异常,Broker无法创建/controller节点。

  • 网络分区,部分Broker无法访问ZooKeeper。

解决方案

  1. 检查ZooKeeper健康状态(zkServer.sh status)。

  2. 查看Broker日志,确认是否有ZooKeeper连接超时错误。

  3. 手动删除/controller节点(zkCli.sh rmr /controller),触发重新选举。

问题2:主题删除卡住

现象:执行kafka-topics.sh --delete后,主题长期处于"标记删除"状态。 可能原因

  • 控制器未收到删除通知(/admin/delete_topics节点未被正确创建)。

  • 部分Broker未响应StopReplica请求,导致删除流程阻塞。

解决方案

  1. 检查ZooKeeper的/admin/delete_topics节点,确认目标主题是否存在。

  2. 查看控制器日志,确认是否有StopReplica请求超时。

  3. 重启卡住的Broker,或手动触发控制器重选举(删除/controller节点)。

问题3:控制器频繁切换

现象controllerEpoch频繁递增,表明控制器频繁故障转移。 可能原因

  • 控制器所在Broker资源不足(CPU/内存过载),导致ZooKeeper会话超时。

  • 网络不稳定,控制器与ZooKeeper的连接频繁中断。

解决方案

  1. 监控控制器所在Broker的资源使用率,确保CPU<70%、内存充足。

  2. 检查网络延迟,确保Broker与ZooKeeper的通信延迟<100ms。

  3. 手动将控制器迁移到更稳定的Broker(重启原控制器Broker,触发新选举)。

控制器迁移与升级技巧

  • 平滑迁移:若需将控制器从Broker A迁移到Broker B,可重启Broker A,触发新选举(确保Broker B配置正常)。

  • 版本升级:升级Kafka集群时,建议先升级非控制器Broker,最后升级控制器所在Broker,减少选举次数。

  • 灾备设计:避免将控制器与集群中负载最高的Broker部署在同一节点(如存储大量分区领导者的Broker)。

总结

Kafka控制器是集群的"大脑",它通过ZooKeeper实现分布式协调,承担着主题管理、集群成员维护、分区重分配等关键职责。其设计经历了从多线程到单线程事件驱动的演进,稳定性和性能不断提升。

理解控制器的工作原理,不仅能帮助开发者更好地使用Kafka(如解释分区领导者切换的原因),更能为运维人员提供排查集群问题的思路(如通过activeControllerCount判断脑裂)。未来,随着ZooKeeper依赖的移除,控制器的设计将更加精简高效,但核心职责和协调逻辑仍将保持延续。

在实际应用中,建议通过监控关键指标(如activeControllerCountcontrollerEpoch)实时掌握控制器状态,结合手动触发选举等技巧,确保集群在控制器故障时能快速恢复,为业务提供稳定的消息服务。

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

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

相关文章

编程与数学 03-002 计算机网络 11_域名系统(DNS)

编程与数学 03-002 计算机网络 11_域名系统&#xff08;DNS&#xff09;一、DNS的作用与功能&#xff08;一&#xff09;域名与IP地址的映射关系&#xff08;二&#xff09;DNS的层次结构二、DNS查询过程&#xff08;一&#xff09;递归查询与迭代查询&#xff08;二&#xff0…

影翎Antigravity将发布全球首款全景无人机,8月开启公测招募

7月28日&#xff0c;消费级无人机品牌「影翎Antigravity」及品牌标识官宣亮相&#xff0c;计划推出全新品类——全球首款「全景无人机」。这一消息引发行业震动&#xff0c;消费级航拍无人机市场或将迎来颠覆性飞行体验。影翎Antigravity官方介绍&#xff0c;引力不仅是束缚双脚…

SpringBoot集成Quzrtz实现定时任务

一 定时任务介绍 自律是很多人都想拥有的一种能力&#xff0c;或者说素质&#xff0c;但是理想往往很美好&#xff0c;现实却是无比残酷的。在现实生活中&#xff0c;我们很难做到自律&#xff0c;或者说做到持续自律。例如&#xff0c;我们经常会做各种学习计划、储蓄计划或减…

Java中的异常判断以及文件中的常用方法及功能

目录 异常 作用 异常的处理方式 JVM&#xff08;虚拟机&#xff09;默认的处理方式 自己处理&#xff08;捕获异常&#xff09; 抛出异常&#xff08;也就是交给调用者处理&#xff09; 自定义异常 file File中常见成员方法 判断和获取 创建和删除 获取并遍历 异常…

【C++算法】74.优先级队列_最后一块石头的重量

文章目录题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;题目链接&#xff1a; 1046. 最后一块石头的重量 题目描述&#xff1a; 解法 每次取出最重的两块石头进行碰撞&#xff0c;将剩余的石头重新放入堆中。 C 算法代码&#xff1a; class Solution …

中兴云电脑W101D2-晶晨S905L3A-2G+8G-安卓9-线刷固件包

中兴云电脑W101D2-晶晨S905L3A-2G8G-WIFI-蓝牙5.0-3个USB2.0-线刷包线刷方法&#xff1a;1、准备好一根双公头USB线刷刷机线&#xff0c;长度30-50CM长度最佳&#xff0c;同时准备一台电脑&#xff1b;2、电脑上安装好刷机工具Amlogic USB Burning Tool 软件 →打开软件 →文件…

Android OkHttp 底层原理和实战完全教程(责任链模式详解)

目录 1. OkHttp 入门:从一个请求开始 1.1 基本 GET 请求:三步走 1.2 同步 vs 异步:选择你的风格 1.3 为什么选 OkHttp? 2. 配置 OkHttpClient:打造你的专属网络引擎 2.1 超时设置:别让请求卡死 2.2 添加拦截器:窥探请求全过程 2.3 缓存:让请求更快更省流量 3. …

【RK3588部署yolo】算法篇

简历描述收集并制作军事伪装目标数据集&#xff0c;包含真实与伪装各种类型军事目标共计60余类。其中&#xff0c;包含最新战场充气伪装军事装备30余类&#xff0c;并为每一张图片制作了详细的标注。针对军事伪装目标的特点&#xff0c;在YOLOv8的Backbone与Neck部分分别加…

【Spring Boot 快速入门】一、入门

目录Spring Boot 简介Web 入门Spring Boot 快速入门HTTP 协议概述请求协议响应协议解析协议TomcatSpring Boot 简介 Spring Boot 是由 Pivotal 团队&#xff08;后被 VMware 收购&#xff09;开发的基于 Spring 框架的开源项目&#xff0c;于 2014 年首次发布。其核心目标是简…

如何调整服务器的内核参数?-哈尔滨云前沿

调整服务器内核参数是一项较为专业的操作&#xff0c;不同的操作系统调整方式略有不同&#xff0c;以下以常见的 Linux 系统为例&#xff0c;介绍一些调整服务器内核参数的一般步骤和常用参数&#xff1a;一般步骤 备份当前配置&#xff1a;在修改内核参数之前&#xff0c;先备…

C++基础:模拟实现queue和stack。底层:适配器

引言模拟实现queue和stack&#xff0c;理解适配器&#xff0c;实现起来非常简单。一、适配器 适配器是一种能让原本不兼容的接口协同工作的设计模式或者组件。它的主要作用是对一个类的接口进行转换&#xff0c;使其符合另一个类的期望接口&#xff0c;进而实现适配和复用。&am…

OI 杂题

OI 杂题字符串括号匹配例 1&#xff1a;与之前的类似&#xff0c;就是讲一点技巧&#xff0c;但是比较乱&#xff0c;凑合着看吧。 字符串 括号匹配 几何意义&#xff1a;考虑令 ( 为 111 变换&#xff0c;令 ) 为 −1-1−1 变换&#xff0c;然后对这个 1/−11/-11/−1 构成…

【论文阅读】Safety Alignment Should Be Made More Than Just a Few Tokens Deep

Safety Alignment Should Be Made More Than Just a Few Tokens Deep原文摘要问题提出现状与漏洞&#xff1a;当前LLMs的安全对齐机制容易被攻破&#xff0c;即使是简单的攻击&#xff08;如对抗性后缀攻击&#xff09;或良性的微调也可能导致模型越狱。核心论点&#xff1a; 作…

Generative AI in Game Development

如有侵权或其他问题&#xff0c;欢迎留言联系更正或删除。 出处&#xff1a;CHI 20241. 一段话总结本研究通过对来自 Reddit 和 Facebook 群组的 3,091 条独立游戏开发者的在线帖子和评论进行定性分析&#xff0c;探讨了他们对生成式 AI在游戏开发中多方面作用的认知与设想。研…

【C++算法】72.队列+宽搜_二叉树的最大宽度

文章目录题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;题目链接&#xff1a; 662. 二叉树最大宽度 题目描述&#xff1a; 解法 这里的宽度指的是一层的最右边的非空节点到一层的最左边的非空节点&#xff0c;一共的节点数。 解法一&#xff1a;硬来&am…

什么是3DVR?VR技术有哪些应用场景?

VR与3D技术解析及应用在高科技领域&#xff0c;VR和3D是两个常被提及的名词。那么&#xff0c;这两者之间究竟存在着怎样的区别与联系呢&#xff1f;简而来说&#xff0c;VR技术是3D技术的一种高级延展和深化应用。3D技术&#xff0c;即将二维设计图转化为立体、逼真的视觉效果…

栈与队列:数据结构核心解密

栈和队列的基本 栈(Stack)是一种后进先出(LIFO, Last In First Out)的数据结构。元素的插入和删除操作只能在栈顶进行。常见的操作包括压栈(push)和弹栈(pop)。 队列(Queue)是一种先进先出(FIFO, First In First Out)的数据结构。元素的插入在队尾进行,删除在队…

《C++初阶之STL》【list容器:详解 + 实现】

【list容器&#xff1a;详解 实现】目录前言------------标准接口介绍------------标准模板库中的list容器是什么样的呢&#xff1f;1. 常见的构造2. 迭代器操作std::list::beginstd::list::endstd::list::rbeginstd::list::rend3. 容量的操作std::list::sizestd::list::empty…

【灰度实验】——图像预处理(OpenCV)

目录 1 灰度图 2 最大值法 3 平均值法 4 加权均值法 5 两个极端的灰度值 将彩色图转为灰度图地过程称为灰度化。 灰度图是单通道图像&#xff0c;灰度化本质就是将彩色图的三通道合并成一个通道的过程。三种合并方法&#xff1a;最大值法&#xff0c;平均值法和加权均值法…

【linux驱动开发】编译linux驱动程序报错:ERROR: Kernel configuration is invalid.

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录一、报错二、解决方法1.先编译linux内核源码2.再重新编译驱动程序一、报错 在编译驱动程序过程中&#xff0c;经常碰到的一个小问题&#xff1a; make -C /home/lu…