Flink Kafka Connector源码解析:从分区发现到事务提交

以前我一直把 Flink 的 Kafka Connector 当黑盒用。启动参数调一调,序列化器配一配,能跑通就算完事。直到线上出了两个问题:一个是在 checkpoint 恢复后出现了一堆微妙的数据重复,另一个是 KafkaSink 在作业重启后卡死在事务上。那次事故逼着我打开了连接器的源码,一行一行往下读,才真正理解了偏移量提交、事务两阶段提交和状态恢复背后的逻辑。

这篇文章我会按读源码的顺序,把 KafkaSource 和 KafkaSink 两条链路拆开讲,重点说清楚:分片发现怎么工作、Reader 怎么从 Kafka 拉数据、checkpoint 时偏移量快照存了什么、事务提交的细节在哪里、以及各类常见异常的代码级原因。适合已经写过 Flink SQL 或 DataStream 作业、但没认真看过连接器实现的同学。代码版本围绕 Flink 1.14 到 1.17 之间主线的 Kafka Connector 模块,旧版 FlinkKafkaConsumer / FlinkKafkaProducer 也会在对应位置上提一下。

1. 为什么值得啃一遍 Kafka Connector 源码

1.1 新旧两代连接器的分水岭

Flink 在 1.14 开始全面推 Source API 和 Sink API,Kafka Connector 也因此分成了新旧两代。

旧的 FlinkKafkaConsumer 继承的是RichParallelSourceFunction,核心逻辑是启动一个内部 KafkaFetcher 线程,把 Kafka Consumer 封装进去,然后自己管理线程生命周期。旧的 FlinkKafkaProducer 则继承TwoPhaseCommitSinkFunction,把事务逻辑和 Checkpoint 机制耦合在一个抽象类里,维护成本很高,很多状态转换在恢复时容易出问题。

新的 KafkaSource 和 KafkaSink 则是基于 FLIP-27 的 Source/Sink API 重新设计的。KafkaSource 实现了Source接口,核心类包括KafkaSourceReader、KafkaSourceEnumerator、KafkaPartitionSplit;KafkaSink 实现了Sink接口,核心类包括KafkaSinkWriter、KafkaCommitter、KafkaTransactionLog。新版把“读取”“发现”“提交事务”三件事彻底解耦,每个环节都能单独测试,代码阅读体验比旧版好非常多。

如果你现在还在看旧版源码,我建议尽快切换到新版上。不是因为旧版一定有问题,而是新版的分层方式更能反映 Flink 未来的设计走向,而且后续很多新特性只在新版上迭代。

1.2 建议先搞清楚的几个源码位置

拿到 flink 源码仓库后,不需要整个项目都看,重点看flink-connectors/flink-connector-kafka模块。这个模块下分两块:

  • kafka-source包:KafkaSource 相关实现。
  • kafka-sink包:KafkaSink 相关实现。

再往下,每个包内部又按enumerator、reader、partitioner、deserializer、sink等职责拆目录。先读入口类,再顺藤摸瓜看调用关系。建议的阅读顺序是:先从KafkaSource这个入口扫一遍,然后去KafkaSourceEnumerator和KafkaPartitionSplitReader,最后再回到KafkaSourceReader看如何组装。Sink 侧则从KafkaSink入口进去,重点读KafkaSinkWriter的 checkpoint 路径。

这个顺序其实也是数据流动的顺序,顺着读下来,比直接钻进某个细节类要容易得多。

2. 从 KafkaSource Builder 到 Enumerator,分区发现到底怎么发生的

2.1 Builder 参数如何一步步变成配置

所有 KafkaSource 作业的起点都是KafkaSource.builder()。Builder 里能设置的项很多:BootstrapServer、Topic 列表、反序列化器、起始偏移量、消费组 ID、分区发现间隔、采样器等。

这些参数最终会转成KafkaSourceConf(老版本叫 KafkaSourceOptions),并缓存在KafkaSource的配置对象里。不少同学以为 Builder 只是简单做参数收集,实际上它还承担了参数合法性的校验。比如设置了setStartingOffsets又设置了setBounded,某些组合会被直接拒绝;再比如指定 timestamp 作为起始偏移时,内部会把时间记录下来,等到真正创建 partition split 时再通过 AdminClient 去查对应的 offset。

