200 lines
5.6 KiB
Go
200 lines
5.6 KiB
Go
// Copyright 2018 The OpenPitrix Authors. All rights reserved.
|
|
// Use of this source code is governed by a Apache license
|
|
// that can be found in the LICENSE file.
|
|
|
|
package manager
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"runtime/debug"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/jsonpb"
|
|
"github.com/golang/protobuf/proto"
|
|
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
|
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
|
|
grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator"
|
|
"github.com/pkg/errors"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/keepalive"
|
|
"google.golang.org/grpc/reflection"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"openpitrix.io/openpitrix/pkg/config"
|
|
"openpitrix.io/openpitrix/pkg/db"
|
|
"openpitrix.io/openpitrix/pkg/gerr"
|
|
"openpitrix.io/openpitrix/pkg/logger"
|
|
"openpitrix.io/openpitrix/pkg/util/ctxutil"
|
|
"openpitrix.io/openpitrix/pkg/version"
|
|
)
|
|
|
|
type checkerT func(ctx context.Context, req interface{}) error
|
|
type builderT func(ctx context.Context, req interface{}) interface{}
|
|
|
|
var (
|
|
defaultChecker checkerT
|
|
defaultBuilder builderT
|
|
)
|
|
|
|
type GrpcServer struct {
|
|
ServiceName string
|
|
Port int
|
|
showErrorCause bool
|
|
checker checkerT
|
|
builder builderT
|
|
mysqlConfig config.MysqlConfig
|
|
}
|
|
|
|
type RegisterCallback func(*grpc.Server)
|
|
|
|
func NewGrpcServer(serviceName string, port int) *GrpcServer {
|
|
return &GrpcServer{
|
|
ServiceName: serviceName,
|
|
Port: port,
|
|
showErrorCause: false,
|
|
checker: defaultChecker,
|
|
builder: defaultBuilder,
|
|
}
|
|
}
|
|
|
|
func (g *GrpcServer) ShowErrorCause(b bool) *GrpcServer {
|
|
g.showErrorCause = b
|
|
return g
|
|
}
|
|
|
|
func (g *GrpcServer) WithChecker(c checkerT) *GrpcServer {
|
|
g.checker = c
|
|
return g
|
|
}
|
|
|
|
func (g *GrpcServer) WithBuilder(b builderT) *GrpcServer {
|
|
g.builder = b
|
|
return g
|
|
}
|
|
|
|
func (g *GrpcServer) WithMysqlConfig(cfg config.MysqlConfig) *GrpcServer {
|
|
g.mysqlConfig = cfg
|
|
return g
|
|
}
|
|
|
|
func (g *GrpcServer) Serve(callback RegisterCallback, opt ...grpc.ServerOption) {
|
|
version.PrintVersionInfo(func(s string, i ...interface{}) {
|
|
logger.Info(nil, s, i)
|
|
})
|
|
logger.Info(nil, "Service [%s] start listen at port [%d]", g.ServiceName, g.Port)
|
|
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", g.Port))
|
|
if err != nil {
|
|
err = errors.WithStack(err)
|
|
logger.Critical(nil, "failed to listen: %+v", err)
|
|
}
|
|
|
|
builtinOptions := []grpc.ServerOption{
|
|
grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{
|
|
MinTime: 10 * time.Second,
|
|
PermitWithoutStream: true,
|
|
}),
|
|
grpc_middleware.WithUnaryServerChain(
|
|
grpc_validator.UnaryServerInterceptor(),
|
|
g.unaryServerLogInterceptor(),
|
|
func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
ctx = db.NewContext(ctx, g.mysqlConfig)
|
|
|
|
if g.checker != nil {
|
|
err = g.checker(ctx, req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return handler(ctx, req)
|
|
},
|
|
func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
if g.builder != nil {
|
|
req = g.builder(ctx, req)
|
|
}
|
|
return handler(ctx, req)
|
|
},
|
|
grpc_recovery.UnaryServerInterceptor(
|
|
grpc_recovery.WithRecoveryHandler(func(p interface{}) error {
|
|
logger.Critical(nil, "GRPC server recovery with error: %+v", p)
|
|
logger.Critical(nil, string(debug.Stack()))
|
|
if e, ok := p.(error); ok {
|
|
return gerr.NewWithDetail(nil, gerr.Internal, e, gerr.ErrorInternalError)
|
|
}
|
|
return gerr.New(nil, gerr.Internal, gerr.ErrorInternalError)
|
|
}),
|
|
),
|
|
),
|
|
grpc_middleware.WithStreamServerChain(
|
|
grpc_recovery.StreamServerInterceptor(
|
|
grpc_recovery.WithRecoveryHandler(func(p interface{}) error {
|
|
logger.Critical(nil, "GRPC server recovery with error: %+v", p)
|
|
logger.Critical(nil, string(debug.Stack()))
|
|
if e, ok := p.(error); ok {
|
|
return gerr.NewWithDetail(nil, gerr.Internal, e, gerr.ErrorInternalError)
|
|
}
|
|
return gerr.New(nil, gerr.Internal, gerr.ErrorInternalError)
|
|
}),
|
|
),
|
|
),
|
|
}
|
|
|
|
grpcServer := grpc.NewServer(append(opt, builtinOptions...)...)
|
|
reflection.Register(grpcServer)
|
|
callback(grpcServer)
|
|
|
|
if err = grpcServer.Serve(lis); err != nil {
|
|
err = errors.WithStack(err)
|
|
logger.Critical(nil, "%+v", err)
|
|
}
|
|
}
|
|
|
|
var (
|
|
jsonPbMarshaller = &jsonpb.Marshaler{
|
|
OrigName: true,
|
|
}
|
|
)
|
|
|
|
func (g *GrpcServer) unaryServerLogInterceptor() grpc.UnaryServerInterceptor {
|
|
showErrorCause := g.showErrorCause
|
|
|
|
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
|
var err error
|
|
s := ctxutil.GetSender(ctx)
|
|
requestId := ctxutil.GetRequestId(ctx)
|
|
ctx = ctxutil.SetRequestId(ctx, requestId)
|
|
ctx = ctxutil.ContextWithSender(ctx, s)
|
|
locale := ctxutil.GetLocale(ctx)
|
|
ctx = ctxutil.SetLocale(ctx, locale)
|
|
|
|
method := strings.Split(info.FullMethod, "/")
|
|
action := method[len(method)-1]
|
|
if p, ok := req.(proto.Message); ok {
|
|
if content, err := jsonPbMarshaller.MarshalToString(p); err != nil {
|
|
logger.Error(ctx, "Failed to marshal proto message to string [%s] [%+v] [%+v]", action, s, err)
|
|
} else {
|
|
logger.Info(ctx, "Request received [%s] [%+v] [%s]", action, s, content)
|
|
}
|
|
}
|
|
start := time.Now()
|
|
|
|
resp, err := handler(ctx, req)
|
|
|
|
elapsed := time.Since(start)
|
|
logger.Info(ctx, "Handled request [%s] [%+v] exec_time is [%s]", action, s, elapsed)
|
|
if e, ok := status.FromError(err); ok {
|
|
if e.Code() != codes.OK {
|
|
logger.Debug(ctx, "Response is error: %s, %s", e.Code().String(), e.Message())
|
|
if !showErrorCause {
|
|
err = gerr.ClearErrorCause(err)
|
|
}
|
|
}
|
|
}
|
|
return resp, err
|
|
}
|
|
}
|