how to use truss creating a micro-service with service discovery function?
always-waiting opened this issue · 7 comments
Due to svc/server/run.go is generated by truss, i have to modify the file to make service has SD.
func Run(cfg handlers.Config) {
service := handlers.NewService()
endpoints := NewEndpoints(service)
// Mechanical domain.
errc := make(chan error)
// register SD
registar, err := DefaultConfig.GetSDRegister()
if err != nil {
panic(err)
}
// save config to consul
go func() {
if err := DefaultConfig.SaveBasicCfg(); err != nil {
errc <- err
}
}()
// Interrupt handler.
go handlers.InterruptHandler(errc)
// Debug listener.
go func() {
log.Println("transport", "debug", "addr", cfg.DebugAddr)
m := http.NewServeMux()
m.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
m.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
m.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
m.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
m.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
errc <- http.ListenAndServe(cfg.DebugAddr, m)
}()
// HTTP transport.
go func() {
log.Println("transport", "HTTP", "addr", cfg.HTTPAddr)
h := svc.MakeHTTPHandler(endpoints)
registar.Register()
errc <- http.ListenAndServe(cfg.HTTPAddr, h)
}()
// gRPC transport.
go func() {
log.Println("transport", "gRPC", "addr", cfg.GRPCAddr)
ln, err := net.Listen("tcp", cfg.GRPCAddr)
if err != nil {
errc <- err
return
}
srv := svc.MakeGRPCServer(endpoints)
s := grpc.NewServer()
pb.RegisterAdminServer(s, srv)
errc <- s.Serve(ln)
}()
// Run!
log.Println("exit", <-errc)
registar.Deregister()
}
is there any other methods to make it?
@zaquestion
Could you help me please?
@always-waiting I wrote the below, but then noticed you're calling functions on DefaultConfig
that don't exist, I might need a bit more context on how you modified those? Also if those changes could be done elsewhere?
I might be overlooking something in your code sample, but it looks like you just need a bit of logic in your startup and shutdown paths? Sorry I usually do SD at an infra layer. I don't think you need a custom "main.go" for this (if you end up needing one see: https://github.com/metaverse/truss/wiki/Using-a-custom-main.go-with-generated-truss-services).
For startup and shutdown logic truss uses NewService
in handlers/handlers.go
and InterruptHandler
in handlers/hooks.go
respectively
// NewService returns a naïve, stateless implementation of Service.
func NewService() pb.EchoServer {
// register SD
registar, err := DefaultConfig.GetSDRegister()
if err != nil {
panic(err)
}
// save config to consul
go func() {
if err := DefaultConfig.SaveBasicCfg(); err != nil {
errc <- err
}
}()
return echoService{}
}
func InterruptHandler(errc chan<- error) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
terminateError := fmt.Errorf("%s", <-c)
// Place whatever shutdown handling you want here
registar.Deregister()
errc <- terminateError
}
of course theres's always the matter of scoping the registar
variable. You can either make it a package level variable or modifiy hooks.go:InterruptHandler
to be a function variable and use a closure to scope registar
. Personally I find the former to be simpler and without consequence, but it can be considered a smell...
@always-waiting did that help clear anything up?
@zaquestion
Thank you for your attention!
I prefer to the method which modifies NewService
in handlers/handlers.go
and InterruptHandler
in handlers/hooks.go
respectively. But, I have a worry about abnormal exit. For example, if a error occur at
go func() {
log.Println("transport", "HTTP", "addr", cfg.HTTPAddr)
h := svc.MakeHTTPHandler(endpoints)
registar.Register()
// A error occur
errc <- http.ListenAndServe(cfg.HTTPAddr, h)
}()
or
go func() {
log.Println("transport", "gRPC", "addr", cfg.GRPCAddr)
ln, err := net.Listen("tcp", cfg.GRPCAddr)
if err != nil {
errc <- err
return
}
srv := svc.MakeGRPCServer(endpoints)
s := grpc.NewServer()
pb.RegisterAdminServer(s, srv)
// A error occur
errc <- s.Serve(ln)
}()
in svc/server/run.go
, registar.Deregister()
have no chance to execute.
@always-waiting Sorry for the long delay -- life be cray rn. I think you've exposed an edgecase with our shutdown hook. Without having thought to critically about this, it does seem like we need an additional hook to guarantee certain graceful shutdown in cases like these.
fwiw, you can definitely work around this with a custom main.go
, but truss is lacking a "native" mechanism. Surprisingly this hasn't come up until this point.
@zaquestion
thanks a lot, very helpful!