先把边界说清楚
Reactive Streams 1.0.4 有 43 条规则,TCK 并没有为每一条都写断言。它只提供四类基类,用例由 TestNG 驱动;方法名前缀直接表示覆盖强度:
| 前缀 | 含义 | 统计时怎么算 |
|---|---|---|
required_ | 必测;失败即不合规 | 必须通过 |
optional_ | 与可选能力相关;不具备该能力时会跳过或失败 | 单独说明 |
stochastic_ | 随机化重复测试,可用 skipStochasticTests() 关闭 | 通过/跳过都要记录 |
untested_ | TCK 尚未实现断言,运行时抛 SkipException | 不算通过 |
TCK 1.0.4 的 POM 直接依赖 TestNG 7.3.0;依赖树里还会出现 JUnit 4.12,那是 TestNG 自己的 compile 依赖,不代表 TCK 用例是 JUnit 用例。运行入口是 org.testng.TestNG,不是 JUnit Runner。
四类基类的真实规模
数量按 1.0.4 源码逐个方法统计:
| 基类 | 验证对象 | required | optional | stochastic | untested | 其他 | 合计 |
|---|---|---|---|---|---|---|---|
PublisherVerification<T> | Publisher | 22 | 8 | 1 | 7 | 0 | 38 |
SubscriberWhiteboxVerification<T> | Subscriber(可控制订阅) | 14 | 0 | 0 | 13 | 0 | 27 |
SubscriberBlackboxVerification<T> | Subscriber(黑盒) | 11 | 0 | 0 | 15 | 0 | 26 |
IdentityProcessorVerification<T> | Processor | 38 | 8 | 1 | 20 | 1 | 68 |
IdentityProcessorVerification 的「其他」是 mustImmediatelyPassOnOnErrorEventsReceivedFromItsUpstreamToItsDownstream,对应 §4.2 的「不恢复就必须立即向下游传播 onError」。
规则覆盖到什么程度
这张表回答的是「某条规则到底有没有自动化断言」,而不是「实现是否合规」。同一个规则可能同时有 required、optional 和 untested 三种形态。
| 规则 | 覆盖 | 对应测试 |
|---|---|---|
| §1.1 发射量不超过请求量 | required | spec101 |
| §1.2 可以少发并终止 | required | spec102 |
| §1.3 信号串行 | stochastic | spec103 |
§1.4 失败必须 onError | optional | spec104 |
§1.5 有限流必须 onComplete | required + optional | spec105、optional_spec105_* |
| §1.6 终止后订阅视为取消 | untested | spec106 |
| §1.7 终止后不得再发信号 | required + untested | spec107 两条 |
| §1.8 取消后最终停止 | untested | spec108 |
§1.9 onSubscribe 先行与拒绝语义 | required | spec109 三条 |
| §1.10 同一 Subscriber 不得重复订阅 | untested | spec110 |
| §1.11 多订阅者能力 | optional | spec111 五条 |
§2.1 必须通过 request 表达需求 | required | spec201 |
| §2.2 建议异步派发 | untested | spec202 |
| §2.3 终止回调不得操作订阅 | required | spec203 两条 |
| §2.4 终止后视为已取消 | untested | spec204 |
§2.5 重复 onSubscribe 取消新订阅 | required | spec205 |
| §2.6 不再需要时取消 | untested | spec206 |
§2.7 request/cancel 串行调用 | untested | spec207 |
§2.8 取消后仍可能收到 onNext | required | spec208 |
§2.9 无请求也要能收 onComplete | required | spec209 两条 |
§2.10 无请求也要能收 onError | required | spec210 两条 |
| §2.11 信号 happens-before | untested | spec211 |
§2.12 onSubscribe 最多一次 | untested | spec212 |
| §2.13 null 参数抛 NPE | required + untested | spec213 四条 |
| §3.1 只在 Subscriber 上下文调用 | untested | spec301 |
§3.2 允许同步 request | required | spec302 |
| §3.3 同步递归有上界 | required | spec303、required_validate_boundedDepth... |
§3.4 request 及时返回 | untested | spec304 |
§3.5 cancel 不同步做重计算 | untested | spec305(TCK 未实现断言) |
§3.6 取消后 request 是 NOP | required | spec306 |
§3.7 取消后 cancel 是 NOP | required | spec307 |
§3.8 request 累加需求 | required | spec308 |
§3.9 非正请求必须 onError | required + optional | spec309 三条 |
§3.10 request 可同步 onNext | untested | spec310 |
§3.11 request 可同步终止 | untested | spec311 |
| §3.12 取消后最终停止发射 | required | spec312 |
| §3.13 取消后释放 Subscriber 引用 | required | spec313 |
| §3.14 取消可使 Publisher 关闭 | untested | spec314 |
§3.15 cancel 不得抛异常 | untested | spec315 |
§3.16 request 不得抛异常 | untested | spec316 |
§3.17 支持 Long.MAX_VALUE 需求 | required | spec317 三条 |
| §4.1 Processor 同时遵守两侧契约 | 委托 | Publisher + Subscriber 两套用例 |
§4.2 Processor 不恢复时必须传播 onError | 专用用例 | mustImmediatelyPassOn... |
结论很直接:§1.6、§1.8、§1.10、§2.4、§2.6、§2.7、§2.11、§2.12、§3.1、§3.4、§3.5、§3.10、§3.11、§3.14、§3.15、§3.16 在 1.0.4 中没有 required 断言。TCK 全绿只说明「被检查的部分」合规,这些条款需要自己补测试或做代码审查。
PublisherVerification
必须实现的抽象方法
public abstract Publisher<T> createPublisher(long elements);
public abstract Publisher<T> createFailedPublisher();
两个方法的语义:
createPublisher(long elements)返回的 Publisher 必须恰好发射elements个元素,并在之后终止。TCK 至少会用 1 和 3 调用它,所以「永远只发一个元素」的实现不能直接拿来跑。createFailedPublisher()返回null表示跳过失败路径用例;返回实例时,该实例必须在onSubscribe之后发onError,而不是直接抛异常。
可选覆写:
public long maxElementsFromPublisher() { return Long.MAX_VALUE - 1; } // 默认值
public long boundedDepthOfOnNextAndRequestRecursion() { return 1; } // 默认值
public boolean skipStochasticTests() { return false; } // 默认值
maxElementsFromPublisher() 的默认值是 Long.MAX_VALUE - 1,不是 Long.MAX_VALUE。返回 Long.MAX_VALUE 会让依赖于「有限流」的用例直接跳过。
22 个必测方法
配置与元素数:
| 方法 | 验证内容 |
|---|---|
required_createPublisher1MustProduceAStreamOfExactly1Element | createPublisher(1) 恰好 1 个元素 |
required_createPublisher3MustProduceAStreamOfExactly3Elements | createPublisher(3) 恰好 3 个元素 |
required_validate_maxElementsFromPublisher | maxElementsFromPublisher() >= 0 |
required_validate_boundedDepthOfOnNextAndRequestRecursion | 递归深度配置 >= 1 |
§1 发布侧:
| 方法 | 规则 |
|---|---|
required_spec101_subscriptionRequestMustResultInTheCorrectNumberOfProducedElements | §1.1 |
required_spec102_maySignalLessThanRequestedAndTerminateSubscription | §1.2 |
required_spec105_mustSignalOnCompleteWhenFiniteStreamTerminates | §1.5 |
required_spec107_mustNotEmitFurtherSignalsOnceOnCompleteHasBeenSignalled | §1.7 |
required_spec109_subscribeThrowNPEOnNullSubscriber | §1.9 |
required_spec109_mustIssueOnSubscribeForNonNullSubscriber | §1.9 |
required_spec109_mayRejectCallsToSubscribeIfPublisherIsUnableOrUnwillingToServeThemRejectionMustTriggerOnErrorAfterOnSubscribe | §1.9 |
§3 订阅侧:
| 方法 | 规则 |
|---|---|
required_spec302_mustAllowSynchronousRequestCallsFromOnNextAndOnSubscribe | §3.2 |
required_spec303_mustNotAllowUnboundedRecursion | §3.3 |
required_spec306_afterSubscriptionIsCancelledRequestMustBeNops | §3.6 |
required_spec307_afterSubscriptionIsCancelledAdditionalCancelationsMustBeNops | §3.7 |
required_spec309_requestZeroMustSignalIllegalArgumentException | §3.9 |
required_spec309_requestNegativeNumberMustSignalIllegalArgumentException | §3.9 |
required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling | §3.12 |
required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber | §3.13 |
required_spec317_mustSupportAPendingElementCountUpToLongMaxValue | §3.17 |
required_spec317_mustSupportACumulativePendingElementCountUpToLongMaxValue | §3.17 |
required_spec317_mustNotSignalOnErrorWhenPendingAboveLongMaxValue | §3.17 |
可选与未测
8 个 optional 方法:
| 方法 | 触发条件 |
|---|---|
optional_spec104_mustSignalOnErrorWhenFails | createFailedPublisher() 非 null |
optional_spec105_emptyStreamMustTerminateBySignallingOnComplete | createPublisher(0) 能正常完成 |
optional_spec111_maySupportMultiSubscribe | 支持多订阅 |
optional_spec111_registeredSubscribersMustReceiveOnNextOrOnCompleteSignals | 多订阅者都能收到信号 |
optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingOneByOne | 多播且逐个请求 |
optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfront | 多播且一次性请求 |
optional_spec111_multicast_mustProduceTheSameElementsInTheSameSequenceToAllOfItsSubscribersWhenRequestingManyUpfrontAndCompleteAsExpected | 多播、一次性请求且能完成 |
optional_spec309_requestNegativeNumberMaySignalIllegalArgumentExceptionWithSpecificMessage | 负数请求的错误消息足够具体 |
7 个 untested 方法会直接跳过:spec106(终止后订阅视为取消)、spec107(错误分支终止后静默)、spec108(取消后不再收到终止信号)、spec109(subscribe 抛非致命异常)、spec110(同一 Subscriber 重复订阅)、spec304(request 不做重计算)、spec305(cancel 不做重计算)。
另有 stochastic_spec103_mustSignalOnMethodsSequentially,重复检查信号是否串行。
SubscriberWhiteboxVerification
必须实现的方法
public abstract Subscriber<T> createSubscriber(WhiteboxSubscriberProbe<T> probe);
public abstract T createElement(int element); // 来自 WithHelperPublisher
publisherExecutorService() 在基类里已有默认实现:@BeforeClass 创建 4 线程池,@AfterClass 关闭。只有需要自定义线程模型时才覆写,且覆写后要自己负责关闭。
白盒没有 triggerRequest。测试通过 WhiteboxSubscriberProbe 注册的 SubscriberPuppet 控制请求与取消:
| 探针方法 | 调用时机 |
|---|---|
registerOnSubscribe(SubscriberPuppet) | 收到 onSubscribe 后 |
registerOnNext(T) | 处理完 onNext 后 |
registerOnError(Throwable) | 处理完 onError 后 |
registerOnComplete() | 处理完 onComplete 后 |
SubscriberPuppet 只有两个方法:triggerRequest(long n) 和 signalCancel()。只实现 onNext 而不调用 registerOnNext,TCK 会认为信号从未到达,最终以超时失败。
14 个必测方法
| 方法 | 规则 |
|---|---|
required_exerciseWhiteboxHappyPath | 综合路径 |
required_spec201_mustSignalDemandViaSubscriptionRequest | §2.1 |
required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete | §2.3 |
required_spec203_mustNotCallMethodsOnSubscriptionOrPublisherInOnError | §2.3 |
required_spec205_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal | §2.5 |
required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel | §2.8 |
required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall | §2.9 |
required_spec209_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall | §2.9 |
required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall | §2.10 |
required_spec210_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall | §2.10 |
required_spec213_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
required_spec213_onNext_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
required_spec213_onError_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
required_spec308_requestMustRegisterGivenNumberElementsToBeProduced | §3.8 |
13 个 untested:spec202、spec204、spec206、spec207、spec211、spec212、spec213_failingOnSignalInvocation、spec301、spec310、spec311、spec314、spec315、spec316。
SubscriberBlackboxVerification
必须实现的方法
public abstract Subscriber<T> createSubscriber();
public abstract T createElement(int element);
publisherExecutorService() 同样有默认实现。黑盒额外提供:
public void triggerRequest(Subscriber<? super T> subscriber) {
// 默认什么都不做
}
如果你的 Subscriber 不在 onSubscribe 里主动请求,而是等待外部触发,就必须覆写 triggerRequest;否则 TCK 发不出元素,用例会超时。
11 个必测方法
| 方法 | 规则 |
|---|---|
required_spec201_blackbox_mustSignalDemandViaSubscriptionRequest | §2.1 |
required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnComplete | §2.3 |
required_spec203_blackbox_mustNotCallMethodsOnSubscriptionOrPublisherInOnError | §2.3 |
required_spec205_blackbox_mustCallSubscriptionCancelIfItAlreadyHasAnSubscriptionAndReceivesAnotherOnSubscribeSignal | §2.5 |
required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithoutPrecedingRequestCall | §2.9 |
required_spec209_blackbox_mustBePreparedToReceiveAnOnCompleteSignalWithPrecedingRequestCall | §2.9 |
required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithoutPrecedingRequestCall | §2.10 |
required_spec210_blackbox_mustBePreparedToReceiveAnOnErrorSignalWithPrecedingRequestCall | §2.10 |
required_spec213_blackbox_onSubscribe_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
required_spec213_blackbox_onNext_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
required_spec213_blackbox_onError_mustThrowNullPointerExceptionWhenParametersAreNull | §2.13 |
15 个 untested:spec202、spec204、spec206、spec207、spec208、spec211、spec212、spec213_failingOnSignalInvocation、spec301、spec308、spec310、spec311、spec314、spec315、spec316。
IdentityProcessorVerification
必须实现的四个方法
public abstract Processor<T, T> createIdentityProcessor(int bufferSize);
public abstract Publisher<T> createFailedPublisher();
public abstract T createElement(int element); // 来自 WithHelperPublisher
public abstract ExecutorService publisherExecutorService(); // 来自 WithHelperPublisher
createPublisher(long) 和 createSubscriber(probe) 都有默认实现:前者用 createHelperPublisher(elements) 生成元素再喂给 Processor,后者把同一个 Processor 包装成 Subscriber。不要自己再写一遍。
68 个用例的构成
| 组成 | 数量 | 说明 |
|---|---|---|
| Publisher 侧 required | 22 | 与 PublisherVerification 相同的 22 条 |
| Subscriber 侧 required | 14 | 与白盒相同的 14 条 |
| Processor 专用 required | 2 | required_spec104_mustCallOnErrorOnAllItsSubscribersIfItEncountersANonRecoverableError、required_mustRequestFromUpstreamForElementsThatHaveBeenRequestedLongAgo |
| optional | 8 | 与 Publisher 侧的 8 条一致 |
| stochastic | 1 | spec103 |
| untested | 20 | Publisher/Subscription 侧 7 条 + Subscriber 侧 13 条 |
| §4.2 专用 | 1 | mustImmediatelyPassOnOnErrorEventsReceivedFromItsUpstreamToItsDownstream |
required_spec104_mustCallOnErrorOnAllItsSubscribers... 验证 Processor 遇到不可恢复错误时,所有下游都收到 onError;required_mustRequestFromUpstream... 验证「下游很早以前请求过的元素,Processor 必须向上游补请求」,这是 Processor 背压桥接的核心。
§4.1 没有单独的测试方法,它由 Publisher 与 Subscriber 两套委托用例共同覆盖。§4.2 的「不恢复就立即传播」由 mustImmediatelyPassOn... 专门验证;「恢复则必须把上游订阅视为已取消」在 1.0.4 中没有对应 required 用例。
Flow 版本
reactive-streams-tck-flow:1.0.4 提供 FlowPublisherVerification、FlowSubscriberWhiteboxVerification、FlowSubscriberBlackboxVerification 和 IdentityFlowProcessorVerification。抽象方法换成 Flow 类型:
| 基类 | 必须实现 |
|---|---|
FlowPublisherVerification<T> | createFlowPublisher(long)、createFailedFlowPublisher() |
FlowSubscriberWhiteboxVerification<T> | createFlowSubscriber(WhiteboxSubscriberProbe<T>)、createElement(int) |
FlowSubscriberBlackboxVerification<T> | createFlowSubscriber()、createElement(int);另有 triggerFlowRequest 钩子 |
IdentityFlowProcessorVerification<T> | createIdentityFlowProcessor(int)、createFailedFlowPublisher()、createElement(int)、publisherExecutorService() |
Reactor 3.8.8 的 Flux 实现的是 org.reactivestreams.Publisher,不是 java.util.concurrent.Flow.Publisher。要跑 Flow TCK,用 org.reactivestreams.FlowAdapters.toFlowPublisher(...) 适配,不要手写一层转换。
最小依赖与运行
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams-tck</artifactId>
<version>1.0.4</version>
<scope>test</scope>
</dependency>
<!-- 只有验证 java.util.concurrent.Flow 类型时才需要 -->
<dependency>
<groupId>org.reactivestreams</groupId>
<artifactId>reactive-streams-tck-flow</artifactId>
<version>1.0.4</version>
<scope>test</scope>
</dependency>
一个可以直接跑通的 Publisher 测试:
import org.reactivestreams.Publisher;
import org.reactivestreams.tck.PublisherVerification;
import org.reactivestreams.tck.TestEnvironment;
import reactor.core.publisher.Flux;
public class RangePublisherTckTest extends PublisherVerification<Integer> {
public RangePublisherTckTest() {
super(new TestEnvironment(5_000));
}
@Override
public Publisher<Integer> createPublisher(long elements) {
return Flux.range(0, (int) elements);
}
@Override
public Publisher<Integer> createFailedPublisher() {
return null; // 跳过失败路径用例;需要覆盖 §1.4 时换成 Flux.error(...)
}
}
用 TestNG 运行:
java -cp "testng-7.3.0.jar:jcommander-1.78.jar:\
reactive-streams-tck-1.0.4.jar:reactive-streams-1.0.4.jar:\
reactor-core-3.8.8.jar:out" \
org.testng.TestNG -testclass RangePublisherTckTest
Flow 版本只需替换基类和创建方法:
import org.reactivestreams.FlowAdapters;
import org.reactivestreams.tck.flow.FlowPublisherVerification;
public class FlowRangeTckTest extends FlowPublisherVerification<Integer> {
public FlowRangeTckTest() {
super(new TestEnvironment(5_000));
}
@Override
public java.util.concurrent.Flow.Publisher<Integer> createFlowPublisher(long elements) {
return FlowAdapters.toFlowPublisher(Flux.range(0, (int) elements));
}
@Override
public java.util.concurrent.Flow.Publisher<Integer> createFailedFlowPublisher() {
return null;
}
}
实测计数
以下数字来自本机 TestNG 7.3.0 运行;createFailedPublisher() 返回 null,Publisher 两类用 1 秒超时:
| 基类 | 被测实现 | 总用例 | 通过 | 失败 | 跳过 |
|---|---|---|---|---|---|
PublisherVerification | Flux.range | 38 | 29 | 0 | 9 |
FlowPublisherVerification | Flux.range + FlowAdapters | 38 | 29 | 0 | 9 |
SubscriberWhiteboxVerification | Reactor BaseSubscriber | 27 | 14 | 0 | 13 |
SubscriberBlackboxVerification | Reactor BaseSubscriber | 26 | 11 | 0 | 15 |
IdentityProcessorVerification | RxJava PublishProcessor | 68 | 30 | 14 | 24 |
Publisher 的 9 个跳过 = 7 个 untested_* + 2 个因没有失败 Publisher(optional_spec104、required_spec109_mayRejectCalls...)。把 createFailedPublisher() 改成 Flux.error(new RuntimeException("boom")) 后,变为 31 通过、7 跳过。
IdentityProcessorVerification 用热的多播处理器 PublishProcessor 时出现 14 个失败,不是 RxJava 不合规,而是「identity processor」要求能缓冲并按下游需求转发;热处理器没有下游时直接丢元素,语义不匹配。Processor 的 24 个跳过主要是 20 个 untested_* 和 2 个失败 Publisher 用例,其余是可选多播用例的条件跳过。
TestEnvironment 的默认超时是 100ms,不是 5000ms。单参构造器把默认超时、无信号超时、轮询间隔都设成同一个值;慢速 CI 上建议从 new TestEnvironment(5_000) 起步。
失败时怎么读
| 失败方法 | 典型信息 | 常见原因 |
|---|---|---|
required_createPublisher1MustProduceAStreamOfExactly1Element | Expected end-of-stream but got element [2] | createPublisher(1) 多发了一个元素 |
required_spec309_requestZeroMustSignalIllegalArgumentException | Expected onError(java.lang.IllegalArgumentException) within 1000 ms | request(0) 被静默忽略 |
required_spec205_mustCallSubscriptionCancel... | Expected 2nd Subscription given to subscriber to be cancelled, but Subscription.cancel() was not called within 5000 ms | 收到第二个 onSubscribe 时取消了旧订阅,而不是新订阅 |
required_spec312_cancelMustMakeThePublisherToEventuallyStopSignaling | 取消后仍有 onNext | cancel() 只置标志,已排队任务继续发射 |
required_spec313_cancelMustMakeThePublisherEventuallyDropAllReferencesToTheSubscriber | Subscriber 无法被 GC | Subscription 取消后仍持有下游强引用 |
先看方法名里的规范编号,再对照上文的规则表;不要第一反应调大超时。超时通常说明信号根本发错了顺序或根本没发。
TCK 之外还要测什么
TCK 的 untested_* 已经列出一批空白,此外还有它天然覆盖不到的部分:
| 场景 | 建议手段 |
|---|---|
| 长时间运行的内存泄漏 | JFR 分配采样、GC 日志、WeakReference + System.gc() 的弱引用断言 |
| 多线程竞争下的信号串行 | 多轮压测 + 线程名/序号断言,而不是单次运行 |
| 业务语义(去重、聚合、顺序) | 普通单元测试与 StepVerifier |
| 调度器与超时行为 | StepVerifier.withVirtualTime |
| 取消后的资源释放 | 连接、文件句柄、线程池的显式计数 |
一个补充 TCK 的 StepVerifier 例子,验证「逐个请求 + 中途取消」:
@Test
void requestOneByOneThenCancel() {
StepVerifier.create(Flux.range(1, 5), 1)
.expectNext(1)
.thenRequest(1)
.expectNext(2)
.thenCancel()
.verify();
}
再用一个手写 Subscriber 验证 SyncSinglePublisher 对 request(0) 的处理:
@Test
void nonPositiveRequestSignalsIllegalArgumentException() {
AtomicReference<Throwable> error = new AtomicReference<>();
new SyncSinglePublisher<>("x").subscribe(new Flow.Subscriber<>() {
@Override public void onSubscribe(Flow.Subscription s) { s.request(0); }
@Override public void onNext(String item) { fail("unexpected onNext"); }
@Override public void onError(Throwable t) { error.set(t); }
@Override public void onComplete() { fail("unexpected onComplete"); }
});
assertInstanceOf(IllegalArgumentException.class, error.get());
}
不要用 Runtime.totalMemory() - freeMemory() 判断泄漏:这个差值受分配速率和 GC 时机影响,既不是峰值也不是稳定占用。要证明「取消后 Subscriber 可回收」,用弱引用加 GC,或者直接用 JFR 看对象是否还被 Publisher 持有。
相关阅读
| 主题 | 入口 |
|---|---|
| Reactive Streams 规范 | reactive-streams.org |
| TCK 源码 | reactive-streams-jvm/tck |
| Project Reactor 文档 | projectreactor.io/docs |
| RxJava 文档 | github.com/ReactiveX/RxJava |
| 背压规则清单 | 附录 C |
| 迁移指南 | 附录 D |
阅读导航
上一篇:附录 D 从 CompletableFuture 到响应式流迁移指南
下一篇:返回书稿书架 选择其他卷。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。