Время HTTP ответа в Go



В моей программе Go я сделал несколько HTTP-запросов, и мне нужно время Время ответа (а не время запроса).



Вот мой текущий код (время запроса времени):



func Get() int {
start := time.Now()
result, err := http.Get("http://www.google.com")
if err != nil {
log.Fatal(err)
}
defer result.Body.Close()
elapsed := time.Since(start).Seconds()
log.Println(elapsed)

return result.StatusCode
}


На самом деле, этот код покажет что-то о времени запроса 5s, включая разрешение DNS и другие вещи... Если я выполняю тот же тест с помощью такого инструмента, как Apache JMeter, время составляет всего около 100 мс (что является реальным временем отклика сервера, не заботясь о запросе время).



На самом деле я хочу вычислить реальное время отклика сервера. Как я могу вычислить это в Go ?

594   3  

3 ответов:

Вы можете измерить его путем открытия TCP-соединение и "говорящие" необработанные HTTP - (Википедии, rfc7230, rfc7231, rfc7232, rfc7233, rfc7234, rfc7235). Вы устанавливаете соединение, отправляете запрос и запускаете таймер здесь. И ждать ответа. Таким образом, вы исключите разрешение DNS и время, необходимое для установления соединения.

Так как вы не имеете представления, начинает ли сервер отправлять данные обратно немедленно или нет. только когда все будет готово, и у вас нет информации о сетевых задержках, это будет не точная, а просто оценка.

Я бы измерил в 2 точках: когда первый байт может быть прочитан и когда все прочитано:

conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
    panic(err)
}
defer conn.Close()
conn.Write([]byte("GET / HTTP/1.0\r\n\r\n"))

start := time.Now()
oneByte := make([]byte,1)
_, err = conn.Read(oneByte)
if err != nil {
    panic(err)
}
log.Println("First byte:", time.Since(start))

_, err = ioutil.ReadAll(conn)
if err != nil {
    panic(err)
}
log.Println("Everything:", time.Since(start))

Примечание:

Было бы разумно измерять в 3-й точке: когда все заголовки ответов прочитаны. Это обнаруживается при чтении полных строк из ответа (соединения) и обнаружении пустой строки. Эта пустая линия разделяет заголовки ответов из тела ответа.

Чтобы ничего не уводить от абсолютно корректного принятого ответа, одна альтернатива, о которой нужно знать, - это реализация пользовательского RoundTripper, который обертывает http по умолчанию.Транспорт и сеть.Номеронабиратель. Это может быть полезно, если вы инструментирование кода, который использует HTTP.Клиент или если вам нужно поддерживать прокси, TLS, keep-alive или другие возможности HTTP, но не хотите/не нужно повторно реализовать их все. У вас будет не так много контроля, как с полностью настроенным клиентом, но это стоит иметь в виду. твой ящик с инструментами.

Пример круглого триппера:

type customTransport struct {
    rtp       http.RoundTripper
    dialer    *net.Dialer
    connStart time.Time
    connEnd   time.Time
    reqStart  time.Time
    reqEnd    time.Time
}

func newTransport() *customTransport {
    tr := &customTransport{
        dialer: &net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        },
    }
    tr.rtp = &http.Transport{
        Proxy:               http.ProxyFromEnvironment,
        Dial:                tr.dial,
        TLSHandshakeTimeout: 10 * time.Second,
    }
    return tr
}

func (tr *customTransport) RoundTrip(r *http.Request) (*http.Response, error) {
    tr.reqStart = time.Now()
    resp, err := tr.rtp.RoundTrip(r)
    tr.reqEnd = time.Now()
    return resp, err
}

func (tr *customTransport) dial(network, addr string) (net.Conn, error) {
    tr.connStart = time.Now()
    cn, err := tr.dialer.Dial(network, addr)
    tr.connEnd = time.Now()
    return cn, err
}

func (tr *customTransport) ReqDuration() time.Duration {
    return tr.Duration() - tr.ConnDuration()
}

func (tr *customTransport) ConnDuration() time.Duration {
    return tr.connEnd.Sub(tr.connStart)
}

func (tr *customTransport) Duration() time.Duration {
    return tr.reqEnd.Sub(tr.reqStart)
}

Я опустил это в простой пример программы здесь: https://github.com/skyec/go-instrumented-roundtripper/blob/master/main.go

Есть пакет https://golang.org/pkg/net/http/httptrace/ начиная с go 1.7; Он поддерживает синхронизацию следующих операций:

  • создание соединения
  • повторное использование соединения
  • DNS-запросы
  • запись запроса на провод
  • чтение ответа

Также взгляните на https://github.com/davecheney/httpstat . это хорошая утилита для визуализации результатов синхронизации.

Comments

    Ничего не найдено.