github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2015/gotham-grpc/frontend/frontend.go (about)

     1  // The frontend command runs a Google server that combines results
     2  // from multiple backends.
     3  package main
     4  
     5  import (
     6  	"flag"
     7  	"log"
     8  	"net"
     9  	"net/http"
    10  	_ "net/http/pprof"
    11  	"strings"
    12  	"sync"
    13  
    14  	"golang.org/x/net/context"
    15  	pb "golang.org/x/talks/2015/gotham-grpc/search"
    16  	"google.golang.org/grpc"
    17  )
    18  
    19  var (
    20  	backends = flag.String("backends", "localhost:36061,localhost:36062", "comma-separated backend server addresses")
    21  )
    22  
    23  type server struct {
    24  	backends []pb.GoogleClient
    25  }
    26  
    27  // Search issues Search RPCs in parallel to the backends and returns
    28  // the first result.
    29  func (s *server) Search(ctx context.Context, req *pb.Request) (*pb.Result, error) { // HL
    30  	c := make(chan result, len(s.backends))
    31  	for _, b := range s.backends {
    32  		go func(backend pb.GoogleClient) { // HL
    33  			res, err := backend.Search(ctx, req) // HL
    34  			c <- result{res, err}                // HL
    35  		}(b) // HL
    36  	}
    37  	first := <-c                // HL
    38  	return first.res, first.err // HL
    39  }
    40  
    41  type result struct {
    42  	res *pb.Result
    43  	err error
    44  }
    45  
    46  // Watch runs Watch RPCs in parallel on the backends and returns a
    47  // merged stream of results.
    48  func (s *server) Watch(req *pb.Request, stream pb.Google_WatchServer) error { // HL
    49  	ctx := stream.Context()
    50  	c := make(chan result) // HL
    51  	var wg sync.WaitGroup
    52  	for _, b := range s.backends {
    53  		wg.Add(1)
    54  		go func(backend pb.GoogleClient) { // HL
    55  			defer wg.Done()                    // HL
    56  			watchBackend(ctx, backend, req, c) // HL
    57  		}(b) // HL
    58  	}
    59  	go func() {
    60  		wg.Wait()
    61  		close(c) // HL
    62  	}()
    63  	for res := range c { // HL
    64  		if res.err != nil {
    65  			return res.err
    66  		}
    67  		if err := stream.Send(res.res); err != nil { // HL
    68  			return err // HL
    69  		} // HL
    70  	}
    71  	return nil
    72  }
    73  
    74  // watchBackend runs Watch on a single backend and sends results on c.
    75  // watchBackend returns when ctx.Done is closed or stream.Recv fails.
    76  func watchBackend(ctx context.Context, backend pb.GoogleClient, req *pb.Request, c chan<- result) {
    77  	stream, err := backend.Watch(ctx, req) // HL
    78  	if err != nil {
    79  		select {
    80  		case c <- result{err: err}: // HL
    81  		case <-ctx.Done():
    82  		}
    83  		return
    84  	}
    85  	for {
    86  		res, err := stream.Recv() // HL
    87  		select {
    88  		case c <- result{res, err}: // HL
    89  			if err != nil {
    90  				return
    91  			}
    92  		case <-ctx.Done():
    93  			return
    94  		}
    95  	}
    96  }
    97  
    98  func main() {
    99  	flag.Parse()
   100  	go http.ListenAndServe(":36660", nil)   // HTTP debugging
   101  	lis, err := net.Listen("tcp", ":36060") // RPC port
   102  	if err != nil {
   103  		log.Fatalf("failed to listen: %v", err)
   104  	}
   105  	s := new(server)
   106  	for _, addr := range strings.Split(*backends, ",") {
   107  		conn, err := grpc.Dial(addr, grpc.WithInsecure())
   108  		if err != nil {
   109  			log.Fatalf("fail to dial: %v", err)
   110  		}
   111  		client := pb.NewGoogleClient(conn)
   112  		s.backends = append(s.backends, client)
   113  	}
   114  	g := grpc.NewServer()
   115  	pb.RegisterGoogleServer(g, s)
   116  	g.Serve(lis)
   117  }