Comments (4)
I think the key of using tableflip is to replace net.Listen with upg.Fds.Listen, so that when Upgrade the listening socket will be inherited by child.
That sounds about right.
Can you share the snippet that does the listen and then serve? Looks like you're using net/http
, are you calling ListenAndServe directly?
from tableflip.
I'm listening with upg.Fds.Listen
and then pass the net.Listener
to serve
function, I do the same for both http server and my private tcp server:
...
upg, err := tableflip.New(tableflip.Options{PIDFile: pidFile})
if err != nil {
panic(err)
}
defer upg.Stop()
....
httpServer := &http.Server{Addr: httpAddr}
ln, err := upg.Fds.Listen("tcp", httpAddr)
if err != nil {
instance.Logger().Errorln("Listen httpAddr err", err)
return
}
var g run.Group
g.Add(func() error {
// upg.Fds.Listen is passed to qserver as func, and called inside ListenAndServe
err := qserver.ListenAndServe()
if err != nil {
instance.Logger().Errorln("ListenAndServe err", err)
}
return err
}, func(err error) {
shutdownQRPC(qserver)
instance.Logger().Infoln("shutdownQRPC err", err)
})
g.Add(func() error {
...apis omited...
err = httpServer.Serve(ln)
if err != nil {
instance.Logger().Errorln("httpServer.Serve err", err)
}
return err
}, func(err error) {
shutdownHTTP(httpServer)
instance.Logger().Infoln("httpServer.Shutdown", err)
})
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGUSR1, syscall.SIGQUIT)
for range sig {
// upgrade on signal
instance.Logger().Errorln("GracefulUpgrade", config.Load().GracefulUpgrade)
instance.Logger().Errorln("upgrade start")
err := upg.Upgrade()
instance.Logger().Errorln("upgrade end", err)
if err != nil {
instance.Logger().Errorln("upgrade failed:", err)
}
}
}()
groupDoneCh := make(chan struct{})
go func() {
err := g.Run()
if err != nil {
instance.Logger().Errorln("Group.Run err", err)
}
close(groupDoneCh)
}()
if err := upg.Ready(); err != nil {
instance.Logger().Errorln("upg.Ready err", err)
return
}
instance.Logger().Infoln("child ready to serve")
// ready to exit
select {
case <-upg.Exit():
case <-groupDoneCh:
}
instance.Logger().Errorln("parent prepare for exit")
shutdownQRPC(qserver)
shutdownHTTP(httpServer)
And here's the code for qserver.ListenAndServe
:
// ListenAndServe starts listening on all bindings
func (srv *Server) ListenAndServe() error {
var g run.Group
for i := range srv.bindings {
idx := i
binding := srv.bindings[i]
g.Add(func() error {
return srv.listenAndServe(idx, binding)
}, func(err error) {
serr := srv.Shutdown()
LogError("err", err, "serr", serr)
})
}
return g.Run()
}
func (srv *Server) listenAndServe(idx int, binding ServerBinding) (err error) {
var (
ln net.Listener
)
if binding.ListenFunc != nil {
ln, err = binding.ListenFunc("tcp", binding.Addr)
} else {
ln, err = net.Listen("tcp", binding.Addr)
}
if err != nil {
return
}
err = srv.Serve(ln.(*net.TCPListener), idx)
return
}
Here's the complete code:
https://github.com/zhiqiangxu/qchat/blob/master/app/server/common.go
from tableflip.
Ah, I think the problem is that you have a race condition between calling Ready
in main and Listen
in Server.ListenAndServe. Ready closes all inherited-but-unused Fds. The following ordering is causing your problem:
- go ListenAndServe()
- Ready()
- binding.ListenFunc()
Here, binding.ListenFunc won't be able to use the inherited Fd, because it was already closed by Ready. It then tries to do a regular net.Listen, which fails because the old process is still around and holding onto the listening socket.
To fix this I recommend creating all listeners in main()
and only then calling Ready.
from tableflip.
Nice catch! I'll try it out asap:)
from tableflip.
Related Issues (20)
- supervisord HOT 4
- [Question] Windows HOT 4
- Expose parent presence
- support unix socket HOT 2
- Logs go missing after systemd-journald restart HOT 2
- How to integrate with systemd and supervisor? HOT 2
- Impossible to build for windows HOT 2
- Add an example for supervisord integration HOT 1
- Enable SO_REUSEPORT HOT 1
- Allow multiple back-to-back graceful upgrades HOT 1
- Request for updating README.MD HOT 1
- Keep foreground control with signal propagation after successful upgrade HOT 5
- Hello, is https not supported? HOT 1
- upg.Exit() do not effective in go routine ? HOT 1
- [Bug] Connections not closed on inherited net.Conn HOT 2
- Can't catch syscall.SIGINT after upgrade HOT 1
- how can i use it for rpcx restart
- Overseer HOT 3
- Upgrade function documentation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tableflip.