gRPC 上下文取消传播

Vis*_*hal 7 go grpc grpc-go

我试图了解 Go 上下文取消在客户端服务通信(例如任何 gRPC API 调用)中是如何工作的。

假设客户端取消了上下文。这是否会导致向服务器发出新的 HTTP 请求,并取消先前/正在进行的 gRPC 请求的上下文?服务器如何知道客户端是否取消了上下文?

zan*_*ngw 8

默认情况下,HTTP2用作 gPRC 的底层协议。连接上可能有多个流HTTP2。HTTP2这是一张图片,说明了来自网络开发的连接和流

如果您在响应返回之前取消一个 gRPC 调用中的上下文。errCode 可以CANCEL映射到RST_STREAM客户端,然后RST_STREAM将其发送到服务器。该RST_STREAM帧允许立即终止流,即当前的 RPC 调用流将被终止。但是,HTTP2连接仍然存在,当调用下一个 RPC 调用时,将使用另一个新流来执行 RPC 调用。


这是一些测试代码片段。

客户端:100 毫秒后强制取消上下文。

    conn, err := grpc.Dial(serverAddr,
        grpc.WithTransportCredentials(insecure.NewCredentials()))

    defer conn.Close()

    c := pb.NewGreeterClient(conn)

    for i := 0; i < 10; i++ {
        ctx, cancel := context.WithCancel(context.TODO())
        go func() {
            // force call cancel after 100 milliseconds
            time.Sleep(100 * time.Millisecond)
            cancel()
        }()

        r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "name"})
     }
Run Code Online (Sandbox Code Playgroud)

服务器端:延迟sayHello响应1秒。

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    // Delay ack on server by 1 seconds
    randMS := 1000

    select {
    case <-time.After(time.Duration(randMS) * time.Millisecond):

    case <-ctx.Done():
        if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded {
            log.Printf("SayHello: context err %+v \n", ctx.Err())
            return nil, ctx.Err()
            
        }
    }
Run Code Online (Sandbox Code Playgroud)

运行代码 GODEBUG=http2debug=2,更多gRPC的调试日志可以帮助我们了解客户端和服务器之间的消息。


客户端日志

2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote SETTINGS len=0
2022/10/21 20:40:38 http2: Framer 0xc0005be000: read SETTINGS len=6, settings: MAX_FRAME_SIZE=16384
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote SETTINGS flags=ACK len=0
2022/10/21 20:40:38 http2: Framer 0xc0005be000: read SETTINGS flags=ACK len=0
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote HEADERS flags=END_HEADERS stream=1 len=98
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote DATA flags=END_STREAM stream=1 len=12 data="\x00\x00\x00\x00\a\n\x05world"
2022/10/21 20:40:38 http2: Framer 0xc0005be000: read WINDOW_UPDATE len=4 (conn) incr=12
2022/10/21 20:40:38 http2: Framer 0xc0005be000: read PING len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote PING flags=ACK len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
force call cancel in client
2022/10/21 20:40:38 could not greet: rpc error: code = Canceled desc = context canceled
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote RST_STREAM stream=1 len=4 ErrCode=CANCEL
2022/10/21 20:40:41 http2: Framer 0xc0005be000: wrote HEADERS flags=END_HEADERS stream=3 len=7
2022/10/21 20:40:41 http2: Framer 0xc0005be000: wrote DATA flags=END_STREAM stream=3 len=12 data="\x00\x00\x00\x00\a\n\x05world"
2022/10/21 20:40:41 http2: Framer 0xc0005be000: read WINDOW_UPDATE len=4 (conn) incr=12
2022/10/21 20:40:41 http2: Framer 0xc0005be000: read PING len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
2022/10/21 20:40:41 http2: Framer 0xc0005be000: wrote PING flags=ACK len=8 ping="\x02\x04\x10\x10\t\x0e\a\a"
force call cancel in client
2022/10/21 20:40:41 could not greet: rpc error: code = Canceled desc = context canceled
2022/10/21 20:40:41 http2: Framer 0xc0005be000: wrote RST_STREAM stream=3 len=4 ErrCode=CANCEL
2022/10/21 20:40:44 http2: Framer 0xc0005be000: wrote HEADERS flags=END_HEADERS stream=5 len=7
2022/10/21 20:40:44 http2: Framer 0xc0005be000: wrote DATA flags=END_STREAM stream=5 len=12 data="\x00\x00\x00\x00\a\n\x05world"
2022/10/21 20:40:44 http2: Framer 0xc0005be000: read WINDOW_UPDATE len=4 (conn) incr=12

Run Code Online (Sandbox Code Playgroud)