f99aq8ove's blog

golang の net.http で http keepalive を一定時間で切る方法

tag: golang and http
22 March 2019

golang の net.http で http keepalive を一定時間で切る方法

net.http を使って http request を送ると、デフォルトでは http keepalive が有効になります。("Connection: keep-alive" は送っていないけど、HTTP/1.1 ではデフォルトで keep-alive になるという背景もある。)

ですが、DNS ラウンドロビンを用いたロードバランサーに向けて接続をする場合等に、一定期間毎にコネクションを切って接続しなおしたいという場合があったりします。(そうしないと、DNS から外したサーバーにもアクセスが来続けるというような事が起きます。)

net.http には、keepalive を一定期間で切断する機能は存在しません。

ですが、transport を定期的に差し替えてやれば、実現は可能です。 (他に良い方法があったらぜひ教えてほしいです。)

package main

import (
	"io/ioutil"
	"net"
	"net/http"
	"sync"
	"time"
)

type transportHolder struct {
	transport       *http.Transport
	sync            sync.Mutex
	refreshDeadline time.Time
}

var th = transportHolder{}

func (th *transportHolder) getTransport() *http.Transport {
	th.sync.Lock()
	defer th.sync.Unlock()

	if th.transport == nil || time.Now().After(th.refreshDeadline) {
		// https://golang.org/src/net/http/transport.go
		th.transport = &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,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		}

		// 10秒ごとに transport を差し替える設定する例
		th.refreshDeadline = time.Now().Add(10 * time.Second)
	}

	return th.transport
}

func main() {
	for {
		client := &http.Client{
			Transport: th.getTransport(),
		}

		access(client)

		time.Sleep(1 * time.Second)
	}
}

func access(client *http.Client) {
	resp, err := client.Get("http://localhost")
	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))
}

次の記事があります golang の net.http で http keepalive を一定時間で切る方法(transport を使い回す)

Related Posts