Reputation: 57741
I have a unit test that is similar to the following (adapted from 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
}
When I run it, it prints the expected output:
> go run main.go
2024/05/15 10:20:19 Received: Mary Contrary
2024/05/15 10:20:19 response message: Hello, Mary Contrary
However, DialContext
is deprecated and replaced by NewClient
. If I try to replace it like so,
conn, err := grpc.NewClient("bufnet", grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
return lis.Dial()
}), grpc.WithInsecure())
The program now errors:
> 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
I suspect that this has something to do with this documentation comment:
One subtle difference between NewClient and Dial and DialContext is that the former uses "dns" as the default name resolver, while the latter use "passthrough" for backward compatibility. This distinction should not matter to most users, but could matter to legacy users that specify a custom dialer and expect it to receive the target string directly.
Indeed if I look at the definition of 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
}
...
It seems that it just calls NewClient
under the hood and overrides the default scheme with an non-exported option. How can I get this example to work using NewClient
?
Upvotes: 6
Views: 3100
Reputation: 111
Specify the passthrough
scheme when creating a new client. The passthrough resolver will not attempt to resolve anything:
conn, err := grpc.NewClient("passthrough://bufnet", ...)
It's better not to use SetDefaultScheme
to avoid thread safety issues and to keep the global state clean. From docs:
// NOTE: this function must only be called during initialization time (i.e. in
// an init() function), and is not thread-safe. The scheme set last overrides
// previously set values.
Upvotes: 10
Reputation: 57741
It turns out the default scheme can be overridden globally by calling resolver.SetDefaultScheme
. The full updated program is:
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
}
Which again prints the expected output:
> go run main.go
2024/05/15 11:34:34 Received: Mary Contrary
2024/05/15 11:34:34 response message: Hello, Mary Contrary
Upvotes: 2