github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/grpcserver/server.go (about) 1 package grpcserver 2 3 import ( 4 "net" 5 "sync" 6 7 "go.uber.org/atomic" 8 9 "github.com/rs/zerolog" 10 11 "google.golang.org/grpc" 12 _ "google.golang.org/grpc/encoding/gzip" //required for gRPC compression 13 14 _ "github.com/onflow/flow-go/engine/common/grpc/compressor/deflate" // required for gRPC compression 15 _ "github.com/onflow/flow-go/engine/common/grpc/compressor/snappy" // required for gRPC compression 16 17 "github.com/onflow/flow-go/module/component" 18 "github.com/onflow/flow-go/module/irrecoverable" 19 ) 20 21 // GrpcServer wraps `grpc.Server` and allows to manage it using `component.Component` interface. It can be injected 22 // into different engines making it possible to use single grpc server for multiple services which live in different modules. 23 type GrpcServer struct { 24 component.Component 25 log zerolog.Logger 26 Server *grpc.Server 27 grpcSignalerCtx *atomic.Pointer[irrecoverable.SignalerContext] 28 29 grpcListenAddr string // the GRPC server address as ip:port 30 31 addrLock sync.RWMutex 32 grpcAddress net.Addr 33 } 34 35 var _ component.Component = (*GrpcServer)(nil) 36 37 // NewGrpcServer returns a new grpc server. 38 func NewGrpcServer(log zerolog.Logger, 39 grpcListenAddr string, 40 grpcServer *grpc.Server, 41 grpcSignalerCtx *atomic.Pointer[irrecoverable.SignalerContext], 42 ) *GrpcServer { 43 server := &GrpcServer{ 44 log: log, 45 Server: grpcServer, 46 grpcListenAddr: grpcListenAddr, 47 grpcSignalerCtx: grpcSignalerCtx, 48 } 49 server.Component = component.NewComponentManagerBuilder(). 50 AddWorker(server.serveGRPCWorker). 51 AddWorker(server.shutdownWorker). 52 Build() 53 return server 54 } 55 56 // serveGRPCWorker is a worker routine which starts the gRPC server. 57 // The ready callback is called after the server address is bound and set. 58 func (g *GrpcServer) serveGRPCWorker(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 59 g.log = g.log.With().Str("grpc_address", g.grpcListenAddr).Logger() 60 g.log.Info().Msg("starting grpc server on address") 61 62 g.grpcSignalerCtx.Store(&ctx) 63 64 l, err := net.Listen("tcp", g.grpcListenAddr) 65 if err != nil { 66 g.log.Err(err).Msg("failed to start the grpc server") 67 ctx.Throw(err) 68 return 69 } 70 71 // save the actual address on which we are listening (may be different from g.config.GRPCListenAddr if not port 72 // was specified) 73 g.addrLock.Lock() 74 g.grpcAddress = l.Addr() 75 g.addrLock.Unlock() 76 g.log.Debug().Msg("listening on port") 77 ready() 78 79 err = g.Server.Serve(l) // blocking call 80 if err != nil { 81 g.log.Err(err).Msg("fatal error in grpc server") 82 ctx.Throw(err) 83 } 84 } 85 86 // GRPCAddress returns the listen address of the GRPC server. 87 // Guaranteed to be non-nil after Engine.Ready is closed. 88 func (g *GrpcServer) GRPCAddress() net.Addr { 89 g.addrLock.RLock() 90 defer g.addrLock.RUnlock() 91 return g.grpcAddress 92 } 93 94 // shutdownWorker is a worker routine which shuts down server when the context is cancelled. 95 func (g *GrpcServer) shutdownWorker(ctx irrecoverable.SignalerContext, ready component.ReadyFunc) { 96 ready() 97 <-ctx.Done() 98 g.Server.GracefulStop() 99 }