req, err := http.NewRequest(http.MethodPost, reqUrl, bytes.NewBuffer([]byte{}))
if err != nil {
	continue
}
client := &http.Client{Timeout: 15 * time.Second}
resp, err := client.Do(req)
if err != nil {
	continue
}

현재 대부분의 코드에서 http client 객체를 요청마다 생성하여 api 요청을 하고 있는데, golang에서는 기본적으로 connection pooling이 되어있음.

http.Client{}DefaultTransport 를 사용하는데, 이는 커넥션 풀 사이즈를 정의하고 관리하며, 싱글톤으로 관리되어 Client 객체를 매 request마다 생성하더라도 기본적으로 pool을 사용하고 있다.

Default Connection pooling

type Client struct {
	// Transport specifies the mechanism by which individual
	// HTTP requests are made.
	// **If nil, DefaultTransport is used**.
	Transport RoundTripper
	CheckRedirect func(req *Request, via []*Request) error
	Jar CookieJar
	Timeout time.Duration
}

Default Connection Pool 테스트

func startLoadTest() {
	for {
		startTime := time.Now()
		resp, err := http.Get("<http://localhost:8080/>")
		if err != nil {
			panic(fmt.Sprintf("Got error: %v %v", err)
		}
		ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		log.Printf("Finished GET request")
	}
}

func main() {
	for i := 0; i < 2; i++ {
			go startLoadTest()
	}
	time.Sleep(time.Second * 2400)
}
tcp6       0      0  ::1.63138              ::1.8080               ESTABLISHED
tcp6       0      0  ::1.63137              ::1.8080               ESTABLISHED

부하 상황

func main() {
	for i := 0; i < 6; i++ {
			go startLoadTest()
	}
	time.Sleep(time.Second * 2400)
}

최초 연결

tcp6       0      0  ::1.63049              ::1.8080               ESTABLISHED
tcp6       0      0  ::1.63047              ::1.8080               ESTABLISHED
tcp6       0      0  ::1.63048              ::1.8080               ESTABLISHED
tcp6       0      0  ::1.63046              ::1.8080               ESTABLISHED
**tcp6       0      0  ::1.63045              ::1.8080               ESTABLISHED
tcp6       0      0  ::1.63044              ::1.8080               ESTABLISHED**