忘记关闭的流会怎么样?GC 管不管?它会不会拖累别的请求?CloseSend、cancel、读到 EOF 到底哪个才算结束?这篇用服务端 handler 计数器和抓包代理逐个验证。
开了 200 个流、没处理:服务端在运行的 handler 200 个;进程 goroutine 比基线多 405 丢掉所有引用并 runtime.GC() 3 次之后:服务端 handler 200 个;goroutine 比基线多 405 conn.Close() 之后:服务端 handler 0 个
一个流只有读到服务端结束、取消 ctx 或关闭连接才会结束。不再引用它、等 GC,服务端 handler 不会因此退出。
10 个流被泄漏占满名额:Unary 调用 1002.1 ms 后返回 code=DeadlineExceeded message="context deadline exceeded" cancel 掉泄漏的流之后:Unary 调用 0.7 ms 后返回 成功;服务端在运行的 handler 0 个
每个未结束的流占一个并发名额(MaxConcurrentStreams),泄漏的流足够多时,同一连接上别的调用也会排队到超时。
只调用 CloseSend():服务端 handler 1 个(还在) 再开一个并 cancel ctx:服务端 handler 共 1 个(A 那个还在,B 已经释放) 再开一个会自己结束的流并读到 EOF:服务端 handler 共 1 个(仍只有 A) conn.Close() 之后:服务端 handler 0 个
服务端流:读到 EOF 或取消 ctx,CloseSend 不算。客户端流:CloseSend 或 CloseAndRecv,让服务端读到 EOF。最稳妥是为每个流使用 context.WithCancel 并 defer cancel()。
发了 3 条之后没有 CloseAndRecv:服务端 handler 1 个 调用 CloseAndRecv():成功,服务端数到 3 条;服务端 handler 0 个
泄漏的流让连接一直有在途的流,MaxConnectionIdle 这类空闲超时永远不触发。
有一个泄漏的流 1.5 秒内服务端发出的 GOAWAY 0 个;TCP 连接数 1
cancel 之后再过 0.8 秒:GOAWAY 共 1 个
没有任何在途的流 1.5 秒内服务端发出的 GOAWAY 1 个;TCP 连接数 1服务端在运行的 handler 数、goroutine 数持续上涨是泄漏的信号。