簡體   English   中英

Golang:http2 客戶端在啟用 idleConn 時立即通過代理關閉連接

[英]Golang: http2 client immediately close connections via proxy while idleConn was enabled

0x00 TL;DR

我正在使用 Go 來實現 http 客戶端和 squid 作為轉發代理向遠程服務器發送請求。 通過代理使用 http/1.1 或 http/1.1, http2 不使用代理時一切順利,但是,通過代理使用 http2 客戶端時,大多數連接立即關閉,只保留一兩個。

不確定這是我的錯誤代碼還是什么。 在 http 傳輸上啟用了 idleConn 配置。 提前致謝。

0x01 環境

代碼

package main

import (
    "context"
    "crypto/tls"
    "fmt"
    "io"
    "io/ioutil"
    "net"
    "net/http"
    "net/url"
    "sync"
    "time"
)

const (
    proxyURL = "http://{{proxy-ip}}:3128"
)

func main() {
    wg := &sync.WaitGroup{}

    wg.Add(1)
    go func() {
        doSendRequests()
        wg.Done()
    }()

    wg.Wait()
}

func doSendRequests() {
    //client := newHTTPClient(nil)
    client := newHTTPClient(proxy)

    round := 0
    for {
        round += 1
        for i := 0; i < 5; i++ {
            go func(r int) {
                start := time.Now()
                var err error
                defer func() {
                    duration := time.Since(start)
                    fmt.Printf("Round: %d, A: %v, duration: %v\n", r, err, duration)
                }()
                req := newRequest("https://www.google.com")
                resp, err := client.Do(req)
                if err == nil {
                    io.Copy(ioutil.Discard, resp.Body)
                    resp.Body.Close()
                } else {
                    fmt.Printf("A: %v\n", err)
                }
            }(round)
        }

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

type TLSDialer struct {
    net.Dialer
}

var (
    config = &tls.Config{InsecureSkipVerify: true}
)

func (dialer *TLSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
    return tls.DialWithDialer(&dialer.Dialer, network, addr, config)
}

var (
    DefaultDialer = net.Dialer{
        Timeout:   30 * time.Second,
        KeepAlive: 30 * time.Second,
        DualStack: true,
    }

    DefaultTLSDialer = TLSDialer{DefaultDialer}
)

func proxy(req *http.Request) (*url.URL, error) {
    uri, err := url.Parse(proxyURL)
    if err != nil {
        return nil, nil
    }
    return uri, nil
}

func newHTTPClient(proxy func(*http.Request) (*url.URL, error)) *http.Client {
    t := &http.Transport{
        DialContext:           (&DefaultDialer).DialContext,
        DisableKeepAlives:     false,
        DisableCompression:    false,
        ForceAttemptHTTP2:     true,
        MaxIdleConns:          5000,
        MaxIdleConnsPerHost:   1000,
        IdleConnTimeout:       0 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }

    if proxy == nil {
        t.DialTLSContext = (&DefaultTLSDialer).DialContext
    } else {
        t.Proxy = proxy
        t.TLSClientConfig = &tls.Config{
            InsecureSkipVerify: true,
        }
    }

    return &http.Client{Transport: t}
}

func newRequest(uri string) *http.Request {
    ctx, _ := context.WithTimeout(context.Background(), 60*time.Second)
    req, _ := http.NewRequestWithContext(ctx, "GET", uri, nil)

    return req
}

環境

go 1.15.3

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ec2-user/.cache/go-build"
GOENV="/home/ec2-user/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/ec2-user/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/ec2-user/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build200120630=/tmp/go-build -gno-record-gcc-switches"

unname -a

Linux ip-10-53-74-64.ec2.internal 4.14.243-185.433.amzn2.x86_64 #1 SMP Mon Aug 9 05:55:52 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

魷魚 4.15

0x02 現象

正如我在摘要中提到的,當我通過代理使用 http/1.1 或 http/1.1、http2 沒有代理時,一切都很順利。 但是,當通過代理使用 http2 客戶端時,將創建與請求一樣多的連接,然后關閉其他連接,只留下一兩個連接。 我使用 Wireshark 捕獲包,發現連接最初是從客戶端關閉的,因此代理端可能無關緊要,對此不確定。

沒有代理

// www.google.com
Every 0.5s: netstat -anop | grep '172.217'

Sat Nov 27 04:26:22 2021

tcp        0      0 10.53.74.64:44106       172.217.0.36:443        ESTABLISHED 3110/main            keepalive (25.35/0/0)
tcp        0      0 10.53.74.64:44378       172.217.0.36:443        ESTABLISHED 3110/main            keepalive (25.44/0/0)
tcp        0      0 10.53.74.64:44084       172.217.0.36:443        ESTABLISHED 3110/main            keepalive (25.60/0/0)
tcp        0      0 10.53.74.64:44374       172.217.0.36:443        ESTABLISHED 3110/main            keepalive (25.43/0/0)
tcp        0      0 10.53.74.64:44220       172.217.0.36:443        ESTABLISHED 3110/main            keepalive (25.52/0/0)

使用代理和 http/1.1

Every 0.5s: netstat -anop | grep ":3128"

Sun Dec  5 13:09:44 2021

tcp        0      0 10.53.74.64:58370       {{proxy-ip}}:3128     ESTABLISHED 26322/main           keepalive (23.70/0/0)
tcp        0      0 10.53.74.64:58366       {{proxy-ip}}:3128     ESTABLISHED 26322/main           keepalive (23.70/0/0)
tcp        0      0 10.53.74.64:58374       {{proxy-ip}}:3128     ESTABLISHED 26322/main           keepalive (23.70/0/0)
tcp        0      0 10.53.74.64:58368       {{proxy-ip}}:3128     ESTABLISHED 26322/main           keepalive (23.69/0/0)
tcp        0      0 10.53.74.64:58372       {{proxy-ip}}:3128     ESTABLISHED 26322/main           keepalive (23.70/0/0)

使用代理和 http2

如下所示,其他連接在 ESTABLISHED 后不久關閉,並且在 http 傳輸上啟用了 idleConn 配置。

Sun Dec  5 13:19:35 UTC 2021
tcp        0    284 10.53.74.64:58414       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.20/0/0)
tcp        0    284 10.53.74.64:58416       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.19/0/0)
tcp        0    284 10.53.74.64:58410       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.19/0/0)
tcp        0    284 10.53.74.64:58412       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.19/0/0)
tcp        0    284 10.53.74.64:58418       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.19/0/0)