我建议读源码时重点看这个校验过程。很多线上作业启动失败的原因,其实都是 Builder 里传了不可达的 topic 或异常的参数组合,只是报错堆栈被外面一层层包装隐藏了。把校验逻辑搞明白,定位参数问题会快很多。

2.2 分片发现与动态分区扫描:分摊到哪个 Task 由谁决定

KafkaSource 把每个 topic 分区抽象成KafkaPartitionSplit,这个 split 里存放了 topic 名、分区 id、起始 offset、停止 offset,以及必要的动态分区发现上下文。

KafkaSourceEnumerator负责两件事:初始时把全量分区抓出来,运行过程中周期性地做动态分区发现。初始发现通过KafkaMetadataReader调用 AdminClient,列出所有 topic 的所有分区,然后根据订阅的重试、起始偏移量设置生成对应的 KafkaPartitionSplit。

动态分区发现的触发也很直接:KafkaSourceEnumerator内部持有一个PartitionDiscoveryInterval,按给定间隔(默认 300 秒)重新执行一次分区扫描。每次扫描之后,新增的分区会进入待分配队列,已经分配过的分区不再重复生成。

很多人碰到的问题是新分区迟迟没有被消费。这种情况几乎都能在动态分区发现的代码路径里找到原因。常见的有两种:一种是作业使用了无界流,但分区发现间隔被调得非常大;另一种是作业并行度没变,但新分区在分配时没有可用 reader 槽位,导致新 split 一直挂在 pending 队列里。

2.3 让源码自己“说话”:一段核心枚举器逻辑

KafkaSourceEnumerator的分配逻辑可以从assignSplits方法入手。核心思路是:

  • 当前有哪些 reader 已经注册,并且还没有被分配满 split。
  • 当前有哪些 split 还停在 pending 状态。
  • 给每个空闲 reader 分配一个 split,尽量保持均匀。

伪代码长这样:

