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  }