1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-12 01:22:21 +02:00
Removed graceful shutdown, fixed #797

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2017-01-13 18:40:27 -08:00
parent 8526358e8a
commit 9797cf4b9c
9 changed files with 140 additions and 105 deletions

118
echo.go
View File

@ -43,6 +43,7 @@ import (
"fmt"
"io"
slog "log"
"net"
"net/http"
"path"
"reflect"
@ -51,13 +52,16 @@ import (
"time"
"github.com/labstack/gommon/log"
"github.com/tylerb/graceful"
"golang.org/x/crypto/acme/autocert"
)
type (
// Echo is the top-level framework instance.
Echo struct {
Server *http.Server
TLSServer *http.Server
Listener net.Listener
TLSListener net.Listener
DisableHTTP2 bool
Debug bool
HTTPErrorHandler HTTPErrorHandler
@ -65,13 +69,8 @@ type (
Validator Validator
Renderer Renderer
AutoTLSManager autocert.Manager
ReadTimeout time.Duration
WriteTimeout time.Duration
ShutdownTimeout time.Duration
Logger Logger
stdLogger *slog.Logger
server *graceful.Server
tlsServer *graceful.Server
premiddleware []MiddlewareFunc
middleware []MiddlewareFunc
maxParam *int
@ -239,13 +238,16 @@ var (
// New creates an instance of Echo.
func New() (e *Echo) {
e = &Echo{
Server: new(http.Server),
TLSServer: new(http.Server),
AutoTLSManager: autocert.Manager{
Prompt: autocert.AcceptTOS,
},
ShutdownTimeout: 15 * time.Second,
Logger: log.New("echo"),
maxParam: new(int),
Logger: log.New("echo"),
maxParam: new(int),
}
e.Server.Handler = e
e.TLSServer.Handler = e
e.HTTPErrorHandler = e.DefaultHTTPErrorHandler
e.Binder = &DefaultBinder{}
e.Logger.SetLevel(log.OFF)
@ -518,76 +520,64 @@ func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
e.pool.Put(c)
}
// Start starts the HTTP server.
// Start starts an HTTP server.
func (e *Echo) Start(address string) error {
return e.StartServer(&http.Server{
Addr: address,
ReadTimeout: e.ReadTimeout,
WriteTimeout: e.WriteTimeout,
ErrorLog: e.stdLogger,
})
e.Server.Addr = address
return e.StartServer(e.Server)
}
// StartTLS starts the HTTPS server.
// StartTLS starts an HTTPS server.
func (e *Echo) StartTLS(address string, certFile, keyFile string) (err error) {
if certFile == "" || keyFile == "" {
return errors.New("invalid tls configuration")
}
config := new(tls.Config)
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
s := e.TLSServer
s.TLSConfig = new(tls.Config)
s.TLSConfig.Certificates = make([]tls.Certificate, 1)
s.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return
}
return e.startTLS(address, config)
return e.startTLS(address)
}
// StartAutoTLS starts the HTTPS server using certificates automatically from https://letsencrypt.org.
// StartAutoTLS starts an HTTPS server using certificates automatically from https://letsencrypt.org.
func (e *Echo) StartAutoTLS(address string) error {
config := new(tls.Config)
config.GetCertificate = e.AutoTLSManager.GetCertificate
return e.startTLS(address, config)
s := e.TLSServer
s.TLSConfig = new(tls.Config)
s.TLSConfig.GetCertificate = e.AutoTLSManager.GetCertificate
return e.startTLS(address)
}
func (e *Echo) startTLS(address string, config *tls.Config) error {
func (e *Echo) startTLS(address string) error {
s := e.TLSServer
s.Addr = address
if !e.DisableHTTP2 {
config.NextProtos = append(config.NextProtos, "h2")
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2")
}
return e.StartServer(&http.Server{
Addr: address,
ReadTimeout: e.ReadTimeout,
WriteTimeout: e.WriteTimeout,
TLSConfig: config,
ErrorLog: e.stdLogger,
})
return e.StartServer(e.TLSServer)
}
// StartServer runs a custom HTTP server.
// StartServer starts a custom http server.
func (e *Echo) StartServer(s *http.Server) error {
s.Handler = e
gs := &graceful.Server{
Server: s,
Timeout: e.ShutdownTimeout,
Logger: e.stdLogger,
s.ErrorLog = e.stdLogger
l, err := net.Listen("tcp", s.Addr)
if err != nil {
return err
}
if s.TLSConfig == nil {
e.server = gs
e.Logger.Printf("http server started on %s", s.Addr)
return gs.ListenAndServe()
if e.Listener == nil {
e.Listener = tcpKeepAliveListener{l.(*net.TCPListener)}
}
e.Logger.Printf("http server started on %s", e.Server.Addr)
return s.Serve(e.Listener)
}
e.tlsServer = gs
e.Logger.Printf(" ⇛ https server started on %s", s.Addr)
return gs.ListenAndServeTLSConfig(s.TLSConfig)
}
// Shutdown gracefully shutdown the HTTP server with timeout.
func (e *Echo) Shutdown(timeout time.Duration) {
e.server.Stop(timeout)
}
// ShutdownTLS gracefully shutdown the TLS server with timeout.
func (e *Echo) ShutdownTLS(timeout time.Duration) {
e.tlsServer.Stop(timeout)
if e.TLSListener == nil {
e.TLSListener = tls.NewListener(tcpKeepAliveListener{l.(*net.TCPListener)}, s.TLSConfig)
}
e.Logger.Printf(" ⇛ https server started on %s", e.Server.Addr)
return s.Serve(e.TLSListener)
}
// NewHTTPError creates a new HTTPError instance.
@ -632,3 +622,21 @@ func handlerName(h HandlerFunc) string {
}
return t.String()
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

View File

@ -5,7 +5,6 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"
"reflect"
"strings"
@ -392,8 +391,6 @@ func TestEchoStart(t *testing.T) {
go func() {
assert.NoError(t, e.Start(":0"))
}()
time.Sleep(200 * time.Millisecond)
e.Shutdown(1 * time.Second)
}
func TestEchoStartTLS(t *testing.T) {
@ -401,8 +398,6 @@ func TestEchoStartTLS(t *testing.T) {
go func() {
assert.NoError(t, e.StartTLS(":0", "_fixture/certs/cert.pem", "_fixture/certs/key.pem"))
}()
time.Sleep(200 * time.Millisecond)
e.ShutdownTLS(1 * time.Second)
}
func testMethod(t *testing.T, method, path string, e *Echo) {

View File

@ -0,0 +1,20 @@
package main
import (
"net/http"
"github.com/facebookgo/grace/gracehttp"
"github.com/labstack/echo"
)
func main() {
// Setup
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Six sick bricks tick")
})
e.Server.Addr = ":1323"
// Serve it like a boss
e.Logger.Fatal(gracehttp.Serve(e.Server))
}

View File

@ -0,0 +1,21 @@
package main
import (
"net/http"
"time"
"github.com/labstack/echo"
"github.com/tylerb/graceful"
)
func main() {
// Setup
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Sue sews rose on slow joe crows nose")
})
e.Server.Addr = ":1323"
// Serve it like a boss
graceful.ListenAndServe(e.Server, 5*time.Second)
}

View File

@ -1,20 +0,0 @@
package main
import (
"time"
"github.com/labstack/echo"
)
func main() {
e := echo.New()
// Setting up the termination timeout to 30 seconds.
e.ShutdownTimeout = 30 * time.Second
e.GET("/", func(ctx echo.Context) error {
return ctx.String(200, "OK")
})
e.Logger.Fatal(e.Start(":1323"))
}

33
glide.lock generated
View File

@ -1,10 +1,21 @@
hash: 23f3636cfa67541062f212b8285762ca142c6bb55889f243bf0574ba00e0ff92
updated: 2016-12-25T21:41:41.108263974-08:00
hash: 4a4f41416395516f7eefabd29ee5c7b065a411dec7446f6d6853d4cc28c4c644
updated: 2017-01-13T18:25:37.646516032-08:00
imports:
- name: github.com/daaku/go.zipexe
version: a5fe2436ffcb3236e175e5149162b41cd28bd27d
- name: github.com/dgrijalva/jwt-go
version: 9ed569b5d1ac936e6494082958d63a6aa4fff99a
version: a601269ab70c205d26370c16f7c81e9017c14e04
- name: github.com/facebookgo/clock
version: 600d898af40aa09a7a93ecb9265d87b0504b6f03
- name: github.com/facebookgo/grace
version: 5729e484473f52048578af1b80d0008c7024089b
subpackages:
- gracehttp
- gracenet
- name: github.com/facebookgo/httpdown
version: a3b1354551a26449fbe05f5d855937f6e7acbd71
- name: github.com/facebookgo/stats
version: 1b76add642e42c6ffba7211ad7b3939ce654526e
- name: github.com/GeertJohan/go.rice
version: 9fdfd46f9806a9228aae341d65ab75c5235c383c
subpackages:
@ -14,11 +25,11 @@ imports:
subpackages:
- proto
- name: github.com/gorilla/websocket
version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13
version: 17634340a83afe0cab595e40fbc63f6ffa1d8915
- name: github.com/kardianos/osext
version: c2c54e542fb797ad986b31721e1baedf214ca413
- name: github.com/labstack/gommon
version: b2765095a572012ea4828c9596fefd824c2aee72
version: f72d3c883f8ea180da8f085dd320804c41332ad1
subpackages:
- bytes
- color
@ -29,16 +40,18 @@ imports:
- name: github.com/mattn/go-isatty
version: 30a891c33c7cde7b02a981314b4228ec99380cca
- name: github.com/tylerb/graceful
version: 4df1190835320af7076dfcf27b3d071fd3612caf
version: d37e108c89765a8e307f15b8fb2ecd10575da6bb
- name: github.com/valyala/bytebufferpool
version: e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7
- name: github.com/valyala/fasttemplate
version: 3b874956e03f1636d171bda64b130f9135f42cff
version: d090d65668a286d9a180d43a19dfdc5dcad8fe88
- name: golang.org/x/crypto
version: f6b343c37ca80bfa8ea539da67a0b621f84fab1d
version: 2f8be38b9a7533b8763d48273737ff6e90428a96
subpackages:
- acme
- acme/autocert
- name: golang.org/x/net
version: 45e771701b814666a7eb299e6c7a57d0b1799e91
version: c427ad74c6d7a814201695e9ffde0c5d400a7674
subpackages:
- context
- context/ctxhttp
@ -48,7 +61,7 @@ imports:
subpackages:
- unix
- name: google.golang.org/appengine
version: 08a149cfaee099e6ce4be01c0113a78c85ee1dee
version: 9e2ad0873f358c54296ccdc5116b0652c98226d1
subpackages:
- internal
- internal/app_identity

View File

@ -10,7 +10,6 @@ import:
- log
- random
- package: github.com/mattn/go-isatty
- package: github.com/tylerb/graceful
- package: github.com/valyala/fasttemplate
- package: golang.org/x/crypto
subpackages:
@ -22,6 +21,7 @@ import:
- package: gopkg.in/mgo.v2
subpackages:
- bson
- package: github.com/facebookgo/grace
testImport:
- package: github.com/stretchr/testify
subpackages:

View File

@ -7,21 +7,25 @@ description = "Graceful shutdown example for Echo"
weight = 13
+++
Echo now ships with graceful server termination inside it, to accomplish it Echo
uses `github.com/tylerb/graceful` library. By Default echo uses 15 seconds as shutdown
timeout, giving 15 secs to open connections at the time the server starts to shut-down.
In order to change this default 15 seconds you could change the `ShutdownTimeout`
property of your Echo instance as needed by doing something like:
## Using [grace](https://github.com/facebookgo/grace)
`server.go`
{{< embed "graceful-shutdown/server.go" >}}
{{< embed "graceful-shutdown/grace/server.go" >}}
## Using [graceful](https://github.com/tylerb/graceful)
`server.go`
{{< embed "graceful-shutdown/graceful/server.go" >}}
## Source Code
- [graceful]({{< source "graceful-shutdown/graceful" >}})
- [grace]({{< source "graceful-shutdown/grace" >}})
## Maintainers
- [mertenvg](https://github.com/mertenvg)
- [apaganobeleno](https://github.com/apaganobeleno)
- [vishr](https://github.com/vishr)

View File

@ -77,20 +77,14 @@ e.Logger.Fatal(s.ListenAndServe())
## Read Timeout
`Echo#ReadTimeout` can be used to set the maximum duration before timing out read
`Echo#*Server#ReadTimeout` can be used to set the maximum duration before timing out read
of the request.
## Write Timeout
`Echo#WriteTimeout` can be used to set the maximum duration before timing out write
`Echo#*Server#WriteTimeout` can be used to set the maximum duration before timing out write
of the response.
## Shutdown Timeout
`Echo#ShutdownTimeout` can be used to set the maximum duration to wait until killing
active requests and stopping the server. If timeout is 0, the server never times
out. It waits for all active requests to finish.
## Validator
`Echo#Validator` can be used to register a validator for performing data validation