protected void assignSplits() { final List<KafkaPartitionSplit> assignableSplits = pendingSplits; // 找到所有已注册但没有拿到 split 的 reader final List<SourceReaderBase<?, ?, ?>> availableReaders = findAvailableReaders(); // 按顺序轮询分配,直到 pending 为空或没有可用 reader while (assignableSplits.size() > 0 && availableReaders.size() > 0) { KafkaPartitionSplit split = assignableSplits.remove(0); int readerIndex = getNextReaderIndex(); assignSplit(readerIndex, split); } }

源码不会这么简洁,但骨架就是这个意思。注意它用的是“轮询 + 有空闲再分配”的思路,不是按 partition id 取模。这带来的直接后果是:并行度变化或新分区注入后,分区的分布并不严格均匀,所以在动并行度前要做好心理准备,部分 Task 会有明显的流量倾斜,尤其在分区数较少时。

还有一个容易忽略的细节:KafkaSourceEnumerator实现了SupportsSplitSnapshotting接口,这意味着它自己也参与 checkpoint。checkpoint 时,枚举器会把“尚未分配出去的 split”保存到状态里;故障恢复时,restoreFromState再从状态里取出这些 split,重新走分配流程。这保证了“发现了但还没被消费者读取”的分区不会在恢复过程中丢失。

3. Reader 侧源码:数据怎么进到 Flink 算子

3.1 SourceReaderBase 与 KafkaPartitionSplitReader 的分工

KafkaSourceReader继承的是SourceReaderBase,SourceReaderBase把 split 生命周期和 fetch 循环统一管理。真正和 Kafka 打交道的是KafkaPartitionSplitReader。

这种分工特别像“项目经理 + 干活的师傅”。SourceReaderBase 负责对接 Flink 引擎,比如汇报当前可用记录数、处理 checkpoint 触发、响应重启等;而 KafkaPartitionSplitReader 负责跟 Kafka Consumer 交互:assign 一个或多个 split、poll 数据、做反序列化、处理 cleanup。

KafkaPartitionSplitReader里维护着一个 KafkaConsumer 实例,每个 split 被分配给某个 reader 后,reader 会调用consumer.assign()把对应分区挂到这个 consumer 上,再调用consumer.seek()跳到 split 指定的起始 offset。

3.2 poll 循环、解码与反压

KafkaPartitionSplitReader的核心方法是pollNext()。这个方法会从内部 KafkaConsumer 里拉一批消息,然后交给KafkaRecordDeserializer逐条反序列化,最终封装成RecordAndPosition之类的记录结构。

这里有几个源码层面的关键点:

  • 每次 poll 不是拉一条,而是一批。读完一批之后,fetch返回记录数,SourceReaderBase通过这个数字来向引擎申请发送权。
  • 如果这个批次里有很多条记录,会形成一个队列,pollNext被反复调用时先从队列里逐条吐给上游算子,队列吐完后再去 Kafka 拉下一批。
  • 当整个 channel 反压时,SourceReader 不会无限拉数据,而是由SourceReaderBase控制唤醒时机。简单说,Flink 会给 reader 一个可用的“贷款额度”,拉完这个额度就停住,直到下游背压解除。

这个设计直接影响了吞吐和延迟。实测中,如果下游算子计算量大,Kafka reader 端 CPU 占用会先降下来,因为不需要继续拉数据。反过来如果下游很快,reader 端 poll 循环的调用频率会非常高,此时 Kafka consumer 内部的缓冲设置、fetch.max.bytes 这些参数才真正产生作用。

3.3 分区内顺序性:一个容易被忽略的开关

一个 reader 可能同时分配了多个 topic 分区。默认情况下,KafkaSourceReader从多个 split 拉到的记录,是按“拉取顺序”交给下游的,而不是按某个全局顺序。

如果业务上要求严格按分区内 offset 顺序处理,可以留意 KafkaSource 的setRecordsSort配置。不过我要提醒一句,开启排序会带来额外的排序开销和延迟,不是无脑开的。绝大多数实时计算作业不需要跨分区严格有序,只需要保证“单分区内不丢不重”,那么默认行为就够了。

真正需要关注的反而是“暂停”逻辑:SourceReaderBase在被分配新 split 时,会对旧 split 做 pause/resume 操作,这部分代码在并发场景下有一些细节。以前被坑过一次,并发度高时出现偶发的乱序,后来定位到是旧版KafkaSourceReader中某个竞态条件,升级版本后解决。所以遇到这类问题,可以把连接器版本升级到最新主线再验证。

4. Checkpoint 时偏移量快照与恢复的源码逻辑

4.1 SnapshotState 里到底存了哪些状态

KafkaSource 的 checkpoint 状态存在KafkaSourceState里。它看起来就是一个封装结构,里面大致有三个维度:

  • 每个还没处理完的 split 当前消费到的 offset。
  • 还没分配给 reader 的 pending split。
  • 因为某些原因被临时暂停的 split 列表。

对应代码里的字段大概是currentOffsets、pendingSplits、pendingSplitsState的组合。

很多人以为 checkpoint 存的是“消费组 offset”,这是天大的误解。KafkaSource 并不会在每次 checkpoint 时把 offset 提交到 Kafka broker,它只是把当前进度存到 Flink 的 state backend。真正提交到 Kafka 消费组的动作,要么由offsetCommitMode控制,要么在 checkpoint 完成后的回调里做。

所以这里存在一个非常重要的推导:如果只把记录写到了下游存储,但 Flink 状态里没记下当前 offset,那么重启之后就会从旧 offset 继续消费,于是出现“看起来像是重复消费”的现象。这不是连接器 bug,而是状态与偏移量没有统一。

4.2 恢复/扩缩容时 offset 被重新分配的过程

当作业从 checkpoint 恢复时,引擎会把KafkaSourceState交还给对应并行子任务的KafkaSourceReader。每个 reader 根据 state 里记录的 split 和 offset 重建消费状态。

扩缩容的场景比较复杂。假设原来并行度是 3,现在改成 5,那么旧 3 个 reader 的状态需要被重新映射到新的 5 个 reader 上。Flink 的状态恢复机制是按 keyed state 来重分配的,但KafkaSourceState不是 keyed state,它走的是 operator state 的 union list 模式。

以我实际的经验,扩并行度最怕遇到的一种情况是:新 reader 拿到的 state 里包含一个 split,但该 split 的 offset 记录的是“已经消费到一半”的状态,恢复后 Kafka 上已经没有对应的 offset 了,于是抛OffsetOutOfRangeException。这个问题在早期版本比较常见,后来版本对 split 的 finished 状态处理更完善,但依然建议你在扩容前先做一次消费位点确认。

4.3 消费端精确一次为什么不是端到端幂等

读完源码你就会发现,KafkaSource 的 checkpoint 机制能保证的是:Flink 内部不丢数据,并且在故障恢复时,每条记录恰好被处理一次。但这里的“恰好一次”是相对于 Flink 状态而言的。

如果下游是另一个 Kafka topic、数据库或 API,那么“端到端精确一次”还需要下游存储支持事务或幂等写入。比如 KafkaSink 的精确一次是基于 Kafka 事务实现的,数据库接收端则要自己保证按主键去重。很多人把“Flink 消费 Kafka 的精确一次语义”和“整个数据链路不重不漏”混为一谈,源码读得越深越会发现,这完全是两个层面。

5. KafkaSink 侧源码:事务如何两阶段提交

5.1 从 Flush 到 Committable

KafkaSink 的写入链路是:上游算子把记录发送给KafkaSinkWriter,Writer 立刻调用KafkaProducer.send()发到 Kafka 的缓冲批次里,这一步是异步的。

当 Flink 触发 checkpoint 时,KafkaSinkWriter会执行flush(),确保所有已经 send 出去的记录真正完成了网络发送,然后针对当前 checkpoint 创建KafkaCommittable,并把它作为待提交状态交给框架。之后,checkpoint 完成时,KafkaCommitter拿到KafkaCommittable,对 Kafka 事务执行 commit。

这实际上就是经典两阶段提交在 Flink 里的落地方式:

  • 预提交阶段:flush 所有写缓冲,写事务标记。
  • 正式提交阶段:在 checkpoint 完成回调里 commit 事务。

如果任务失败,上一次未完成的事务会被 abort,避免脏数据提交。

5.2 TransactionalId 的生成规则与坑

精确一次模式要求 Kafka Producer 开启事务,每个事务都要有唯一的 transactional.id。KafkaSink 生成 transactional id 的规则是“前缀 + 算子索引 + 子任务索引 + checkpoint 编号”拼接。

这个唯一性非常重要。如果两个并发子任务生成了相同的 transactional id,Kafka 会拒绝第二个 producer 的初始化,抛出ProducerFencedException。反过来说,如果配置了过高的并行度但没有正确设置transactional.id.prefix,在多作业复用同一个 Kafka 集群时,也可能因为前缀冲突导致事务被 fence。

我遇到过一类经典问题:作业 A 和作业 B 写同一个 topic,同时把 KafkaSink 的transactionalIdPrefix配成一样,结果两边的 producer 互相抢占事务,引发频繁的ProducerFencedException。解决办法就是把前缀改成按业务线隔离。

5.3 事务超时那些让人头大的异常

KafkaSink 的精确一次还会遇到TransactionTimeoutException。源码里事务超时由transaction.timeout.ms控制,默认好像是 3600000 毫秒? 呃,是 1 小时。看代码时你会发现,Kafka 服务端的transaction.max.timeout.ms限制了客户端可设置的最大超时,如果超时时间超过这个上限,初始化 producer 时就会直接报错。

另一个坑是 checkpoint 间隔跟事务超时时间的关系。如果 checkpoint 间隔太长,事务可能长时间没提交,等真正提交时已经超过了允许的事务时长,同样会失败。如果你的作业经常报TransactionTimeoutException,先检查两件事:

  • transaction.timeout.ms是否接近或超过transaction.max.timeout.ms。
  • checkpoint 周期是否明显大于事务超时时间。

6. 常见问题与排查技巧实录

6.1 问题速查表

这部分是我在开发和维护过程中反复遇到的,整理成一张速查表:

症状可能原因排查方向解决建议
启动报错找不到 offset从未消费过,且使用了committedOffsets初始化检查 group.id,检查起始偏移配置换成 earliest 或已存在的消费组
新分区迟迟不被消费分区发现间隔太长 / 无空闲 reader观察 TaskManager 日志中 enumerator 扫描日志把 discovery interval 调短,或提升并行度
数据大量重复下游写入不支持幂等 / offset 未提交检查 checkpoint 和下游存储让下游幂等,或启用事务写入
作业恢复半天不成功state 中 split 对应的 offset 已被 kafka 清理查看底层 OffsetsOutOfRange 日志清理旧状态或用 earliest 重新启动
频繁ProducerFencedExceptiontransactionalId 冲突查看 transactionId 前缀配置换全局唯一前缀
TransactionTimeoutException事务超时设置不合理检查 producer 端超时和 checkpoint 间隔调整超时参数,缩短 checkpoint 周期
并行度调整后消费倾斜枚举器轮询分配策略导致观察各 subtask 消费速率手动调整并行度,或接受短时倾斜

这些坑大多不是单点问题,排查时先看异常类型,再看状态链路。我习惯的第一动作是:打开 Flink UI 的 checkpoint 页面,看有没有频繁失败;如果有,再去看对应 subtask 的异常堆栈。绝大部分 Kafka Connector 问题,都能在这个路径里定位出根因。

6.2 源码调试环境与断点位置建议

如果你也想边读边调试,可以考虑本地搭一个 Flink 源码调试环境:

  1. 拉取 flink 仓库,切到稳定的 release 分支。
  2. 编译flink-connector-kafka模块,默认会有一个带依赖的 jar。
  3. 用 IDEA 打开工程,找到connectors/kafka目录。
  4. 在flink-runtime里直接用MiniCluster写一个简单的 WordCount 任务,输入源设为 KafkaSource,sink 设为打印,这样能触发完整的 reader、enumerator 逻辑。
  5. 直接在KafkaPartitionSplitReader.pollNext()、KafkaSourceReader.snapshotState()、KafkaSinkWriter.flush()、KafkaCommitter.commit()这几个方法上下断点。

断点位置建议先从“数据拉取”开始观察,你会发现每次 poll 出来的记录数、反序列化结果、offset 自增逻辑一目了然。然后走到 checkpoint 路径,看状态内容是怎么打包、序列化的。

我个人体会是:读 Kafka Connector 源码,不要贪多,每次只追一个完整场景。比如“无界流从最早位点开始消费”,把这个场景从 Builder 一直追到 Sink,所有环节串起来之后,你对 Flink 状态管理和两阶段提交的理解会上一个台阶。

另外有一个小建议。改源码前一定注意flink-connector-kafka和flink-core、flink-streaming-java的版本一致性问题,否则调试半天发现跑的还是旧的 jar,白白浪费时间。

相关新闻

采集网关性能优化:ArrayPool、零拷贝排序、背压与批量写入实战

采集网关性能优化:ArrayPool、零拷贝排序、背压与批量写入实战

先把结论放在前面&#xff1a;在高频设备数据采集场景里&#xff0c;你最该担心的不是“业务逻辑能不能算完”&#xff0c;而是内存怎么分配、数据怎么排队、下游扛不住时系统怎么退让。ArrayPool池化缓冲、零拷贝排序、背压机制、异步批量写入、防 OOM 设计&#xff0c;这五件…

2026/10/20 14:09:27 阅读更多 →
本地AI模型Web UI扩展:给DeepSeek加一层浏览器里的操作界面

本地AI模型Web UI扩展:给DeepSeek加一层浏览器里的操作界面

简介&#xff1a;Page Assist 是面向 Chrome 与 Firefox 的本地 AI 模型 Web UI 插件&#xff0c;基于 Deepseek 模型运行&#xff0c;为注重隐私与离线体验的用户提供浏览器内的智能问答与页面内容分析能力。压缩包内含两个浏览器版本&#xff0c;其中 Chrome 版已实测可用 De…

2026/10/20 14:09:27 阅读更多 →
pyasc编译链解析:从Python语法到AI Core指令的NPU算子开发

pyasc编译链解析:从Python语法到AI Core指令的NPU算子开发

做 AI 芯片算子开发的人应该都有同感&#xff1a;写 Python 一时爽&#xff0c;但要让它跑在 NPU 上&#xff0c;中间那道坎能把人折腾死。CANN 工具链里的 pyasc 要解决的就是这件事——把 Python 语法一层一层翻译成硬件能直接执行的指令&#xff0c;而不是靠解释器去硬跑。p…

2026/10/20 14:09:27 阅读更多 →

最新新闻

60次实测:MCP不省上下文,子agent平均省38.1%token增量

60次实测:MCP不省上下文,子agent平均省38.1%token增量

做Agent应用的人&#xff0c;大概都被问过一个问题&#xff1a;接上MCP之后&#xff0c;上下文是不是就能省不少&#xff1f;我前两天刚跑完一轮60次真实任务的对照实测&#xff0c;结论很干脆——MCP在省上下文这件事上的贡献是精确的0&#xff1b;而换成子agent路线&#xff…

2026/10/20 14:46:40 阅读更多 →
RRT-Connect双树搜索MATLAB代码详解:从原理到实现

RRT-Connect双树搜索MATLAB代码详解:从原理到实现

简介&#xff1a;面向机器人运动规划与路径规划研究者&#xff0c;这份资源提供了RRTConnect算法的完整MATLAB实现。RRTConnect在经典RRT基础上采用双向搜索策略&#xff0c;适合解决高维空间或复杂环境下两点间快速路径规划问题&#xff0c;对自动化、机器人及相关专业学生开展…

2026/10/20 14:46:40 阅读更多 →
Windows下Mask R-CNN PyTorch原生编译与部署实战

Windows下Mask R-CNN PyTorch原生编译与部署实战

1. 为什么在Windows上配Mask R-CNN不是“装个包”那么简单 Mask R-CNN是目标检测与实例分割领域的标杆模型&#xff0c;PyTorch版因其模块清晰、易于调试、社区生态活跃&#xff0c;成为多数研究者和工程落地首选。但当你在Windows系统上打开终端&#xff0c;输入 pip install…

2026/10/20 14:46:40 阅读更多 →
AI生成实操型博文的安全与格式规范要点

AI生成实操型博文的安全与格式规范要点

好的&#xff0c;我将严格遵循安全与格式要求&#xff0c;基于标题生成一篇完整、可直接发布的实操型博文&#xff0c;全程不涉及任何风险或敏感内容。因内容较长&#xff0c;我会分两条消息完成全文。

2026/10/20 14:46:40 阅读更多 →
n8n外部执行器部署指南:独立代码沙箱隔离与Docker Compose实战

n8n外部执行器部署指南:独立代码沙箱隔离与Docker Compose实战

这两年用 n8n 做流程自动化的人越来越多&#xff0c;但大部分人把 n8n 部署好以后就默认开工了&#xff0c;根本不会注意到一个关键组件——任务执行器。n8n 从 2.x 版本开始引入了外部执行器机制&#xff0c;简单说就是把 Code 节点、自定义 JS、Python 脚本这类代码运行环境从…

2026/10/20 14:46:40 阅读更多 →
Ubuntu 24.04用Docker Compose部署Whisper语音识别服务全指南

Ubuntu 24.04用Docker Compose部署Whisper语音识别服务全指南

如果你需要在Ubuntu 24.04上搭一个完全本地的语音识别服务&#xff0c;把录音、播客、会议音频批量转成文本&#xff0c;同时又不想被一堆Python依赖折腾到怀疑人生&#xff0c;那Whisper配合Docker Compose几乎是我目前试过最省心的组合。我最近把整套流程从零跑通&#xff0c…

2026/10/20 14:45:32 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →