Flink批流双模式Word Count实战:从入门到避坑
1. 从一个Word Count说起为什么它是Flink的Hello World但凡接触过大数据处理的人对Word Count都不会陌生。它就像编程语言里的Hello World看似简单却涵盖了数据处理框架最核心的几个概念数据读取、转换、分组、聚合、输出。我刚开始接触Flink的时候也是从一个Word Count案例入手的当时觉得这玩意儿能有多难结果真正跑起来才发现批处理和流处理两种模式下代码写法、执行逻辑、结果输出都有不少门道。这篇文章要聊的就是如何用Flink分别以批处理和流处理两种方式实现Word Count。你可能会问一个Word Count有什么好讲的网上不是一搜一大把吗说实话确实很多但大部分要么只给代码不讲原理要么版本太老跑不起来要么批和流混在一起讲得云里雾里。我踩过的坑包括但不限于依赖版本冲突导致NoSuchMethodError、流处理模式下文件读取完程序不退出、批处理模式下ExecutionEnvironment和StreamExecutionEnvironment搞混导致编译报错等等。所以这篇内容适合以下人群刚接触Flink想跑通第一个案例的新手、从Spark Streaming或其他框架转过来想快速上手Flink的开发者、以及想搞清楚批流两种模式底层差异的进阶学习者。我会从环境搭建开始把两种模式的代码逐行拆解补充参数配置、执行原理、常见报错和排查思路让你看完就能自己跑起来而且知道每一步为什么这么写。提示本文基于Flink 1.17.x版本编写不同版本API可能有差异尤其是1.12之前和之后的版本在DataStream API上有较大变化建议尽量使用1.14以上的稳定版本。2. 环境准备与项目骨架搭建2.1 开发环境选型和依赖管理工欲善其事必先利其器。Flink开发的环境准备其实不复杂但有几个关键点容易翻车。首先是JDK版本。Flink 1.17要求JDK 8或11我实测下来JDK 11最稳JDK 8也能跑但某些新API会有警告。JDK 17目前兼容性还不够好不建议新手折腾。你可以用java -version确认一下当前版本。其次是构建工具。Maven和Gradle都行但社区里Maven的示例更多遇到问题好搜解决方案所以我这里用Maven演示。创建一个标准的Maven项目pom.xml里核心依赖就两个flink-streaming-java和flink-clients。注意如果你要跑批处理不需要额外引入批处理的依赖因为Flink 1.12之后批处理统一用DataStream API的ExecutionEnvironment来做了DataSet API已经进入维护状态官方建议新项目直接用DataStream API的批模式。properties flink.version1.17.1/flink.version java.version11/java.version /properties dependencies dependency groupIdorg.apache.flink/groupId artifactIdflink-streaming-java/artifactId version${flink.version}/version /dependency dependency groupIdorg.apache.flink/groupId artifactIdflink-clients/artifactId version${flink.version}/version /dependency /dependencies这里有个坑要提醒很多人会习惯性地加上flink-java依赖觉得批处理肯定需要它。实际上从1.12开始flink-java里的DataSet API已经不推荐使用了加了反而可能引起依赖冲突。我自己就遇到过同时引入flink-java和flink-streaming-java后ExecutionEnvironment类加载到了错误的版本编译能过但运行时报ClassNotFoundException。2.2 输入数据准备与项目目录结构Word Count的输入数据很简单一个文本文件就行。我在项目根目录下建了个input文件夹放了一个words.txt内容随便写几行英文单词比如hello flink hello world flink batch and stream word count example hello hello flink项目结构大概是这样flink-wordcount/ ├── pom.xml ├── input/ │ └── words.txt └── src/ └── main/ └── java/ └── com/ └── example/ ├── BatchWordCount.java └── StreamWordCount.java注意输入文件路径在代码里写相对路径时要搞清楚是相对于项目根目录还是相对于运行时的classpath。IDEA里直接Run main方法工作目录默认是项目根目录所以input/words.txt能直接找到。但如果你打成jar包提交到集群路径就得改成绝对路径或者HDFS路径这个后面会细说。3. 批处理模式实现Word Count3.1 批处理代码逐行拆解先上完整代码然后我逐段解释为什么这么写。package com.example; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; public class BatchWordCount { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.setRuntimeMode(org.apache.flink.api.common.RuntimeExecutionMode.BATCH); DataStreamString text env.readTextFile(input/words.txt); DataStreamTuple2String, Integer counts text .flatMap((String line, org.apache.flink.util.CollectorTuple2String, Integer out) - { for (String word : line.split( )) { out.collect(Tuple2.of(word, 1)); } }) .returns(Types.TUPLE(Types.STRING, Types.INT)) .keyBy(value - value.f0) .sum(1); counts.print(); env.execute(Batch WordCount); } }第一行StreamExecutionEnvironment.getExecutionEnvironment()获取执行环境。这里你可能注意到批处理用的也是StreamExecutionEnvironment而不是ExecutionEnvironment。这就是Flink 1.12之后的变化——批流统一用DataStream API通过setRuntimeMode来指定是BATCH还是STREAMING模式。如果你在网上看到用ExecutionEnvironment和DataSet的代码那是老版本的写法虽然还能跑但已经不推荐了。env.setRuntimeMode(RuntimeExecutionMode.BATCH)这行是关键。它告诉Flink以批处理模式执行底层会用更高效的批处理优化策略比如对排序、分组操作做特殊优化而不是像流处理那样逐条处理。env.readTextFile(input/words.txt)读取文本文件返回一个DataStreamString每一行是一个元素。接下来是核心的转换逻辑。flatMap把每一行拆成单词并输出(word, 1)的二元组。这里有个细节Java的Lambda表达式在Flink里会有类型擦除问题所以必须加.returns(Types.TUPLE(Types.STRING, Types.INT))显式声明返回类型否则运行时会报InvalidTypesException。这个坑我踩过不止一次尤其是从Scala转Java的人特别容易忘。keyBy(value - value.f0)按单词分组sum(1)对第二个字段求和。注意keyBy返回的是KeyedStreamsum之后又变回DataStream。最后counts.print()把结果打印到控制台env.execute()触发实际执行。Flink是懒执行的前面所有的转换操作只是构建了执行图只有调用execute()才会真正提交任务。3.2 批处理执行原理与输出解读跑起来之后控制台输出大概是这样(flink,3) (hello,4) (world,1) (batch,1) (and,1) (stream,1) (word,1) (count,1) (example,1)你会发现输出顺序和输入顺序不一致。这是因为批处理模式下Flink会对数据进行分组和排序优化相同key的数据会被分配到同一个分区处理最终输出的顺序取决于分区和合并的策略。这不是bug是批处理的正常行为。批处理模式的核心特点是有界数据、全量计算、结果一次性输出。Flink在批模式下会尽可能优化执行计划比如对keyBy后的数据做排序而不是哈希减少内存压力。如果你处理的是历史日志分析、离线报表这类场景批模式是首选。实操心得批处理模式下print()的输出会带上线程ID前缀如1这是并行执行的标识。如果你想让结果按单词字母序输出可以在sum之后加一个.sortPartition(0, Order.ASCENDING)但注意这会产生额外的排序开销数据量大时慎用。4. 流处理模式实现Word Count4.1 流处理代码与批处理的差异对比流处理的代码和批处理长得很像但有几个关键区别。package com.example; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; public class StreamWordCount { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); DataStreamString text env.readTextFile(input/words.txt); DataStreamTuple2String, Integer counts text .flatMap((String line, org.apache.flink.util.CollectorTuple2String, Integer out) - { for (String word : line.split( )) { out.collect(Tuple2.of(word, 1)); } }) .returns(Types.TUPLE(Types.STRING, Types.INT)) .keyBy(value - value.f0) .sum(1); counts.print(); env.execute(Stream WordCount); } }乍一看几乎一样区别在于没有setRuntimeMode(BATCH)默认就是STREAMING模式加了env.setParallelism(1)把并行度设为1方便观察输出顺序。流处理模式下readTextFile读取的是有界数据但Flink会把它当作流来处理——逐行读取、逐条处理、每来一条数据就更新结果并输出。所以你会看到输出是这样的(hello,1) (flink,1) (hello,2) (world,1) (hello,3) (flink,2) ...每处理一个单词就输出一次当前累计结果这就是流处理的增量计算特性。和批处理的全量计算不同流处理是来一条算一条结果实时更新。4.2 流处理模式的核心机制解析流处理模式有几个批处理没有的概念理解它们对后续深入学习很重要。并行度与任务槽。setParallelism(1)意味着所有操作符都在一个线程里串行执行。如果不设置默认并行度是CPU核数输出会交错在一起看起来比较乱。实际生产环境中并行度根据数据量和集群资源来定一般设置为Kafka分区数的整数倍比较合理。KeyBy的哈希分区。流处理模式下keyBy会对key做哈希然后对并行度取模决定数据发往哪个分区。相同key的数据一定在同一个分区这样才能正确累加。批处理模式下虽然逻辑一样但底层实现可能用排序代替哈希这是Flink的内部优化。事件时间与水位线。这个Word Count案例用的是处理时间Processing Time也就是数据被处理的系统时间。实际流处理场景中数据可能乱序到达就需要用事件时间Event Time加水位线Watermark来处理。不过这是进阶内容入门阶段先不用管。注意流处理模式下如果读取的是有界文件程序处理完所有数据后会自动退出。但如果读取的是Kafka这类无界数据源程序会一直运行需要用CtrlC或者提交取消命令来停止。我见过有同学跑流处理Word Count读的是socket数据源结果程序不退出以为代码写错了其实是正常行为。5. 批流两种模式的深度对比与选型建议5.1 执行模式、性能与适用场景对比很多人学完批和流两个版本后最大的困惑是到底什么时候用哪个我把关键差异整理成了一张表方便你对照。对比维度批处理模式流处理模式数据特征有界数据文件、历史表无界数据消息队列、实时日志计算方式全量计算一次性输出增量计算持续输出执行模式RuntimeExecutionMode.BATCHRuntimeExecutionMode.STREAMING延迟分钟级到小时级毫秒级到秒级结果输出最终结果每次更新的中间结果资源占用相对较高需要排序、缓存相对较低逐条处理典型场景离线报表、历史数据分析实时监控、实时推荐、告警从性能角度看批处理模式在处理有界数据时通常更快因为Flink会做执行计划优化比如算子融合、排序合并等。流处理模式胜在低延迟但吞吐量可能不如批处理因为每条数据都要走完整的处理链路。选型的核心判断标准就一条你的数据有没有边界业务对延迟的要求是什么。如果数据是有限的、可以等几分钟甚至几小时出结果用批处理。如果数据源源不断、需要秒级甚至毫秒级响应用流处理。5.2 代码复用与批流一体的实践思路既然两种模式代码这么像能不能写一份代码同时支持批和流答案是肯定的这也是Flink批流一体理念的体现。核心思路是把setRuntimeMode做成可配置的通过命令行参数或者配置文件来决定用哪种模式。数据源也可以抽象批处理用readTextFile流处理用addSource比如Kafka Source但后续的转换逻辑完全复用。// 伪代码示意 StreamExecutionEnvironment env StreamExecutionEnvironment.getExecutionEnvironment(); if (isBatch) { env.setRuntimeMode(RuntimeExecutionMode.BATCH); text env.readTextFile(path); } else { env.setRuntimeMode(RuntimeExecutionMode.STREAMING); text env.addSource(kafkaSource); } // 后续转换逻辑完全一样这样做的好处是维护一套代码降低开发和测试成本。不过要注意批和流的语义差异还是存在的比如流处理中的状态管理、水位线机制在批处理中不存在复杂场景下还是需要分别处理。实操心得我在实际项目中会先把批处理版本跑通确认逻辑正确再改成流处理版本。因为批处理调试更方便结果确定容易验证。流处理涉及时间语义和状态调试成本高很多。这个顺序反过来会浪费很多时间。6. 常见报错与排查技巧实录6.1 依赖冲突与类型擦除问题问题一NoSuchMethodError或ClassNotFoundException这是最典型的依赖冲突。表现是编译能过运行时报找不到类或方法。原因通常是Flink版本不一致或者引入了多个版本的Flink依赖。排查方法用mvn dependency:tree查看依赖树确认所有Flink相关依赖版本一致。如果引入了flink-java和flink-streaming-java检查版本号是否相同。另外flink-clients在1.17里是必须的不加会报找不到StreamExecutionEnvironment的实现类。问题二InvalidTypesException: The generic type parameters of Collector are missing这是Lambda类型擦除导致的。Flink无法从Lambda表达式推断出返回类型必须用.returns()显式声明。解决方法在flatMap后面加.returns(Types.TUPLE(Types.STRING, Types.INT))。如果是其他类型用Types.POJO(YourClass.class)或TypeInformation.of(YourClass.class)。问题三keyBy之后sum结果不对检查keyBy的字段索引是否正确。Tuple2的f0是第一个字段f1是第二个字段。sum(1)表示对第二个字段求和。如果写成了sum(0)会对字符串求和直接报错。6.2 运行环境与并行度相关故障问题四输出结果顺序混乱这是并行度导致的。默认并行度是CPU核数多个线程同时输出顺序自然乱。解决方法env.setParallelism(1)或者接受乱序因为分布式计算本来就不保证全局有序。问题五流处理程序不退出如果数据源是有界文件处理完会自动退出。如果是Kafka或Socket等无界源程序会一直运行。这是正常行为不是bug。需要手动停止或通过Flink的取消机制停止。问题六文件路径找不到readTextFile的路径在本地运行时相对于项目根目录提交到集群时相对于TaskManager的工作目录。建议用绝对路径或HDFS路径。如果路径是file:///...开头表示本地文件系统如果是hdfs://...表示HDFS。报错信息可能原因解决方法NoSuchMethodError依赖版本冲突统一Flink版本检查依赖树InvalidTypesExceptionLambda类型擦除加.returns()显式声明类型ClassNotFoundException缺少flink-clients依赖添加flink-clients依赖结果顺序混乱并行度大于1setParallelism(1)或接受乱序程序不退出无界数据源正常行为手动停止文件找不到路径问题用绝对路径或HDFS路径避坑技巧我习惯在main方法第一行加env.setParallelism(1)调试阶段先保证结果可预测等逻辑验证无误后再调大并行度做性能测试。这个习惯帮我省了很多排查时间。7. 从Word Count延伸下一步可以学什么Word Count跑通之后其实只是打开了Flink的大门。基于这个案例你可以往几个方向继续深入。第一个方向是换数据源。把readTextFile换成Kafka Source体验真正的流处理。你会接触到KafkaSource的配置、偏移量管理、水位线生成等概念。这个过程中Word Count的逻辑不变但数据接入方式变了能帮你理解Flink的数据源抽象。第二个方向是加时间语义。给数据加上时间戳用事件时间代替处理时间配合水位线处理乱序数据。这是流处理的核心难点也是面试高频考点。你可以把输入数据改成带时间戳的格式然后观察窗口计算的结果变化。第三个方向是状态管理。Word Count里的sum其实是有状态的Flink帮你管理了状态。你可以进一步学习KeyedState、OperatorState了解状态后端Memory、Fs、RocksDB的区别和选型。数据量大时状态后端的选择直接影响性能和稳定性。第四个方向是容错机制。了解Checkpoint和Savepoint的原理知道Flink怎么保证Exactly-Once语义。这个在Word Count案例里体现不出来但生产环境必不可少。我自己是从Word Count开始然后逐步加了Kafka源、事件时间、窗口、状态最后搭了一个完整的实时统计Demo。每一步都会遇到新问题但每一步也都能加深对Flink的理解。建议你不要急着上复杂场景先把Word Count的批和流两个版本吃透把依赖、类型、并行度这些基础问题搞清楚后面会顺很多。最后分享一个小技巧Flink的Web UI是个好东西。本地运行时会启动一个Web界面默认8081端口可以看到执行计划、算子链、数据流量、背压情况。跑Word Count的时候打开看看能直观理解Flink是怎么把代码变成执行图的。这个习惯我从入门保持到现在排查问题时特别有用。