Sun Dec  5 13:19:35 UTC 2021
tcp        0     89 10.53.74.64:58414       {{proxy-ip}}:3128     FIN_WAIT1   -                    on (0.14/0/0)
tcp        0     89 10.53.74.64:58416       {{proxy-ip}}:3128     FIN_WAIT1   -                    on (0.14/0/0)
tcp        0    365 10.53.74.64:58410       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.14/0/0)
tcp        0     89 10.53.74.64:58412       {{proxy-ip}}:3128     FIN_WAIT1   -                    on (0.14/0/0)
tcp        0     89 10.53.74.64:58418       {{proxy-ip}}:3128     FIN_WAIT1   -                    on (0.14/0/0)

Sun Dec  5 13:19:35 UTC 2021
tcp        0      0 10.53.74.64:58414       {{proxy-ip}}:3128     TIME_WAIT   -                    timewait (59.99/0/0)
tcp        0      0 10.53.74.64:58416       {{proxy-ip}}:3128     TIME_WAIT   -                    timewait (59.99/0/0)
tcp        0     31 10.53.74.64:58410       {{proxy-ip}}:3128     ESTABLISHED 34301/main           on (0.26/0/0)
tcp        0      0 10.53.74.64:58412       {{proxy-ip}}:3128     TIME_WAIT   -                    timewait (59.99/0/0)
tcp        0      0 10.53.74.64:58418       {{proxy-ip}}:3128     TIME_WAIT   -                    timewait (59.99/0/0)

這可以被最新的 golang 1.17.4(現在:2021-12-05)復制。 有沒有人可以提供一種方法來弄清楚為什么會發生這種情況? 謝謝。

EDIT1:所有請求都沒有錯誤地完成。

EDIT2:我在 Golang repo 上打開了一個問題,以防有人感興趣: https://github.com/golang/go/issues/50000

這是一個熟悉的代理端口號。

最后,我弄清楚了,一切都按預期工作,所以它與 net/http2 無關。

更多詳情請參考本期: https://github.com/golang/go/issues/50000

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM