The 'runner' can help you run your application with any a components and use one point for running and stopping them
Basic usage will be looking something like that
package someapp
import (
"net""go.uber.org/zap""github.com/lygo/runner"
)
typeConfigstruct {
HttpAddrstringGrpcAddrstringDBURIstringOtherGrpcServiceEndpointstringGracefulTimeout time.Duration
}
// static validatefunc (cConfig) Validate() error {
// just correct addresses and some database uri// or any params like timeout and retry config sizereturnnil
}
funcNew(cfgConfig) (app*runner.App, errerror) {
// here our config are correct and we will do dynamic validation in the current environment app=runner.New()
deferfunc() {
iferr!=nil {
app.Shutdown()
}
}()
var (
grpcListener net.ListenerapiListener net.Listener
)
grpcListener, err=net.Listen(`tcp`, cfg.GrpcAddr)
iferr!=nil {
return
}
apiListener, err=net.Listen(`tcp`, cfg.HttpAddr)
iferr!=nil {
return
}
// or from config level and formatvarzapLogger*zap.LoggerzapLogger, err=zap.NewDevelopment()
iferr!=nil {
return
}
zapLogger=zapLogger.Named(`service-name`)
app.Slams=append(app.Slams, zapLogger.Sync)
// also any metrics and tracing agent your can adding here // with runnes and slams // of course that code can move to contructor of your grpc servicegrpcSrv:=grpc.NewServer(
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
grpc_ctxtags.StreamServerInterceptor(),
grpc_zap.StreamServerInterceptor(zapLogger),
grpc_recovery.StreamServerInterceptor(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_zap.UnaryServerInterceptor(zapLogger),
grpc_recovery.UnaryServerInterceptor(),
)),
)
someServiceApb.RegisterServiceAServer(grpcSrv, serviceAImpl.New(cfg))
someServiceBpb.RegisterServiceBServer(grpcSrv, serviceBImpl.New(cfg))
app.Runners=append(app.Runners, func() error {
err:=grpcSrv.Serve(grpcListener)
iferr==grpc.ErrServerStopped {
returnnil
}
returnerr
})
app.Slams=append(app.Slams, func() error {
grpcSrv.GracefulStop()
returnnil
})
// registry HTTP handlersmux:=runtime.NewServeMux()
opts:= []grpc.DialOption{grpc.WithInsecure()}
err=someServiceBpb.RegisterServiceAHandlerFromEndpoint(context.Background(), mux, cfg.GrpcAddr, opts)
iferr!=nil {
return
}
err=someServiceApb.RegisterServiceBHandlerFromEndpoint(context.Background(), mux, cfg.GrpcAddr, opts)
iferr!=nil {
return
}
httpSrv:=&http.Server{Handler: mux}
app.Runners=append(app.Runners, func() error {
err:=httpSrv.Serve(apiListener)
iferr==http.ErrServerClosed {
returnnil
}
returnerr
})
app.Slams=append(app.Slams, func() error {
// also from time out ctxDown, cancelGraceful:=context.WithTimeout(context.Background(),cfg.GracefulTimeout)
defercancelGraceful()
returnhttpSrv.Shutdown(ctxDown)
})
return
}and your main.go file will be
package main
import (
"log""os""os/signal""syscall""someapp"
)
funcmain() {
// fill and validate your config on `NewConfig` functioncfg, err:=someapp.NewConfig()
iferr!=nil {
log.Fatal(err)
}
// get your app app, err:=someapp.New(*cfg)
iferr!=nil {
log.Fatal(err)
}
// run all functions from runnersapp.Run()
ch:=make(chan os.Signal, 10)
signal.Notify(ch,
syscall.SIGINT,
syscall.SIGKILL,
syscall.SIGTERM,
syscall.SIGQUIT,
)
deferclose(ch)
// wait when all will started<-app.Started// listen system channel for closing appgofunc() {
sig, ok:=<-chifok {
log.Printf("shutdown process on %s system signal\n", sig)
}
app.Shutdown()
}()
// exist with correct status// if all slams with out error code code 0, in otherwise will 1os.Exit(<-app.Done)
}