相关新闻

用AI写教材如何降低查重率?从查重原理到文本改造的完整方法论

用AI写教材如何降低查重率?从查重原理到文本改造的完整方法论

用AI写教材这件事,核心痛点永远是同一个:内容看着挺顺眼,一查重就飘红。我见过不少人拿AI生成的初稿直接提交,查重率直奔百分之三四十,然后开始怀疑是不是工具选错了。说实话,工具确实重要,但如…

2026/10/17 12:03:39 阅读更多 →
Claude Code深度实战:从AI编程助手到独立处理复杂任务的资深工程师

Claude Code深度实战:从AI编程助手到独立处理复杂任务的资深工程师

我第一次用 Claude Code 处理一个跨模块重构任务时,最大的感受不是“模型又变聪明了”,而是工作方式真的变了——它不再是一个等着我把代码贴进去再给建议的对话框,而像一个可以独立自主处理复杂任务的资深工程师:自己读代码、自己…

2026/10/17 12:03:39 阅读更多 →
Agent记忆系统从0到1:前端转LLM必懂的短期、情景与语义记忆架构

Agent记忆系统从0到1:前端转LLM必懂的短期、情景与语义记忆架构

做 Agent 开发,前五篇聊了 Prompt 工程、工具调用、结构化输出这些基础,但这些都不是 Agent 区别于普通脚本的真正分水岭。到第六篇终于要碰那块最硬、也最容易被忽略的骨头——Agent Memory,记忆系统。我的感受很直接:开发过带记…

