服务端流里,服务端发得比客户端读得快会怎样?Send 在什么时候阻塞?窗口调大有用吗?阻塞时超时怎么处理?这篇用慢消费者实验一项项量。
客户端 1292 ms 内收了 40 条;服务端此时已 Send 完成(返回)43 条 服务端前 20 次 Send 耗时(ms):[0.0 0.5 0.3 0.4 31.3 30.9 30.7 30.6 31.2 31.1 31.1 30.7 31.5 30.5 33.5 30.7 30.9 31.2 30.3 31.2]
Send 不是“放进队列就走”:传输层只允许每个流有有限的缓冲和流控窗口。前几条消息瞬间返回,之后 Send 要等客户端读走数据放出窗口才返回,生产者自动被消费者拖慢。
默认窗口:600 ms 时已成功 Send 195584 字节(191 条 × 1 KiB),再过 300 ms 还是 195584 字节(已经阻塞);取消后服务端:第 191 次 Send 失败:rpc error: code = Canceled desc = context canceled
客户端完全不读时,默认设置下服务端先发出约 191 KiB 就阻塞。阻塞点约等于流控窗口加不到 64 KiB 的写缓冲(defaultWriteQuota)。
窗口 65536 字节:阻塞前 Send 了 130048 字节(2.0 倍窗口) 窗口 262144 字节:阻塞前 Send 了 324608 字节(1.2 倍窗口) 窗口 1048576 字节:阻塞前 Send 了 1102848 字节(1.1 倍窗口) 窗口 4194304 字节:阻塞前 Send 了 4214784 字节(1.0 倍窗口)
显式设置 WithInitialWindowSize 或 WithInitialConnWindowSize 后,阻塞点相应上移(1 MiB 窗口阻塞前约 1.1 MiB);同时会关闭 BDP 动态窗口,所以默认值有时反而更大。窗口怎么影响吞吐见第 10 篇。
服务端:第 3 次 Send 失败:rpc error: code = Canceled desc = context canceled;客户端的 deadline 是 400 ms,被阻塞的 Send 在 deadline 到了之后才返回
被阻塞的 Send 要等到客户端放弃才返回,错误码是 Canceled。流式 handler 必须检查 Send 的返回值并退出。
客户端收到 100 条(每条 20000 字节),顺序正确:true,最后得到 code=Aborted message="发完就报错"
handler 返回后,已经交给传输层的消息仍会全部送达,状态在最后。
两个 goroutine 各 Send 200 条:客户端收到 400 条,内容与编号对不上的 0 条
官方文档说同一个流的 SendMsg 不能在多个 goroutine 里并发;实验没触发问题,不能当作安全的证据。