【发布时间】:2022-08-15 01:50:09
【问题描述】:
我们遇到了一个问题,即我们的 gRPC 流服务器在 SendMsg 上被阻止,并带有以下堆栈跟踪:
google.golang.org/grpc/internal/transport.(*writeQuota).get(0xc000de4040, 0x32)
/root/go/pkg/mod/google.golang.org/grpc@v1.46.0/internal/transport/flowcontrol.go:59 +0x74
google.golang.org/grpc/internal/transport.(*http2Server).Write(0xc000bb4680, 0xc000aa6000, {0xc000f2be60, 0x5, 0x5}, {0xc000d6d590, 0x2d, 0x2d}, 0x0)
/root/go/pkg/mod/google.golang.org/grpc@v1.46.0/internal/transport/http2_server.go:1090 +0x23b
google.golang.org/grpc.(*serverStream).SendMsg(0xc0002785b0, {0xb8f9e0, 0xc000b686c0})
/root/go/pkg/mod/google.golang.org/grpc@v1.46.0/stream.go:1530 +0x1cc
我们的服务器单向流向客户端。我们在节点上每隔 4-6 小时就会遇到这个问题,但大约 15 分钟后,TCP 连接将关闭,客户端将重新连接,并且流式传输将像以前一样继续。我们通过每 10 秒使用一次保持活动来初始化服务器来解决此问题:
server := grpc.NewServer(grpc.KeepaliveParams(keepalive.ServerParameters{Time: time.Duration(10) * time.Second, Timeout: 0}))
这个问题在过去两天内停止发生。现在这个问题在过去的 5 个小时里一直在单个节点上发生,并且还没有消失。
这是ss 的输出:
$ ss -ntmp|grep -A 1 9222
ESTAB 0 0 10.192.254.1:9222 10.120.224.70:50380
skmem:(r0,rb524288,t0,tb524288,f0,w0,o0,bl0,d0)
对于在节点上正常运行的服务器,t (wmem_alloc) 值和 w (wmem_queued) 值非零。根据this answer,这表明没有数据包排队等待传输。
我还看到每 10 秒从服务器发送一次保持活动的 ACK。顺序是:
- 服务器发送
PSH, ACK - 客户端立即回复
PSH, ACK - 服务器向上面发送
ACK - 服务器在 10 秒后发送另一个
PSH, ACK
所以服务器保活机制认为一切正常。我没有看到来自客户端的任何保活。我会尝试为客户端设置一个keep-alive,但是为什么会出现这个问题?
-
客户端是否在 RecvMsg 上被阻止?中间有代理吗?如果服务器在 SendMsg 上被阻止并且连接正在响应 keepalives,那么唯一真正的答案(除了我以前从未见过的错误)是客户端没有接收,并且流控制已满。
-
中间没有代理。客户端应该在 RecvMsg() 上阻塞,但是当这个问题发生时我没有看客户端,所以我不确定它是否在 RecvMsg() 之后被卡在写入无缓冲通道或其他东西上。不过,自从我们添加了客户端超时后,这个问题就没有发生过。
-
无论如何,客户端保活都是一个好主意。 (我们已经讨论过默认打开它们,但仍然没有这样做。)我不希望它能够解决这种服务器阻塞问题,但也许我只是不完全理解设想。如果它返回,请检查客户端,因为阻塞的服务器发送很可能是流量控制已满。
-
在您的情况下,
grpcClient.Stream中使用了哪些选项? -
@zangw 不熟悉那个 API。在服务器上,我如上所述调用
grpc.NewServer()。在客户端,我用grpc.WithTransportCredentials(insecure.NewCredentials())和grpc.WithKeepaliveParams()调用grpc.Dial()。自从我们添加了客户端超时后,这个问题就没有发生过。