验收口径
把「验收」拆成四道门禁,每道都给出可执行的判据:
| 门禁 | 判据 | 当前状态 |
|---|---|---|
| L1 结构 | 模块、包名、依赖符合约定 | 通过 |
| L2 编译 | mvn clean compile 返回 0 | 通过 |
| L3 合规与运行 | 仓库内 TCK 全绿,演示可启动 | 未达成 |
| L4 文档 | README、运行命令、基线记录齐全 | 未达成 |
下面的结论只针对 .local/java_reactive/eventd-v1 这个仓库快照,不针对书中所有示例代码。
L1:结构
eventd-v1/
├── pom.xml
├── eventd-core/
│ ├── pom.xml
│ └── src/main/java/com/plume/eventd/core/
│ ├── SyncSinglePublisher.java
│ └── LoggingSubscriber.java
└── eventd-app/
├── pom.xml
└── src/ # 空
包名 com.plume.eventd.core、模块命名、父 POM 聚合都符合约定。eventd-app 目录存在,但没有源码。
L2:编译
export JAVA_HOME=~/.jdks/jdk-25.0.5+8/Contents/Home
cd .local/java_reactive/eventd-v1
mvn -q -DskipTests clean compile
本机实测:
mvn -q -DskipTests clean compile 3.25s user 0.26s system 116% cpu 3.004 total
exit=0
生成:
eventd-core/target/classes/com/plume/eventd/core/LoggingSubscriber.class
eventd-core/target/classes/com/plume/eventd/core/SyncSinglePublisher.class
eventd-core/target/classes/com/plume/eventd/core/SyncSinglePublisher$SingleSubscription.class
eventd-app 没有源码,所以没有它的 class。编译成功只证明 core 的两个类语法和依赖没问题。
L3:合规与运行
TCK:当前跑不起来
按书中类名执行:
mvn test -Dtest=SyncSinglePublisherTckTest
真实输出(截取):
[INFO] eventd-core — RS implementations & utilities ....... FAILURE [ 4.420 s]
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.2.5:test
on project eventd-core: No tests matching pattern "SyncSinglePublisherTckTest"
were executed!
原因很直接:仓库里没有 src/test/java,也没有 SyncSinglePublisherTckTest 这个类。书中第 11 章确实跑过 TCK,但那些测试类是独立实验文件,没有进入本仓库。
这也解释了为什么不能把「第 11 章 TCK 全绿」直接当成「eventd v1 验收通过」:前者的被测对象是 BaseSubscriber 和 PublishProcessor,后者的被测对象应该是 SyncSinglePublisher 与后续手写类。
演示应用:当前没有入口
从模块目录执行:
cd eventd-app
mvn -q exec:java -Dexec.mainClass=com.plume.eventd.app.BackpressureDashboard
真实失败:
[ERROR] Failed to execute goal on project eventd-app: Could not resolve dependencies
[ERROR] dependency: com.plume.eventd:eventd-core:jar:1.0.0-SNAPSHOT (compile)
[ERROR] Could not find artifact com.plume.eventd:eventd-core:jar:1.0.0-SNAPSHOT
这有两个原因叠在一起:
eventd-app在模块目录单独执行时,Maven 找不到尚未install的eventd-core。- 即使从根目录用
-pl eventd-app -am,也会因为com.plume.eventd.app.BackpressureDashboard不存在而失败。
真正的入口和面板类需要先按 13.3 的代码加进 eventd-app/src/main/java,再执行:
mvn -q -pl eventd-app -am exec:java \
-Dexec.mainClass=com.plume.eventd.app.BackpressureDashboard
哪些运行结果是真的
| 内容 | 在哪验证 | 是否在仓库内 |
|---|---|---|
| BaseSubscriber 白盒 TCK:27 用例,14 通过,13 跳过 | 第 11 章独立实验 | 否 |
| BaseSubscriber 黑盒 TCK:26 用例,11 通过,15 跳过 | 第 11 章独立实验 | 否 |
PublishProcessor 作为 identity processor:68 用例,30 通过,14 失败,24 跳过 | 第 11 章独立实验 | 否 |
SensorSimulatorPublisher 的 request(1)/request(5)/request(0)/cancel 输出 | 13.2 | 否(章节代码) |
| BUFFER / DROP / LATEST 的丢弃统计 | 13.3 | 否(章节代码) |
| 1M 元素与 10 万元素延迟测量 | 14.2 | 否(独立 benchmark) |
这些都是真实跑出来的,但它们不等于「仓库 CI 绿了」。验收文档必须把这两件事分开。
L4:文档
ls README.md eventd-core/README.md
真实输出:
ls: README.md: No such file or directory
ls: eventd-core/README.md: No such file or directory
现有两个类有 JavaDoc,但仓库没有 README、没有运行说明、没有基线记录。第 14.2 节的测量模板可以作为 README 的一部分,目前尚未落地。
逐项清单
| 检查项 | 状态 | 证据 |
|---|---|---|
| 父 POM 聚合两个模块 | 通过 | pom.xml 的 <modules> |
eventd-core 编译 | 通过 | mvn clean compile,exit 0 |
eventd-app 有入口 | 未通过 | src/ 为空 |
SyncSinglePublisherTckTest 存在并可运行 | 未通过 | surefire: No tests matching pattern |
AsyncRangePublisherTckTest | 未通过 | 类不存在 |
MapProcessorTckTest | 未通过 | 类不存在 |
| 背压演示可一键启动 | 未通过 | 无 main 类 |
| README 与运行命令 | 未通过 | 文件不存在 |
| 性能基线可复现 | 部分 | 14.2 有代码与实测,未进仓库 |
| 已知限制有记录 | 部分 | 13.1、13.2、14.2 文中有说明 |
补齐验收的最小工作
按依赖顺序:
- 决定接口口径。现有类用
java.util.concurrent.Flow,TCK 用org.reactivestreams。两条现成路线:用org.reactivestreams.FlowAdapters.toPublisher(...)转成 RS 类型,或者引入reactive-streams-tck-flow:1.0.4直接继承FlowPublisherVerification。不要把两套接口当成同一套。 - 加 TCK 测试类。
reactive-streams-tck:1.0.4是 TestNG 体系,测试类需要继承PublisherVerification并由 TestNG 运行。当前 POM 同时有 JUnit Jupiter,Surefire 需要能识别 TestNG;最省事的做法是单独建一个eventd-core-tck模块,只放 TCK 测试。 - 加
eventd-app入口与面板。把 13.3 的BackpressureDashboard落到eventd-app/src/main/java/com/plume/eventd/app/,补一个main。否则exec:java永远失败。 - 写 README。至少包含:JDK 版本、
mvn clean compile、TCK 命令、exec:java命令、已知限制。 - 记录基线。用 14.2 的模板,注明 JDK、JVM flags、预热轮数、输入规模和源码版本。
在这些完成之前,把 L3/L4 标成「通过」是不诚实的。
已知限制
SyncSinglePublisher不是线程安全的。cancelled、completed是普通boolean,并发request可能重复发射。null被静默当成空流。构造器不拒绝null,request里用if (value != null)跳过。更严格的做法是构造时抛NullPointerException,需要空流时显式用空 Publisher。Flow与org.reactivestreams不兼容。TCK 不能直接验证现有类,必须适配。- 章节示例不是仓库文件。
AsyncRangePublisher、MapProcessor、SensorSimulatorPublisher、BackpressureDashboard都还需按章节落地。 - 性能数字只代表一次运行。14.2 的数字没有经过 JMH,不能当作生产容量规划依据。
小结
| 门禁 | 结论 |
|---|---|
| L1 结构 | 通过 |
| L2 编译 | 通过(约 3 秒) |
| L3 合规与运行 | 未达成:无 TCK 测试类、无 app 入口 |
| L4 文档 | 未达成:无 README、无基线记录 |
验收的价值不是把复选框全部打勾,而是准确说明「现在能证明什么、还不能证明什么」。以当前仓库状态,能证明的是「两个类可以编译」;不能证明的是「手写 Publisher 合规」和「演示可一键运行」。
阅读导航
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。