这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《游戏服务端架构·核心系统与契约》10.1 邮件投递与去重
《裂隙战场》的结算邮件、《群岛纪元》的全服补偿、《泡泡工坊》的登录奖励,都要求同一业务事件只产生一封邮件。本节比较直写、消息队列与批量扇出三种投递模型,拆解事件 ID、模板与收件人组成的去重键,说明重试、死信和全服邮件游标如何避免重复与漏发,并给出可运行的幂等投递队列。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《裂隙战场》的结算邮件、《群岛纪元》的全服补偿、《泡泡工坊》的登录奖励,都要求同一业务事件只产生一封邮件。本节比较直写、消息队列与批量扇出三种投递模型,拆解事件 ID、模板与收件人组成的去重键,说明重试、死信和全服邮件游标如何避免重复与漏发,并给出可运行的幂等投递队列。
从订单链路三个依赖各承诺 99.95% 却算不出 99.9% 的矛盾切入,说明串联可用性复合、调用放大对预算的重复消耗、按调用次数分摊错误预算的构造性算法与可行性检查,给出可运行的 Go 实现、固定输入测试和本机真实输出,并讨论相关故障为何会让乘积失效。
按 reactive-streams-tck 1.0.4 实际源码整理四类验证基类:PublisherVerification 38 个用例(22 required)、SubscriberWhiteboxVerification 27 个(14 required)、SubscriberBlackboxVerification 26 个(11 required)、IdentityProcessorVerification 68 个(38 required)。逐条列出真实方法名、对应规范条款、抽象方法签名与被标记为 untested 的规则,并给出 TestNG 运行方式、Flow 适配写法和实测通过/跳过计数。
CompletableFuture 与 Mono/Flux 的完整 API 映射表,涵盖异步创建、变换、组合、错误处理、超时与并发控制六大类操作。列出迁移过程中的常见陷阱(阻塞 get、异常处理差异、背压缺失)并给出修正代码,同时明确「不应迁移」的场景以避免过度设计。
按 Reactive Streams 1.0.4 原文重列 43 条规则:Publisher 11 条、Subscriber 13 条、Subscription 17 条、Processor 2 条,并标注每条规则与 request/emit 主链的关系及 TCK 覆盖情况。纠正旧文把 §2 与 §3 对调、「15 条中 9 条与背压相关」的错误,并用实测说明两参数 onBackpressureBuffer 的溢出语义,以及 delayElements 为什么不能当作背压缓冲边界。
按 Reactor 3.8.8 的实际行为给出调度器选择依据:parallel 用于非阻塞 CPU 任务,boundedElastic 用于阻塞 I/O,single 用于需要串行执行的任务;同时纠正三个常见误解——interval 在 parallel 上不会并发执行、boundedElastic 的线程是按需创建而非固定 CPU×10、JDK 10+ 的 availableProcessors 已经容器感知。附 cgroup v1/v2 的核对方法与 VirtualTimeScheduler 的正确写法。
按 Reactor 3.8.8 与 RxJava 3.1.12 的实际 API 核对过的操作符速查表:涵盖创建、变换、组合、错误处理、背压控制与实用工具六大类,标注了两库并不对等的操作符(如 first、mergeOrdered、throttleFirst)和已废弃的重载,避免把「名字像」当成「语义相同」。
按仓库真实状态做验收,而不是把计划全部打勾:编译通过(约 3 秒),但仓库里没有 TCK 测试类、没有 eventd-app 入口、没有 README,因此合规与可运行两项尚未达成。列出实际命令与真实失败输出,区分「章节中验证过的独立实验」和「仓库内可重复的测试」,并给出补齐验收所需的最小工作清单。
给出一套可复现的小型测量方法,而不是编造吞吐数字:1M 元素的 Flux.range、1M 次 Mono.just 订阅、1M 次 SyncSinglePublisher 订阅各跑 5 轮取中位数;另用 publishOn 测 10 万元素的调度延迟分位数。本机实测 Flux.range 约 6ms、Mono.just 单次订阅约 24ns、手写订阅约 6ns,p50 调度延迟 5μs、p99 11μs。同时说明为什么 onBackpressureBuffer 加在 Flux.range 上、totalMemory 差值和 delayElements 都不能当作有效的性能证据。
把第 2、3、13 章的手写实现逐个映射到 Reactor:SyncSinglePublisher 对应 Mono.just,AsyncRangePublisher 对应 Flux.range + subscribeOn,SensorSimulatorPublisher 对应 Flux.create + sink.onRequest。重点不是行数,而是语义差异:Mono.just(null) 直接抛 NPE,raw Subscription.request(0) 报 §3.9 错误而 BaseSubscriber.request(0) 被静默忽略,scheduleAtFixedRate 不等于有背压的传感器源。
用同一套指标对比 BUFFER、DROP、LATEST 三种策略:上游 10ms 一个元素,下游每次请求 1 个并处理 50ms。实测 BUFFER(10, DROP_LATEST) 丢弃 76 个、仍有 10 个留在缓冲区,DROP 丢弃 85 个,LATEST 通过 doOnDiscard 计数丢弃 84 个。重点说明 onBackpressureBuffer 两参数重载在溢出时会发 OverflowException,而不是静默丢弃。
实现一个真正响应 request 的 SensorSimulatorPublisher:指数分布间隔、饱和需求累加、wip 串行化、取消时中断工作线程、非正请求转 onError。给出 request(5)、request(0)、取消与慢消费者的实测输出,并说明为什么总耗时接近 max(到达时间, 处理时间) 而不是简单的「生产者等待」。最后给出使用 Flux.create + onRequest 的 Reactor 等价写法及其适用边界。
按仓库当前真实状态介绍 eventd v1:父 POM + eventd-core + eventd-app 三个模块,eventd-core 目前只有 SyncSinglePublisher 与 LoggingSubscriber 两个类。给出本机 mvn clean compile 的真实结果和 dependency:tree 原始输出,并解释一个容易踩的坑:这两个类实现的是 java.util.concurrent.Flow 接口,而 RS TCK 验证的是 org.reactivestreams 接口,两者形状相似但不能直接互传,需要适配。
用 Scannable 的实际输出说明装配阶段只是生成 Publisher 链:两个 map 是两个步骤,Reactor 不会把 lambda 合并。用 AtomicInteger 实测 Flux.just(load()) 在装配时立即执行、defer 在每次订阅时重新执行;并澄清 map 里的副作用不是装配时执行,log() 的两个位置都在订阅时打印、请求方向与数据方向相反。
用同一段代码的实测栈对比三种手段:默认 subscribe() 会把错误交给 onErrorDropped 并只打印操作符内部帧;Hooks.onOperatorDebug() 增加 Assembly trace 与 Error has been observed 站点;checkpoint() 只在标记处报告,且 3.8.8 只有三个重载,没有所谓「条件检查点」。同时给出 log() 的真实输出格式、MDC 跨线程失效的原因,以及为什么不能用一次计时估算调试开销。
用实测输出纠正几个常见误解:doOnTerminate 在下游收到 onComplete/onError 之前执行,doAfterTerminate 在下游处理完之后执行;doOnEach 只看到 onNext/onError/onComplete,看不到 onSubscribe 和 cancel;doOnRequest 报告的是请求量而不是实际产量。同时给出 doFinally 的 SIGNAL 类型、取消路径的顺序、钩子所在线程,以及为什么不能用一次 nanoTime 测量钩子开销。
分清 SubscriberWhiteboxVerification 与 SubscriberBlackboxVerification 的真实抽象方法:白盒通过 WhiteboxSubscriberProbe 注册 onSubscribe/onNext/onError/onComplete,并用 SubscriberPuppet 控制 request 与 cancel;黑盒的 triggerRequest 默认是空实现。给出可编译、14 个必测项全部通过的白盒骨架,用实测失败信息解释 spec205 要求取消的是「新收到的重复订阅」而不是旧订阅,并展示 IdentityProcessorVerification 的四个抽象方法与 PublishProcessor 的 14 项实败。
按真实方法名梳理 PublisherVerification 的必测项:createPublisher 精确元素数、spec101 请求与发射数量、spec102 可少发、spec103 串行信号、spec105 完成信号、spec107 终止后静默、spec109 订阅规则、spec302 同步 request、spec303 递归深度、spec306/307 取消后 no-op、spec309 非正请求、spec312/313 取消语义、spec317 长整型需求。实测 38 个用例中 29 通过、7 个 untested、2 个因无失败 Publisher 跳过,并给出两条真实失败信息与定位方法。
搭建 Reactive Streams TCK 1.0.4 测试环境:确认它依赖 TestNG 7.3.0、存在独立的 reactive-streams-tck-flow 构件与 FlowPublisherVerification 类,给出普通 Publisher 与 java.util.concurrent.Flow 两套最小依赖和验证基类的真实抽象方法。实测 TestEnvironment 默认超时为 100ms(可用 DEFAULT_TIMEOUT_MILLIS 覆盖),并用一次真实运行说明 38 个用例中 29 通过、9 跳过的构成。
给出 RxJava 3 到 Project Reactor 的逐项映射:Observable/Single/Maybe/Completable 与 Flux/Mono 的类型对应,doOnEach、throttleFirst、throttleLast、sample、debounce、onErrorReturn、retryWhen 的真实差异,以及调度器命名对照。重点列出六个迁移陷阱:Observable 无背压、Reactor 没有 onErrorReturn(Function)、Subject 需要 toSerialized、Sinks 返回 FAIL_NON_SERIALIZED、flatMap 默认并发 2147483647 对 256、trampoline 不等于 immediate。示例基于 RxJava 3.1.12 与 reactor-core 3.8.8。