From 0ab367d24aafed7904a3fc64edcbdf8df8f9e990 Mon Sep 17 00:00:00 2001 From: Menghan Li Date: Thu, 6 Feb 2020 14:51:54 -0800 Subject: [PATCH] examples: add client/server to be used for xds examples (#3362) --- examples/features/xds/README.md | 37 ++++++++ examples/features/xds/client/main.go | 97 ++++++++++++++++++++ examples/features/xds/server/main.go | 129 +++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 examples/features/xds/README.md create mode 100644 examples/features/xds/client/main.go create mode 100644 examples/features/xds/server/main.go diff --git a/examples/features/xds/README.md b/examples/features/xds/README.md new file mode 100644 index 00000000..69d75c53 --- /dev/null +++ b/examples/features/xds/README.md @@ -0,0 +1,37 @@ +# gRPC xDS example + +xDS is the protocol initially used by Envoy, that is evolving into a universal +data plan API for service mesh. + +The xDS example is a Hello World client/server capable of being configured with +the XDS management protocol. Out-of-the-box it behaves the same as [our other +hello world +example](https://github.com/grpc/grpc-go/tree/master/examples/helloworld). The +server replies with responses including its hostname. + +**Note** that xDS support is incomplete and experimental, with limited +compatibility. + +## xDS environment setup + +This example doesn't include instuctions to setup xDS environment. Please +refer to documentation specific for your xDS management server. + +The client also needs a bootstrap file. See [gRFC +A27](https://github.com/grpc/proposal/pull/170/files#diff-05ea4a5894abbc0261b006741220598cR100) +for the bootstrap format. + +## The client + +The client application needs to import the xDS package to install the resolver and balancers: + +```go +_ "google.golang.org/grpc/xds/experimental" // To install the xds resolvers and balancers. +``` + +Then, use `xds-experimental` target scheme for the ClientConn. + +``` +$ export GRPC_XDS_BOOTSTRAP=/path/to/bootstrap.json +$ go run client/main.go "xDS world" xds-experimental:///target_service +``` diff --git a/examples/features/xds/client/main.go b/examples/features/xds/client/main.go new file mode 100644 index 00000000..97e1edeb --- /dev/null +++ b/examples/features/xds/client/main.go @@ -0,0 +1,97 @@ +// +build go1.11 + +/* + * + * Copyright 2020 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. + * + */ + +// Package main implements a client for Greeter service. +package main + +import ( + "context" + "flag" + "fmt" + "log" + "time" + + "google.golang.org/grpc" + pb "google.golang.org/grpc/examples/helloworld/helloworld" + + _ "google.golang.org/grpc/xds/experimental" // To install the xds resolvers and balancers. +) + +const ( + defaultTarget = "localhost:50051" + defaultName = "world" +) + +var help = flag.Bool("help", false, "Print usage information") + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), ` +Usage: client [name [target]] + + name + The name you wish to be greeted by. Defaults to %q + target + The URI of the server, e.g. "xds-experimental:///helloworld-service". Defaults to %q +`, defaultName, defaultTarget) + + flag.PrintDefaults() + } +} + +func main() { + flag.Parse() + if *help { + flag.Usage() + return + } + args := flag.Args() + + if len(args) > 2 { + flag.Usage() + return + } + + name := defaultName + if len(args) > 0 { + name = args[0] + } + + target := defaultTarget + if len(args) > 1 { + target = args[1] + } + + // Set up a connection to the server. + conn, err := grpc.Dial(target, grpc.WithInsecure()) + if err != nil { + log.Fatalf("did not connect: %v", err) + } + defer conn.Close() + c := pb.NewGreeterClient(conn) + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) + if err != nil { + log.Fatalf("could not greet: %v", err) + } + log.Printf("Greeting: %s", r.GetMessage()) +} diff --git a/examples/features/xds/server/main.go b/examples/features/xds/server/main.go new file mode 100644 index 00000000..52379df1 --- /dev/null +++ b/examples/features/xds/server/main.go @@ -0,0 +1,129 @@ +// +build go1.11 + +/* + * + * Copyright 2020 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. + * + */ + +// Package main starts Greeter service that will response with the hostname. +package main + +import ( + "context" + "flag" + "fmt" + "log" + "math/rand" + "net" + "os" + "strconv" + "time" + + "google.golang.org/grpc" + pb "google.golang.org/grpc/examples/helloworld/helloworld" +) + +var help = flag.Bool("help", false, "Print usage information") + +const ( + defaultPort = 50051 +) + +// server is used to implement helloworld.GreeterServer. +type server struct { + pb.UnimplementedGreeterServer + + serverName string +} + +func newServer(serverName string) *server { + return &server{ + serverName: serverName, + } +} + +// SayHello implements helloworld.GreeterServer +func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { + log.Printf("Received: %v", in.GetName()) + return &pb.HelloReply{Message: "Hello " + in.GetName() + ", from " + s.serverName}, nil +} + +func determineHostname() string { + hostname, err := os.Hostname() + if err != nil { + log.Printf("Failed to get hostname: %v, will generate one", err) + rand.Seed(time.Now().UnixNano()) + return fmt.Sprintf("generated-%03d", rand.Int()%100) + } + return hostname +} + +func init() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), ` +Usage: server [port [hostname]] + + port + The listen port. Defaults to %d + hostname + The name clients will see in greet responses. Defaults to the machine's hostname +`, defaultPort) + + flag.PrintDefaults() + } +} + +func main() { + flag.Parse() + if *help { + flag.Usage() + return + } + args := flag.Args() + + if len(args) > 2 { + flag.Usage() + return + } + + port := defaultPort + if len(args) > 0 { + var err error + port, err = strconv.Atoi(args[0]) + if err != nil { + log.Printf("Invalid port number: %v", err) + flag.Usage() + return + } + } + + var hostname string + if len(args) > 1 { + hostname = args[1] + } + if hostname == "" { + hostname = determineHostname() + } + + lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s := grpc.NewServer() + pb.RegisterGreeterServer(s, newServer(hostname)) + log.Printf("serving on %s, hostname %s", lis.Addr(), hostname) + s.Serve(lis) +}