客户端取消或超时后,服务端的 goroutine 怎么停?线上是什么帧?流式 RPC 呢?取消刚好撞上响应会怎样?这篇逐项抓包和计数。
cancel:客户端得到 Canceled;线上帧:
C→S HEADERS 流 3 :method: POST; :scheme: http; :path: /demo.Demo/Unary; :authority: 127.0.0.1:9801; content-type: application/grpc; user-agent: grpc-go/1.84.0; te: trailers
C→S DATA 流 3 00000000070a057761746368
C→S RST_STREAM 流 3 错误码 CANCEL
deadline:客户端得到 DeadlineExceeded;线上帧:
C→S HEADERS 流 5 :method: POST; :scheme: http; :path: /demo.Demo/Unary; :authority: 127.0.0.1:9801; content-type: application/grpc; user-agent: grpc-go/1.84.0; te: trailers; grpc-timeout: 99928041n
C→S DATA 流 5 00000000070a057761746368
S→C RST_STREAM 流 5 错误码 CANCEL
C→S RST_STREAM 流 5 错误码 CANCEL取消就是 RST_STREAM 帧(错误码 CANCEL)。cancel() 由客户端发出;deadline 到期则两边都会计时并各发一个。
watch 客户端 50 个调用全部超时返回时:服务端还在运行的 handler 0 个
再过 100 ms:0 个
再过 300 ms:0 个
再过 600 ms:0 个
ignore 客户端 50 个调用全部超时返回时:服务端还在运行的 handler 50 个
再过 100 ms:50 个
再过 300 ms:0 个
再过 600 ms:0 个服务端的 ctx 会被取消,但 handler 不检查就继续跑:50 个不监听 ctx 的 handler 在客户端全部返回时仍在运行,直到睡眠结束。长耗时的 handler 要在阻塞处 select ctx.Done()。
客户端取消后下一次 Recv 立刻返回:code=Canceled (0.1 ms) 服务端:第 5 次 Send 失败:rpc error: code = Canceled desc = context canceled;st.Context().Err() = context canceled;仍在运行的 handler 0 个
客户端取消后,服务端流的 Send 会失败并返回带 Canceled 的错误;检查 Send 的返回值才能及时退出。
handler 约 1000 µs,客户端 500 µs 后取消:成功 0、Canceled 400、其它 0 handler 约 1000 µs,客户端 1000 µs 后取消:成功 0、Canceled 400、其它 0 handler 约 1000 µs,客户端 1500 µs 后取消:成功 396、Canceled 4、其它 0 handler 约 1000 µs,客户端 2000 µs 后取消:成功 400、Canceled 0、其它 0 handler 约 1000 µs,客户端 4000 µs 后取消:成功 400、Canceled 0、其它 0
取消和响应恰好撞上时,结果是成功还是 Canceled 取决于时序:取消不能保证请求没被执行,需要幂等。
调用完成后线上 RST_STREAM 0 个;再调用 cancel() 之后 0 个
调用结束后再 cancel() 什么帧都不发,所以 defer cancel() 是安全且免费的。
客户端在 101.0 ms 时取消;A 在 101.2 ms、B 在 101.2 ms、C 在 101.3 ms 发现自己的 ctx 被取消(距取消分别 0.1 / 0.2 / 0.3 ms)
取消通过每一层传递 ctx 一路传到最下游,本机上每跳只要零点几毫秒;前提是每一层都传 ctx(第 5 篇)。