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 }