英文:
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调用中的上下文,错误代码可能是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
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 < 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"})
}
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 <-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 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="\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
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论