1 回答

TA贡献1797条经验 获得超4个赞
您在此处发布的代码似乎有错字:
fmt.Sprintf("localhost:%s", port)
如果我在没有该grpc.WithBlock()选项的情况下运行您的测试功能,c.SayHello则会出现以下错误:
rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp: address localhost::50051: too many colons in address"
罪魁祸首似乎是localhost::50051
从const声明中删除多余的冒号后(或从 fmt.Sprintf("localhost:%s", port),如果您愿意),测试通过。
const (
port = "50051" // without the colon
)
输出:
2020/06/30 23:59:01 Dialing gRPC server...
2020/06/30 23:59:01 Making gRPC request...
2020/06/30 23:59:01 Received: John Doe
2020/06/30 23:59:01 Greeting: Hello John Doe
但是,从文档grpc.WithBlock()
如果没有这个,Dial 会立即返回并在后台连接服务器。
因此,使用此选项,应立即从grpc.Dial调用返回任何连接错误:
conn, err := grpc.Dial("bad connection string", grpc.WithBlock()) // can't connect
if err != nil {
panic(err) // should panic, right?
}
那么为什么你的代码会挂起?
通过查看grpc包的源代码(我针对 构建了测试v1.30.0):
// A blocking dial blocks until the clientConn is ready.
if cc.dopts.block {
for {
s := cc.GetState()
if s == connectivity.Ready {
break
} else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure {
if err = cc.connectionError(); err != nil {
terr, ok := err.(interface {
Temporary() bool
})
if ok && !terr.Temporary() {
return nil, err
}
}
}
if !cc.WaitForStateChange(ctx, s) {
// ctx got timeout or canceled.
if err = cc.connectionError(); err != nil && cc.dopts.returnLastError {
return nil, err
}
return nil, ctx.Err()
}
}
所以s此时确实处于TransientFailure状态,但FailOnNonTempDialError选项默认为false,并且WaitForStateChange在上下文过期时为 false ,这不会发生,因为Dial与后台上下文一起运行:
// Dial creates a client connection to the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
return DialContext(context.Background(), target, opts...)
}
在这一点上,我不知道这是否是预期的行为,因为其中一些 APIv1.30.0被标记为实验性的。
无论如何,最终为了确保你捕捉到这种错误,Dial你也可以将你的代码重写为:
conn, err := grpc.Dial(
"localhost:50051",
grpc.WithInsecure(),
grpc.FailOnNonTempDialError(true),
grpc.WithBlock(),
)
如果连接字符串错误,则会立即失败并显示相应的错误消息。
- 1 回答
- 0 关注
- 334 浏览
添加回答
举报