A library for sharing descriptors in Go.
Package graceful provides tools for sharing file descriptors between processes.
The most common reason to use it is the ability of so-called graceful restart of an application.
There is an example web application that restarts gracefully.
Graceful proposes a client-server mechanism of sharing file descriptors.
That is, application that owns a descriptors is a server in graceful
terminology, and an application that wants to receive those descriptors is a
client.
The most common use of graceful
looks like this:
// Somewhere close to the application initialization.
//
// By some logic we've decided to receive descriptors from currently running
// instance of the application.
var (
ln net.Listener
file *os.File
)
err := graceful.Receive("/var/run/app.sock", func(fd int, meta io.Reader) error {
// Handle received descriptor with concrete application logic.
//
// meta is an additional information that corresponds to the descriptor and
// represented by an io.Reader. User is free to select strategy of
// marshaling/unmarshaling this information.
if meta == nil {
// In our example listener is passed with empty meta.
ln = graceful.FdListener(fd)
return nil
}
// There is a helper type called graceful.Meta that could be used to send
// key-value pairs of meta without additional lines of code. Lets use it.
m := new(graceful.Meta)
if _, err := m.ReadFrom(meta); err != nil {
// Prevent further interaction with the connection due to the error.
return err
}
file = os.NewFile(fd, m["name"])
})
if err != nil {
// Handle error.
}
...
// Somewhere close to the application termination.
//
// By some logic we've decided to send our descriptors to a new application
// instance that is probably just started.
//
// This code will send `ln` and `file` descriptors to every accepted
// connection on unix domain socket "/var/run/app.sock".
go graceful.ListenAndServe("/var/run/app.sock", graceful.SequenceHandler(
graceful.ListenerHandler(ln, nil),
graceful.FileHandler(file, graceful.Meta{
"name": file.Name(),
}),
))
That is, graceful
does not force users to stick to some logic of processing
restarts. It just provides mechanism for sharing descriptors in a client-server
way.
If you have a more difficult logic of restarts, you could use less general API
of graceful
:
// Listen for an upcoming instance at the socket.
ln, err := net.Listen("unix", "/var/run/app.sock")
if err != nil {
// Handle error.
}
defer ln.Close()
// Accept client connection from another application instance.
conn, err := ln.Accept()
if err != nil {
// Handle error.
}
defer conn.Close()
// Send some application specific data first. This is useful when data is much
// larger than graceful i/o buffers and won't be putted in the descriptor meta.
if _, err := conn.Write(data); err != nil {
// Handle error.
}
// Then send some descriptors to the connection.
graceful.SendListenerTo(conn, ln, nil)
graceful.SendListenerTo(conn, file, graceful.Meta{
"name": file.Name(),
})
// Alternatively, you could send custom meta information with the fd.
//
// bytes.Buffer instance implements io.WriterTo, so wa are free to use it as
// meta argument for graceful.Send*() calls.
buf := new(bytes.Buffer)
buf.Write("Hello, I am meta!")
// Now send some descriptor with custom meta bytes.
// Client will receive io.Reader instance as meta that contains our simple
// string.
graceful.SendTo(conn, someFD, buf)
There is an example web application that handles restarts
gracefully. Note that it does not handle SIGTERM
signal just to show up that
graceful
if flexible and could be used in a simple way.
This library is not tagged as stable version (and not tagged at all yet). This means that backward compatibility can be broken due some changes or refactoring.