gRPC上下文取消传播

huangapple go评论79阅读模式
英文:

gRPC Context cancellation propogation

问题

我正在尝试理解在客户端服务通信中Go上下文取消的工作原理(比如,任何gRPC API调用)。

假设客户端在其端点上取消了上下文。这会导致向服务器发送一个新的HTTP请求,以通知服务器上一个/正在进行的gRPC请求的上下文已被取消吗?服务器如何知道客户端取消了上下文?

英文:

I am trying to understand how Go context cancellation works underneath in client service communication (say, any gRPC API call ).

Let's say a client canceled the context on its end. Does it result in a new HTTP request to the server communicating the context of the previous/ongoing gRPC request is canceled? How does server knows if client cancelled the context?

答案1

得分: 8

默认情况下,gRPC使用HTTP2作为底层协议,并且可以在一个HTTP2连接中存在多个流。下面的图片展示了HTTP2连接和流的示意图,可以从web dev获取更多信息。

gRPC上下文取消传播

如果在收到响应之前取消了一个gRPC调用中的上下文,错误代码可能是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() {
            // 在100毫秒后强制取消调用
			time.Sleep(100 * time.Millisecond)
			cancel()
		}()

		r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "name"})
     }

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

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    // 延迟1秒响应
	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()
			
		}
	}

使用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"
在客户端强制调用取消
2022/10/21 20:40:38 无法打招呼:rpc错误: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"
在客户端强制调用取消
2022/10/21 20:40:41 无法打招呼:rpc错误: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
英文:

By default, the HTTP2 is used as the underneath protocol for gPRC. And there could be multiple streams in on HTTP2 connection. Here is one image illustrate the connection and stream of HTTP2 from web dev

gRPC上下文取消传播

If you cancel the context in one gRPC call before the response comes back. The errCode could be CANCEL and mapping to RST_STREAM on client side, then this RST_STREAM would be sent to server. The RST_STREAM frame allows for immediate termination of a stream, which is the current RPC call stream would be terminated. However, the HTTP2 connection still there, when the next RPC call is invoked, another new stream will used to do the RPC call.


Here are some test codes snippet.

Client side: force to cancel context after 100 millseconds.

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

	defer conn.Close()

	c := pb.NewGreeterClient(conn)

	for i := 0; i &lt; 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, &amp;pb.HelloRequest{Name: &quot;name&quot;})
     }

Server side: delay sayHello response in 1 second.

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

	select {
	case &lt;-time.After(time.Duration(randMS) * time.Millisecond):

	case &lt;-ctx.Done():
		if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded {
			log.Printf(&quot;SayHello: context err %+v \n&quot;, ctx.Err())
			return nil, ctx.Err()
			
		}
	}

Run the codes with GODEBUG=http2debug=2, more debug logs of gRPC could help us to know the message between client and server.


Client logs

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=&quot;\x00\x00\x00\x00\a\n\x05world&quot;
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=&quot;\x02\x04\x10\x10\t\x0e\a\a&quot;
2022/10/21 20:40:38 http2: Framer 0xc0005be000: wrote PING flags=ACK len=8 ping=&quot;\x02\x04\x10\x10\t\x0e\a\a&quot;
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=&quot;\x00\x00\x00\x00\a\n\x05world&quot;
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=&quot;\x02\x04\x10\x10\t\x0e\a\a&quot;
2022/10/21 20:40:41 http2: Framer 0xc0005be000: wrote PING flags=ACK len=8 ping=&quot;\x02\x04\x10\x10\t\x0e\a\a&quot;
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=&quot;\x00\x00\x00\x00\a\n\x05world&quot;
2022/10/21 20:40:44 http2: Framer 0xc0005be000: read WINDOW_UPDATE len=4 (conn) incr=12

答案2

得分: 0

让我们假设客户端在其端上取消了上下文。这会导致向服务器发送一个新的HTTP请求,以通知前一个/正在进行的GRPC请求的上下文被取消吗?

不会。它只会关闭网络连接。

服务器如何知道客户端取消了上下文?

通过网络连接被关闭这个事实。

英文:

> Let's say a client canceled the context on its end. Does it result in a new HTTP request to the server communicating the context of the previous/ongoing GRPC request is canceled?

No. It closes the network connection.

> How does server knows if client cancelled the context?

By virtue of the fact that the network connection was closed.

huangapple
  • 本文由 发表于 2022年10月21日 15:53:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/74150409.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定