背景
结论
测试环境
问题分析
package main
import (
"net/http"
log "github.com/sirupsen/logrus"
"io/ioutil"
"fmt"
"bytes"
"sync"
)
func main() {
var wg sync.WaitGroup
var count int
var rw sync.RWMutex
TEST:
for i := 0; i < 30; i++ {
wg.Add(1)
go func () {
defer wg.Done()
tr := http.Transport{DisableKeepAlives: false}
client := &http.Client{Transport: &tr}
for i := 0; i < 1000; i++ {
f, err := ioutil.ReadFile("data")
if err != nil {
fmt.Println("read file err", err)
return
}
fmt.Println(len(f))
reader := bytes.NewReader(f)
rw.Lock()
count += 1
index := count
rw.Unlock()
resp, err := client.Post("http://0.0.0.0:8888", "application/x-www-form-urlencoded", reader)
if err != nil {
rw.RLock()
currentCount := count
rw.RUnlock()
log.Fatal(err, index, currentCount)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
log.Printf("data[%s]", string(data))
}
}()
}
wg.Wait()
goto TEST
}复制代码
golang server代码
package main
import (
graceful "github.com/cgCodeLife/graceful2"
"net/http"
log "github.com/sirupsen/logrus"
"io/ioutil"
"fmt"
"os"
"strconv"
)
func main() {
server := graceful.NewServer()
handler := http.HandlerFunc(handle)
server.Register("0.0.0.0:8888", handler)
err := server.Run()
if err != nil {
log.Fatal(err)
}
}
func handle(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
_, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("read body error[%s] pid[%d]", err, os.Getpid())
}
w.Write([]byte(strconv.Itoa(os.Getpid())))
}复制代码
实验部分截图
// shutdownPollInterval is how often we poll for quiescence
// during Server.Shutdown. This is lower during tests, to
// speed up tests.
// Ideally we could find a solution that doesn't involve polling,
// but which also doesn't have a high runtime cost (and doesn't
// involve any contentious mutexes), but that is left as an
// exercise for the reader.
var shutdownPollInterval = 500 * time.Millisecond
// Shutdown gracefully shuts down the server without interrupting any
// active connections. Shutdown works by first closing all open
// listeners, then closing all idle connections, and then waiting
// indefinitely for connections to return to idle and then shut down.
// If the provided context expires before the shutdown is complete,
// Shutdown returns the context's error, otherwise it returns any
// error returned from closing the Server's underlying Listener(s).
//
// When Shutdown is called, Serve, ListenAndServe, and
// ListenAndServeTLS immediately return ErrServerClosed. Make sure the
// program doesn't exit and waits instead for Shutdown to return.
//
// Shutdown does not attempt to close nor wait for hijacked
// connections such as WebSockets. The caller of Shutdown should
// separately notify such long-lived connections of shutdown and wait
// for them to close, if desired. See RegisterOnShutdown for a way to
// register shutdown notification functions.
func (srv *Server) Shutdown(ctx context.Context) error {
atomic.AddInt32(&srv.inShutdown, 1)
defer atomic.AddInt32(&srv.inShutdown, -1)
srv.mu.Lock()
lnerr := srv.closeListenersLocked()
srv.closeDoneChanLocked()
for _, f := range srv.onShutdown {
go f()
}
srv.mu.Unlock()
ticker := time.NewTicker(shutdownPollInterval)
defer ticker.Stop()
for {
if srv.closeIdleConns() {
return lnerr
}
select {
case <-ctx.Done():
return ctx.Err()
case <-ticker.C:
}
}
}复制代码