2026/10/17 12:02:38 阅读更多 →

最新新闻

Multi-Agent创业策略:避开通用平台,在垂直场景打造差异化护城河

Multi-Agent创业策略:避开通用平台,在垂直场景打造差异化护城河

一个残酷但真实的现象:近半年我接触了不少想靠Multi-Agent创业的团队,大多数人一开口就是“我们要做一个通用的Agent平台”。每次听到这句话,我内心都会咯噔一下。通用平台这件事,听起来性感和宏大,但在巨头生态已经成…

2026/10/17 17:52:35 阅读更多 →
VSCode 配置 MSVC 与 EasyX:C++ 图形编程环境搭建指南

VSCode 配置 MSVC 与 EasyX:C++ 图形编程环境搭建指南

1. 为什么要在 VSCode 里折腾 MSVC 加 EasyX 这套组合先把场景说清楚:你手里有一台 Windows 机器,想用 C 写点带图形界面的小玩意儿——画个圆、贴张图、做个贪吃蛇或者扫雷,但又不想背 Visual Studio 那套庞大的 IDE 包袱,只想在…

2026/10/17 17:52:35 阅读更多 →
大模型Agent面试必备:考点地图、高频问题与备战攻略

大模型Agent面试必备:考点地图、高频问题与备战攻略

