TCP Keepalives

Enabled TCP keepalive packets to determine if the connection is still
valid, and terminate if needed. It also helps with maintaining idle
connections.

Default to 300 seconds and can be changed by:

    CONFIG SET keepalive 300

addresses #145: clients not being cleaned up properly
This commit is contained in:
Josh Baker 2017-02-09 10:01:59 -07:00
parent 9a37f33a2c
commit 56e0bac24c
3 changed files with 41 additions and 1 deletions

View File

@ -20,9 +20,10 @@ const (
ProtectedMode = "protected-mode" ProtectedMode = "protected-mode"
MaxMemory = "maxmemory" MaxMemory = "maxmemory"
AutoGC = "autogc" AutoGC = "autogc"
KeepAlive = "keepalive"
) )
var validProperties = []string{RequirePass, LeaderAuth, ProtectedMode, MaxMemory, AutoGC} var validProperties = []string{RequirePass, LeaderAuth, ProtectedMode, MaxMemory, AutoGC, KeepAlive}
// Config is a tile38 config // Config is a tile38 config
type Config struct { type Config struct {
@ -44,6 +45,8 @@ type Config struct {
MaxMemory int `json:"-"` MaxMemory int `json:"-"`
AutoGCP string `json:"autogc,omitempty"` AutoGCP string `json:"autogc,omitempty"`
AutoGC uint64 `json:"-"` AutoGC uint64 `json:"-"`
KeepAliveP string `json:"keepalive,omitempty"`
KeepAlive int `json:"-"`
} }
func (c *Controller) loadConfig() error { func (c *Controller) loadConfig() error {
@ -74,6 +77,9 @@ func (c *Controller) loadConfig() error {
if err := c.setConfigProperty(AutoGC, c.config.AutoGCP, true); err != nil { if err := c.setConfigProperty(AutoGC, c.config.AutoGCP, true); err != nil {
return err return err
} }
if err := c.setConfigProperty(KeepAlive, c.config.KeepAliveP, true); err != nil {
return err
}
return nil return nil
} }
@ -161,7 +167,19 @@ func (c *Controller) setConfigProperty(name, value string, fromLoad bool) error
default: default:
invalid = true invalid = true
} }
case KeepAlive:
if value == "" {
c.config.KeepAlive = 300
} else {
keepalive, err := strconv.ParseUint(value, 10, 64)
if err != nil {
invalid = true
} else {
c.config.KeepAlive = int(keepalive)
} }
}
}
if invalid { if invalid {
return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name) return fmt.Errorf("Invalid argument '%s' for CONFIG SET '%s'", value, name)
} }
@ -192,6 +210,8 @@ func (c *Controller) getConfigProperty(name string) string {
return c.config.ProtectedMode return c.config.ProtectedMode
case MaxMemory: case MaxMemory:
return formatMemSize(c.config.MaxMemory) return formatMemSize(c.config.MaxMemory)
case KeepAlive:
return strconv.FormatUint(uint64(c.config.KeepAlive), 10)
} }
} }
@ -216,6 +236,7 @@ func (c *Controller) writeConfig(writeProperties bool) error {
c.config.ProtectedModeP = c.config.ProtectedMode c.config.ProtectedModeP = c.config.ProtectedMode
c.config.MaxMemoryP = formatMemSize(c.config.MaxMemory) c.config.MaxMemoryP = formatMemSize(c.config.MaxMemory)
c.config.AutoGCP = strconv.FormatUint(c.config.AutoGC, 10) c.config.AutoGCP = strconv.FormatUint(c.config.AutoGC, 10)
c.config.KeepAliveP = strconv.FormatUint(uint64(c.config.KeepAlive), 10)
} }
var data []byte var data []byte
data, err = json.MarshalIndent(c.config, "", "\t") data, err = json.MarshalIndent(c.config, "", "\t")

View File

@ -227,8 +227,17 @@ func ListenAndServeEx(host string, port int, dir string, ln *net.Listener, http
return is return is
} }
var clientId uint64 var clientId uint64
opened := func(conn *server.Conn) { opened := func(conn *server.Conn) {
c.mu.Lock() c.mu.Lock()
if c.config.KeepAlive > 0 {
err := conn.SetKeepAlive(
time.Duration(c.config.KeepAlive) * time.Second)
if err != nil {
log.Warnf("could not set keepalive for connection: %v",
conn.RemoteAddr().String())
}
}
clientId++ clientId++
c.conns[conn] = &clientConn{ c.conns[conn] = &clientConn{
id: clientId, id: clientId,

View File

@ -45,6 +45,16 @@ type Conn struct {
Authenticated bool Authenticated bool
} }
func (conn Conn) SetKeepAlive(period time.Duration) error {
if tcp, ok := conn.Conn.(*net.TCPConn); ok {
if err := tcp.SetKeepAlive(true); err != nil {
return err
}
return tcp.SetKeepAlivePeriod(period)
}
return nil
}
var errCloseHTTP = errors.New("close http") var errCloseHTTP = errors.New("close http")
// ListenAndServe starts a tile38 server at the specified address. // ListenAndServe starts a tile38 server at the specified address.