Make HTTP Keep-Alive timeout configurable for backend connections
This commit is contained in:
parent
84d7c65039
commit
f6436663eb
8 changed files with 149 additions and 0 deletions
|
@ -477,6 +477,10 @@
|
||||||
The amount of time to wait for a server's response headers after fully writing
|
The amount of time to wait for a server's response headers after fully writing
|
||||||
the request (including its body, if any). If zero, no timeout exists.
|
the request (including its body, if any). If zero, no timeout exists.
|
||||||
|
|
||||||
|
--serverstransport.forwardingtimeouts.idleconntimeout (Default: "90s")
|
||||||
|
The maximum period for which an idle HTTP keep-alive connection to a backend
|
||||||
|
server will remain open before closing itself.
|
||||||
|
|
||||||
--serverstransport.insecureskipverify (Default: "false")
|
--serverstransport.insecureskipverify (Default: "false")
|
||||||
Disable SSL certificate verification.
|
Disable SSL certificate verification.
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,10 @@ The amount of time to wait until a connection to a backend server can be establi
|
||||||
`TRAEFIK_SERVERSTRANSPORT_FORWARDINGTIMEOUTS_RESPONSEHEADERTIMEOUT`:
|
`TRAEFIK_SERVERSTRANSPORT_FORWARDINGTIMEOUTS_RESPONSEHEADERTIMEOUT`:
|
||||||
The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. (Default: ```0```)
|
The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. (Default: ```0```)
|
||||||
|
|
||||||
|
`TRAEFIK_SERVERSTRANSPORT_FORWARDINGTIMEOUTS_IDLECONNTIMEOUT`:
|
||||||
|
The maximum period for which an idle HTTP keep-alive connection to a backend
|
||||||
|
server will remain open before closing itself. (Default: ```90s```)
|
||||||
|
|
||||||
`TRAEFIK_SERVERSTRANSPORT_INSECURESKIPVERIFY`:
|
`TRAEFIK_SERVERSTRANSPORT_INSECURESKIPVERIFY`:
|
||||||
Disable SSL certificate verification. (Default: ```false```)
|
Disable SSL certificate verification. (Default: ```false```)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[ServersTransport.ForwardingTimeouts]
|
[ServersTransport.ForwardingTimeouts]
|
||||||
DialTimeout = 42
|
DialTimeout = 42
|
||||||
ResponseHeaderTimeout = 42
|
ResponseHeaderTimeout = 42
|
||||||
|
IdleConnTimeout = 5
|
||||||
|
|
||||||
[EntryPoints]
|
[EntryPoints]
|
||||||
|
|
||||||
|
|
31
integration/fixtures/timeout/keepalive.toml
Normal file
31
integration/fixtures/timeout/keepalive.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[global]
|
||||||
|
checkNewVersion = false
|
||||||
|
sendAnonymousUsage = false
|
||||||
|
|
||||||
|
[log]
|
||||||
|
level = "DEBUG"
|
||||||
|
|
||||||
|
[serversTransport.forwardingTimeouts]
|
||||||
|
idleConnTimeout = "{{ .IdleConnTimeout }}"
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":8000"
|
||||||
|
|
||||||
|
[api]
|
||||||
|
|
||||||
|
[providers]
|
||||||
|
[providers.file]
|
||||||
|
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router1]
|
||||||
|
Service = "keepalive"
|
||||||
|
Rule = "PathPrefix(`/keepalive`)"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.keepalive]
|
||||||
|
[http.services.keepalive.LoadBalancer]
|
||||||
|
passHostHeader = true
|
||||||
|
[[http.services.keepalive.LoadBalancer.Servers]]
|
||||||
|
URL = "{{ .KeepAliveServer }}"
|
||||||
|
Weight = 1
|
|
@ -49,6 +49,7 @@ func init() {
|
||||||
check.Suite(&HeadersSuite{})
|
check.Suite(&HeadersSuite{})
|
||||||
check.Suite(&HostResolverSuite{})
|
check.Suite(&HostResolverSuite{})
|
||||||
check.Suite(&HTTPSSuite{})
|
check.Suite(&HTTPSSuite{})
|
||||||
|
check.Suite(&KeepAliveSuite{})
|
||||||
check.Suite(&LogRotationSuite{})
|
check.Suite(&LogRotationSuite{})
|
||||||
check.Suite(&MarathonSuite{})
|
check.Suite(&MarathonSuite{})
|
||||||
check.Suite(&MarathonSuite15{})
|
check.Suite(&MarathonSuite15{})
|
||||||
|
|
105
integration/keepalive_test.go
Normal file
105
integration/keepalive_test.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/integration/try"
|
||||||
|
"github.com/go-check/check"
|
||||||
|
checker "github.com/vdemeester/shakers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeepAliveSuite struct {
|
||||||
|
BaseSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeepAliveConfig struct {
|
||||||
|
KeepAliveServer string
|
||||||
|
IdleConnTimeout string
|
||||||
|
}
|
||||||
|
|
||||||
|
type connStateChangeEvent struct {
|
||||||
|
key string
|
||||||
|
state http.ConnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime(c *check.C) {
|
||||||
|
idleTimeout := time.Duration(75) * time.Millisecond
|
||||||
|
|
||||||
|
connStateChanges := make(chan connStateChangeEvent)
|
||||||
|
noMoreRequests := make(chan bool, 1)
|
||||||
|
completed := make(chan bool, 1)
|
||||||
|
|
||||||
|
// keep track of HTTP connections and their status changes and measure their idle period
|
||||||
|
go func() {
|
||||||
|
connCount := 0
|
||||||
|
idlePeriodStartMap := make(map[string]time.Time)
|
||||||
|
idlePeriodLengthMap := make(map[string]time.Duration)
|
||||||
|
|
||||||
|
maxWaitDuration := 5 * time.Second
|
||||||
|
maxWaitTimeExceeded := time.After(maxWaitDuration)
|
||||||
|
moreRequestsExpected := true
|
||||||
|
|
||||||
|
// Ensure that all idle HTTP connections are closed before verification phase
|
||||||
|
for moreRequestsExpected || len(idlePeriodLengthMap) < connCount {
|
||||||
|
select {
|
||||||
|
case event := <-connStateChanges:
|
||||||
|
switch event.state {
|
||||||
|
case http.StateNew:
|
||||||
|
connCount++
|
||||||
|
case http.StateIdle:
|
||||||
|
idlePeriodStartMap[event.key] = time.Now()
|
||||||
|
case http.StateClosed:
|
||||||
|
idlePeriodLengthMap[event.key] = time.Since(idlePeriodStartMap[event.key])
|
||||||
|
}
|
||||||
|
case <-noMoreRequests:
|
||||||
|
moreRequestsExpected = false
|
||||||
|
case <-maxWaitTimeExceeded:
|
||||||
|
c.Logf("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
|
||||||
|
c.Fail()
|
||||||
|
close(completed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Check(connCount, checker.Equals, 1)
|
||||||
|
|
||||||
|
for _, idlePeriod := range idlePeriodLengthMap {
|
||||||
|
// Our method of measuring the actual idle period is not precise, so allow some sub-ms deviation
|
||||||
|
c.Check(math.Round(idlePeriod.Seconds()), checker.LessOrEqualThan, idleTimeout.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
close(completed)
|
||||||
|
}()
|
||||||
|
|
||||||
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}))
|
||||||
|
|
||||||
|
server.Config.ConnState = func(conn net.Conn, state http.ConnState) {
|
||||||
|
connStateChanges <- connStateChangeEvent{key: conn.RemoteAddr().String(), state: state}
|
||||||
|
}
|
||||||
|
server.Start()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
config := KeepAliveConfig{KeepAliveServer: server.URL, IdleConnTimeout: idleTimeout.String()}
|
||||||
|
file := s.adaptFile(c, "fixtures/timeout/keepalive.toml", config)
|
||||||
|
|
||||||
|
defer os.Remove(file)
|
||||||
|
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||||
|
defer display(c)
|
||||||
|
|
||||||
|
err := cmd.Start()
|
||||||
|
c.Check(err, checker.IsNil)
|
||||||
|
defer cmd.Process.Kill()
|
||||||
|
|
||||||
|
err = try.GetRequest("http://127.0.0.1:8000/keepalive", time.Duration(1)*time.Second, try.StatusCodeIs(200))
|
||||||
|
c.Check(err, checker.IsNil)
|
||||||
|
|
||||||
|
close(noMoreRequests)
|
||||||
|
<-completed
|
||||||
|
}
|
|
@ -109,11 +109,13 @@ func (a *RespondingTimeouts) SetDefaults() {
|
||||||
type ForwardingTimeouts struct {
|
type ForwardingTimeouts struct {
|
||||||
DialTimeout types.Duration `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." export:"true"`
|
DialTimeout types.Duration `description:"The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." export:"true"`
|
||||||
ResponseHeaderTimeout types.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." export:"true"`
|
ResponseHeaderTimeout types.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists." export:"true"`
|
||||||
|
IdleConnTimeout types.Duration `description:"The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaults sets the default values.
|
// SetDefaults sets the default values.
|
||||||
func (f *ForwardingTimeouts) SetDefaults() {
|
func (f *ForwardingTimeouts) SetDefaults() {
|
||||||
f.DialTimeout = types.Duration(30 * time.Second)
|
f.DialTimeout = types.Duration(30 * time.Second)
|
||||||
|
f.IdleConnTimeout = types.Duration(90 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LifeCycle contains configurations relevant to the lifecycle (such as the shutdown phase) of Traefik.
|
// LifeCycle contains configurations relevant to the lifecycle (such as the shutdown phase) of Traefik.
|
||||||
|
|
|
@ -63,6 +63,7 @@ func createHTTPTransport(transportConfiguration *static.ServersTransport) (*http
|
||||||
|
|
||||||
if transportConfiguration.ForwardingTimeouts != nil {
|
if transportConfiguration.ForwardingTimeouts != nil {
|
||||||
transport.ResponseHeaderTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
|
transport.ResponseHeaderTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.ResponseHeaderTimeout)
|
||||||
|
transport.IdleConnTimeout = time.Duration(transportConfiguration.ForwardingTimeouts.IdleConnTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
if transportConfiguration.InsecureSkipVerify {
|
if transportConfiguration.InsecureSkipVerify {
|
||||||
|
|
Loading…
Reference in a new issue