golang の net.http で http keepalive を一定時間で切る方法(transport を使い回す)
golang の net.http で http keepalive を一定時間で切る方法 で Transport を一定期間で作り変える実装を書いてみましたが、メモリリークする可能性があるというという話もあり、一定期間で2つの transport を入れ替えて使うようにしてみました。
package main
import (
"fmt"
"io/ioutil"
"net"
"net/http"
"runtime"
"sync"
"time"
)
type clientHolder struct {
client *http.Client
clientPool chan *http.Client
sync sync.Mutex
refreshDeadline time.Time
}
var ch = clientHolder{
clientPool: make(chan *http.Client, 1),
}
func (ch *clientHolder) getClient() *http.Client {
ch.sync.Lock()
defer ch.sync.Unlock()
if ch.client == nil || time.Now().After(ch.refreshDeadline) {
var newClient *http.Client
if len(ch.clientPool) > 0 {
newClient = <-ch.clientPool
} else {
// https://golang.org/src/net/http/transport.go
newTransport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
//IdleConnTimeout: 90 * time.Second,
IdleConnTimeout: 9 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
newClient = &http.Client{
Transport: newTransport,
}
println("new client")
}
if ch.client != nil {
ch.clientPool <- ch.client
}
ch.client = newClient
ch.refreshDeadline = time.Now().Add(10 * time.Second)
}
return ch.client
}
func main() {
go func() {
ticker := time.NewTicker(1 * time.Second)
for range ticker.C {
printMemStats()
}
}()
for {
access(ch.getClient())
time.Sleep(10 * time.Millisecond)
}
}
func access(client *http.Client) {
resp, err := client.Get("http://10.0.1.1:40080/test")
if err != nil {
panic(err)
}
defer resp.Body.Close()
// println(resp.Status)
/*body*/
_, err = ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
// println(string(body))
}
func printMemStats() {
runtime.GC()
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc: %v bytes, TotalAlloc: %v bytes, Sys: %v bytes, NumGC: %v\n", m.Alloc, m.TotalAlloc, m.Sys, m.NumGC)
}