Newer
Older
package server
import (
"io"
"net"
"runtime/debug"
config "code.fbi.h-da.de/danet/costaquanta/ctrl/internal"
"code.fbi.h-da.de/danet/costaquanta/ctrl/internal/application"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// GRPCServer is an interface that defines the methods for a gRPC server.
// It is used to start the server and register services.
// It also implements the io.Closer interface to allow for graceful shutdown.
type GRPCServer interface {
Start(func(server *grpc.Server, app *application.Application))
io.Closer
}
type gRPCServer struct {
logger *zap.SugaredLogger
grpcServer *grpc.Server
config config.Config
app *application.Application
}
func NewGrpcServer(
config config.Config,
app *application.Application,
logger *zap.SugaredLogger,
) (GRPCServer, error) {
options, err := buildOptions(logger)
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
if err != nil {
return nil, err
}
server := grpc.NewServer(options...)
return &gRPCServer{
config: config,
grpcServer: server,
app: app,
logger: logger,
}, err
}
func (g gRPCServer) Start(serviceRegister func(server *grpc.Server, app *application.Application)) {
grpcListener, err := net.Listen("tcp", ""+g.config.AddrBind)
if err != nil {
g.logger.Fatal("failed to start grpc server", zap.Any("err", err))
}
serviceRegister(g.grpcServer, g.app)
g.logger.Info("start grpc server success ", zap.Any("endpoint", grpcListener.Addr()))
if err := g.grpcServer.Serve(grpcListener); err != nil {
g.logger.Fatal("failed to grpc server serve", zap.Any("err", err))
}
}
func (g gRPCServer) Close() error {
g.grpcServer.GracefulStop()
return nil
}
func buildOptions(
logs *zap.SugaredLogger,
) ([]grpc.ServerOption, error) {
grpcPanicRecoveryHandler := func(p any) (err error) {
logs.Errorf("recovered from gRPC panic %+v; %v", p, debug.Stack())
return status.Errorf(codes.Internal, "%s", p)
}
return []grpc.ServerOption{
grpc.StatsHandler(otelgrpc.NewServerHandler()),
grpc.ChainUnaryInterceptor(
recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(grpcPanicRecoveryHandler)),
),
grpc.ChainStreamInterceptor(
recovery.StreamServerInterceptor(
recovery.WithRecoveryHandler(grpcPanicRecoveryHandler),
),
),
}, nil
}