大模型 Agent 方向的面试,这两年热度一直没降。我既作为候选人面过不少团队,也坐在面试官的位置上筛过简历、出过题、追问过候选人。一个很直观的感受是:这个方向看起来很“热”,但很多人的准备方式还停留在“背八股”阶段——模型…

2026/10/17 17:52:35 阅读更多 →
LangChain提示词管理实战:用YAML配置模板告别硬编码

LangChain提示词管理实战:用YAML配置模板告别硬编码

最早我管理提示词是硬编码派的。所有模板直接写在业务代码里,用的时候拼字符串,改完靠发版上生产。直到有一回,一个助手类产品在线上稳定跑了两个月,业务突然说要调整开场白语气。我心想不就改一句话吗,结果走了一遍完…

2026/10/17 17:52:34 阅读更多 →
模式识别算法MATLAB实现:从特征提取到分类器与聚类实战

模式识别算法MATLAB实现:从特征提取到分类器与聚类实战

简介:《模式识别与智能计算——MATLAB技术实现》(第3版)是一本面向模式识别与智能计算学习者的系统教程,适合计算机、信息工程、智能机器人等专业的学生及科研工程人员阅读。全书以统计学、神经网络、机器学习、群智能计算等交叉学…

2026/10/17 17:52:34 阅读更多 →
数据指标多重应用:从口径定义到业务决策的实战指南

数据指标多重应用:从口径定义到业务决策的实战指南

看一眼标题,很多人第一反应是“哪个指标?”,然后就没有然后了。做数据和运营这些年,最明显的一个感受是:大家天天在看日活、看留存、看转化率,但这些“熟悉得不能再熟悉”的指标,绝大多数时候只…

2026/10/17 17:51:34 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →