-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Name resolving | ||
|
||
This examples shows how `ClientConn` can pick different name resolvers. | ||
|
||
## What is a name resolver | ||
|
||
A name resolver can be seen as a `map[service-name][]backend-ip`. It takes a | ||
service name, and returns a list of IPs of the backends. A commen used name | ||
resolver is DNS. | ||
|
||
In this example, a resolver is created to resolve `resolver.example.grpc.io` to | ||
`localhost:50051`. | ||
|
||
## Try it | ||
|
||
``` | ||
go run server/main.go | ||
``` | ||
|
||
``` | ||
go run client/main.go | ||
``` | ||
|
||
## Explanation | ||
|
||
The echo server is serving on ":50051". Two clients are created, one is dialing | ||
to `passthrough:///localhost:50051`, while the other is dialing to | ||
`example:///resolver.example.grpc.io`. Both of them can connect the the server. | ||
|
||
Name resolver is picked based on the `scheme` in the target string. See | ||
https://github.com/grpc/grpc/blob/master/doc/naming.md for the target syntax. | ||
|
||
The first client picks the `passthrough` resolver, which takes the input, and | ||
use it as the backend addresses. | ||
|
||
The second is connecting to service name `resolver.example.grpc.io`. Without a | ||
proper name resolver, this would fail. In the example it picks the `example` | ||
resolver that we installed. The `example` resolver can handle | ||
`resolver.example.grpc.io` correctly by returning the backend address. So even | ||
though the backend IP is not set when ClientConn is created, the connection will | ||
be created to the correct backend. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
// Binary client is an example client. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"google.golang.org/grpc" | ||
ecpb "google.golang.org/grpc/examples/features/proto/echo" | ||
"google.golang.org/grpc/resolver" | ||
) | ||
|
||
const ( | ||
exampleScheme = "example" | ||
exampleServiceName = "resolver.example.grpc.io" | ||
|
||
backendAddr = "localhost:50051" | ||
) | ||
|
||
func callUnaryEcho(c ecpb.EchoClient, message string) { | ||
ctx, cancel := context.WithTimeout(context.Background(), time.Second) | ||
defer cancel() | ||
r, err := c.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message}) | ||
if err != nil { | ||
log.Fatalf("could not greet: %v", err) | ||
} | ||
fmt.Println(r.Message) | ||
} | ||
|
||
func makeRPCs(cc *grpc.ClientConn, n int) { | ||
hwc := ecpb.NewEchoClient(cc) | ||
for i := 0; i < n; i++ { | ||
callUnaryEcho(hwc, "this is examples/name_resolving") | ||
} | ||
} | ||
|
||
func main() { | ||
passthroughConn, err := grpc.Dial( | ||
fmt.Sprintf("passthrough:///%s", backendAddr), // Dial to "passthrough:///localhost:50051" | ||
grpc.WithInsecure(), | ||
) | ||
if err != nil { | ||
log.Fatalf("did not connect: %v", err) | ||
} | ||
defer passthroughConn.Close() | ||
|
||
fmt.Printf("--- calling helloworld.Greeter/SayHello to \"passthrough:///%s\"\n", backendAddr) | ||
makeRPCs(passthroughConn, 10) | ||
|
||
fmt.Println() | ||
|
||
exampleConn, err := grpc.Dial( | ||
fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName), // Dial to "example:///resolver.example.grpc.io" | ||
grpc.WithInsecure(), | ||
) | ||
if err != nil { | ||
log.Fatalf("did not connect: %v", err) | ||
} | ||
defer exampleConn.Close() | ||
|
||
fmt.Printf("--- calling helloworld.Greeter/SayHello to \"%s:///%s\"\n", exampleScheme, exampleServiceName) | ||
makeRPCs(exampleConn, 10) | ||
} | ||
|
||
// Following is an example name resolver. It includes a | ||
// ResolverBuilder(https://godoc.org/google.golang.org/grpc/resolver#Builder) | ||
// and a Resolver(https://godoc.org/google.golang.org/grpc/resolver#Resolver). | ||
// | ||
// A ResolverBuilder is registered for a scheme (in this example, "example" is | ||
// the scheme). When a ClientConn is created for this scheme, the | ||
// ResolverBuilder will be picked to build a Resolver. Note that a new Resolver | ||
// is built for each ClientConn. The Resolver will watch the updates for the | ||
// target, and send updates to the ClientConn. | ||
|
||
// exampleResolverBuilder is a | ||
// ResolverBuilder(https://godoc.org/google.golang.org/grpc/resolver#Builder). | ||
type exampleResolverBuilder struct{} | ||
|
||
func (*exampleResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { | ||
r := &exampleResolver{ | ||
target: target, | ||
cc: cc, | ||
addrsStore: map[string][]string{ | ||
exampleServiceName: {backendAddr}, | ||
}, | ||
} | ||
r.start() | ||
return r, nil | ||
} | ||
func (*exampleResolverBuilder) Scheme() string { return exampleScheme } | ||
|
||
// exampleResolver is a | ||
// Resolver(https://godoc.org/google.golang.org/grpc/resolver#Resolver). | ||
type exampleResolver struct { | ||
target resolver.Target | ||
cc resolver.ClientConn | ||
addrsStore map[string][]string | ||
} | ||
|
||
func (r *exampleResolver) start() { | ||
addrStrs := r.addrsStore[r.target.Endpoint] | ||
addrs := make([]resolver.Address, len(addrStrs), len(addrStrs)) | ||
for i, s := range addrStrs { | ||
addrs[i] = resolver.Address{Addr: s} | ||
} | ||
r.cc.NewAddress(addrs) | ||
} | ||
func (*exampleResolver) ResolveNow(o resolver.ResolveNowOption) {} | ||
func (*exampleResolver) Close() {} | ||
|
||
func init() { | ||
// Register the example ResolverBuilder. This is usually done in a package's | ||
// init() function. | ||
resolver.Register(&exampleResolverBuilder{}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* | ||
* Copyright 2018 gRPC authors. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
*/ | ||
|
||
// Binary server is an example server. | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"net" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/codes" | ||
ecpb "google.golang.org/grpc/examples/features/proto/echo" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
const addr = "localhost:50051" | ||
|
||
type ecServer struct { | ||
addr string | ||
} | ||
|
||
func (s *ecServer) UnaryEcho(ctx context.Context, req *ecpb.EchoRequest) (*ecpb.EchoResponse, error) { | ||
return &ecpb.EchoResponse{Message: fmt.Sprintf("%s (from %s)", req.Message, s.addr)}, nil | ||
} | ||
func (s *ecServer) ServerStreamingEcho(*ecpb.EchoRequest, ecpb.Echo_ServerStreamingEchoServer) error { | ||
return status.Errorf(codes.Unimplemented, "not implemented") | ||
} | ||
func (s *ecServer) ClientStreamingEcho(ecpb.Echo_ClientStreamingEchoServer) error { | ||
return status.Errorf(codes.Unimplemented, "not implemented") | ||
} | ||
func (s *ecServer) BidirectionalStreamingEcho(ecpb.Echo_BidirectionalStreamingEchoServer) error { | ||
return status.Errorf(codes.Unimplemented, "not implemented") | ||
} | ||
|
||
func startServer(addr string) { | ||
} | ||
|
||
func main() { | ||
lis, err := net.Listen("tcp", addr) | ||
if err != nil { | ||
log.Fatalf("failed to listen: %v", err) | ||
} | ||
s := grpc.NewServer() | ||
ecpb.RegisterEchoServer(s, &ecServer{addr: addr}) | ||
log.Printf("serving on %s\n", addr) | ||
if err := s.Serve(lis); err != nil { | ||
log.Fatalf("failed to serve: %v", err) | ||
} | ||
} |