gf/net/ghttp/ghttp_server_graceful.go

272 lines
7.5 KiB
Go
Raw Normal View History

2021-01-17 21:46:25 +08:00
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp
import (
2019-06-19 09:06:52 +08:00
"context"
"crypto/tls"
"fmt"
2021-11-13 23:23:55 +08:00
"log"
"net"
"net/http"
"os"
"strconv"
"sync"
2021-11-13 23:23:55 +08:00
"github.com/gogf/gf/v2/errors/gcode"
2021-10-11 21:41:56 +08:00
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gres"
"github.com/gogf/gf/v2/text/gstr"
)
2020-05-07 23:05:33 +08:00
// gracefulServer wraps the net/http.Server with graceful reload/restart feature.
type gracefulServer struct {
server *Server // Belonged server.
2022-03-19 17:58:21 +08:00
fd uintptr // File descriptor for passing to the child process when graceful reload.
2020-05-07 23:05:33 +08:00
address string // Listening address like:":80", ":8080".
httpServer *http.Server // Underlying http.Server.
rawListener net.Listener // Underlying net.Listener.
rawLnMu sync.RWMutex // Concurrent safety mutex for `rawListener`.
2020-05-07 23:05:33 +08:00
listener net.Listener // Wrapped net.Listener.
isHttps bool // Is HTTPS.
status int // Status of current server.
}
2022-03-19 17:58:21 +08:00
// newGracefulServer creates and returns a graceful http server with a given address.
// The optional parameter `fd` specifies the file descriptor which is passed from parent server.
func (s *Server) newGracefulServer(address string, fd ...int) *gracefulServer {
// Change port to address like: 80 -> :80
if gstr.IsNumeric(address) {
address = ":" + address
}
2019-06-19 09:06:52 +08:00
gs := &gracefulServer{
server: s,
address: address,
httpServer: s.newHttpServer(address),
2019-06-19 09:06:52 +08:00
}
if len(fd) > 0 && fd[0] > 0 {
gs.fd = uintptr(fd[0])
}
if s.config.Listeners != nil {
addrArray := gstr.SplitAndTrim(address, ":")
addrPort, err := strconv.Atoi(addrArray[len(addrArray)-1])
if err == nil {
for _, v := range s.config.Listeners {
if listenerPort := (v.Addr().(*net.TCPAddr)).Port; listenerPort == addrPort {
gs.rawListener = v
break
}
}
}
}
2019-06-19 09:06:52 +08:00
return gs
}
2022-03-19 17:58:21 +08:00
// newHttpServer creates and returns an underlying http.Server with a given address.
func (s *Server) newHttpServer(address string) *http.Server {
2019-06-19 09:06:52 +08:00
server := &http.Server{
Addr: address,
Handler: http.HandlerFunc(s.config.Handler),
2019-06-19 09:06:52 +08:00
ReadTimeout: s.config.ReadTimeout,
WriteTimeout: s.config.WriteTimeout,
IdleTimeout: s.config.IdleTimeout,
MaxHeaderBytes: s.config.MaxHeaderBytes,
ErrorLog: log.New(&errorLogger{logger: s.config.Logger}, "", 0),
2019-06-19 09:06:52 +08:00
}
server.SetKeepAlivesEnabled(s.config.KeepAlive)
return server
}
2022-03-19 17:58:21 +08:00
// Fd retrieves and returns the file descriptor of the current server.
// It is available ony in *nix like operating systems like linux, unix, darwin.
2018-05-13 22:00:10 +08:00
func (s *gracefulServer) Fd() uintptr {
if ln := s.getRawListener(); ln != nil {
file, err := ln.(*net.TCPListener).File()
2019-06-19 09:06:52 +08:00
if err == nil {
return file.Fd()
}
}
return 0
2018-05-13 22:00:10 +08:00
}
2020-05-07 23:05:33 +08:00
// setFd sets the file descriptor for current server.
2018-05-10 19:16:41 +08:00
func (s *gracefulServer) setFd(fd int) {
2019-06-19 09:06:52 +08:00
s.fd = uintptr(fd)
2018-05-10 19:16:41 +08:00
}
// CreateListener creates listener on configured address.
func (s *gracefulServer) CreateListener() error {
ln, err := s.getNetListener()
if err != nil {
return err
}
s.listener = ln
s.setRawListener(ln)
return nil
}
// CreateListenerTLS creates listener on configured address with HTTPS.
// The parameter `certFile` and `keyFile` specify the necessary certification and key files for HTTPS.
// The optional parameter `tlsConfig` specifies the custom TLS configuration.
func (s *gracefulServer) CreateListenerTLS(certFile, keyFile string, tlsConfig ...*tls.Config) error {
var config *tls.Config
if len(tlsConfig) > 0 && tlsConfig[0] != nil {
2019-06-19 09:06:52 +08:00
config = tlsConfig[0]
} else if s.httpServer.TLSConfig != nil {
config = s.httpServer.TLSConfig
} else {
config = &tls.Config{}
2019-06-19 09:06:52 +08:00
}
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
}
var err error
2019-06-19 09:06:52 +08:00
if len(config.Certificates) == 0 {
config.Certificates = make([]tls.Certificate, 1)
if gres.Contains(certFile) {
config.Certificates[0], err = tls.X509KeyPair(
gres.GetContent(certFile),
gres.GetContent(keyFile),
)
} else {
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
}
2019-06-19 09:06:52 +08:00
}
if err != nil {
return gerror.Wrapf(err, `open certFile "%s" and keyFile "%s" failed`, certFile, keyFile)
2019-06-19 09:06:52 +08:00
}
ln, err := s.getNetListener()
2019-06-19 09:06:52 +08:00
if err != nil {
return err
}
s.listener = tls.NewListener(ln, config)
s.setRawListener(ln)
return nil
}
// Serve starts the serving with blocking way.
func (s *gracefulServer) Serve(ctx context.Context) error {
if s.rawListener == nil {
return gerror.NewCode(gcode.CodeInvalidOperation, `call CreateListener/CreateListenerTLS before Serve`)
2019-06-19 09:06:52 +08:00
}
2019-06-19 09:06:52 +08:00
action := "started"
if s.fd != 0 {
action = "reloaded"
}
s.server.Logger().Infof(
2021-09-27 21:27:24 +08:00
ctx,
`pid[%d]: %s server %s listening on [%s]`,
gproc.Pid(), s.getProto(), action, s.GetListenedAddress(),
)
2020-12-14 13:26:48 +08:00
s.status = ServerStatusRunning
2019-06-19 09:06:52 +08:00
err := s.httpServer.Serve(s.listener)
2020-12-14 13:26:48 +08:00
s.status = ServerStatusStopped
2019-06-19 09:06:52 +08:00
return err
}
// GetListenedAddress retrieves and returns the address string which are listened by current server.
func (s *gracefulServer) GetListenedAddress() string {
if !gstr.Contains(s.address, FreePortAddress) {
return s.address
}
var (
address = s.address
listenedPort = s.GetListenedPort()
)
address = gstr.Replace(address, FreePortAddress, fmt.Sprintf(`:%d`, listenedPort))
return address
}
// GetListenedPort retrieves and returns one port which is listened to by current server.
func (s *gracefulServer) GetListenedPort() int {
if ln := s.getRawListener(); ln != nil {
return ln.Addr().(*net.TCPAddr).Port
}
return -1
}
// getProto retrieves and returns the proto string of current server.
func (s *gracefulServer) getProto() string {
proto := "http"
if s.isHttps {
proto = "https"
}
return proto
}
2020-05-07 23:05:33 +08:00
// getNetListener retrieves and returns the wrapped net.Listener.
func (s *gracefulServer) getNetListener() (net.Listener, error) {
2022-03-18 20:54:32 +08:00
if s.rawListener != nil {
return s.rawListener, nil
}
var (
ln net.Listener
err error
)
2019-06-19 09:06:52 +08:00
if s.fd > 0 {
f := os.NewFile(s.fd, "")
ln, err = net.FileListener(f)
if err != nil {
err = gerror.Wrap(err, "net.FileListener failed")
2019-06-19 09:06:52 +08:00
return nil, err
}
} else {
ln, err = net.Listen("tcp", s.httpServer.Addr)
2019-06-19 09:06:52 +08:00
if err != nil {
err = gerror.Wrapf(err, `net.Listen address "%s" failed`, s.httpServer.Addr)
2019-06-19 09:06:52 +08:00
}
}
return ln, err
}
2020-05-07 23:05:33 +08:00
// shutdown shuts down the server gracefully.
2021-09-27 21:27:24 +08:00
func (s *gracefulServer) shutdown(ctx context.Context) {
2020-12-14 13:26:48 +08:00
if s.status == ServerStatusStopped {
2019-06-19 09:06:52 +08:00
return
}
timeoutCtx, cancelFunc := context.WithTimeout(ctx, gracefulShutdownTimeout)
defer cancelFunc()
if err := s.httpServer.Shutdown(timeoutCtx); err != nil {
s.server.Logger().Errorf(
2021-09-27 21:27:24 +08:00
ctx,
"%d: %s server [%s] shutdown error: %v",
gproc.Pid(), s.getProto(), s.address, err,
)
2019-06-19 09:06:52 +08:00
}
}
// setRawListener sets `rawListener` with given net.Listener.
func (s *gracefulServer) setRawListener(ln net.Listener) {
s.rawLnMu.Lock()
defer s.rawLnMu.Unlock()
s.rawListener = ln
}
// setRawListener returns the `rawListener` of current server.
func (s *gracefulServer) getRawListener() net.Listener {
s.rawLnMu.RLock()
defer s.rawLnMu.RUnlock()
return s.rawListener
}
2020-05-07 23:05:33 +08:00
// close shuts down the server forcibly.
2021-09-27 21:27:24 +08:00
func (s *gracefulServer) close(ctx context.Context) {
2020-12-14 13:26:48 +08:00
if s.status == ServerStatusStopped {
2019-06-19 09:06:52 +08:00
return
}
if err := s.httpServer.Close(); err != nil {
s.server.Logger().Errorf(
2021-09-27 21:27:24 +08:00
ctx,
"%d: %s server [%s] closed error: %v",
gproc.Pid(), s.getProto(), s.address, err,
)
2019-06-19 09:06:52 +08:00
}
}