我有一个类似于以下内容的单元测试(改编自https://github.com/castaneai/grpc-testing-with-bufconn/blob/master/server/server.go):
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/test/bufconn"
)
func main() {
lis := bufconn.Listen(1024 * 1024)
s := grpc.NewServer()
helloworld.RegisterGreeterServer(s, &server{})
go func() {
if err := s.Serve(lis); err != nil {
log.Fatal(err)
}
}()
conn, err := grpc.DialContext(context.Background(), "bufnet", grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
return lis.Dial()
}), grpc.WithInsecure())
if err != nil {
log.Fatalf("dial: %v", err)
}
client := helloworld.NewGreeterClient(conn)
resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
Name: "Mary Contrary",
})
if err != nil {
log.Fatalf("say hello: %v", err)
}
log.Println("response message:", resp.GetMessage())
}
type server struct {
helloworld.UnimplementedGreeterServer
}
func (s *server) SayHello(_ context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &helloworld.HelloReply{
Message: "Hello, " + in.Name,
}, nil
}
当我运行它时,它会打印预期的输出:
> go run main.go
2024/05/15 10:20:19 Received: Mary Contrary
2024/05/15 10:20:19 response message: Hello, Mary Contrary
DialContext
已弃用并由 NewClient
取代。如果我尝试像这样替换它,
conn, err := grpc.NewClient("bufnet", grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
return lis.Dial()
}), grpc.WithInsecure())
程序现在出现错误:
> go run main.go
2024/05/15 10:17:37 say hello: rpc error: code = Unavailable desc = name resolver error: produced zero addresses
exit status 1
我怀疑这与此文档注释有关:
NewClient 与 Dial 和 DialContext 之间的一个细微差别是,前者使用“dns”作为默认名称解析器,而后者使用“passthrough”以实现向后兼容性。这种区别对于大多数用户来说并不重要,但对于指定自定义拨号器并希望它直接接收目标字符串的旧用户来说可能很重要。
确实,如果我看一下
DialContext
的定义,
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) {
// At the end of this method, we kick the channel out of idle, rather than
// waiting for the first rpc.
opts = append([]DialOption{withDefaultScheme("passthrough")}, opts...)
cc, err := NewClient(target, opts...)
if err != nil {
return nil, err
}
...
它似乎只是在后台调用
NewClient
并使用非导出选项覆盖默认方案。我怎样才能使用 NewClient
来运行这个示例?
resolver.SetDefaultScheme
来全局覆盖默认方案。完整更新的程序是:
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/examples/helloworld/helloworld"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/test/bufconn"
)
func main() {
lis := bufconn.Listen(1024 * 1024)
s := grpc.NewServer()
helloworld.RegisterGreeterServer(s, &server{})
go func() {
if err := s.Serve(lis); err != nil {
log.Fatal(err)
}
}()
resolver.SetDefaultScheme("passthrough")
conn, err := grpc.NewClient("bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return lis.Dial()
}), grpc.WithInsecure())
if err != nil {
log.Fatalf("dial: %v", err)
}
client := helloworld.NewGreeterClient(conn)
resp, err := client.SayHello(context.Background(), &helloworld.HelloRequest{
Name: "Mary Contrary",
})
if err != nil {
log.Fatalf("say hello: %v", err)
}
log.Println("response message:", resp.GetMessage())
}
type server struct {
helloworld.UnimplementedGreeterServer
}
func (s *server) SayHello(_ context.Context, in *helloworld.HelloRequest) (*helloworld.HelloReply, error) {
log.Printf("Received: %v", in.Name)
return &helloworld.HelloReply{
Message: "Hello, " + in.Name,
}, nil
}
再次打印预期输出:
> go run main.go
2024/05/15 11:34:34 Received: Mary Contrary
2024/05/15 11:34:34 response message: Hello, Mary Contrary