2015-05-29 2 views
7

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

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

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?

+3

Что вы имеете в виду по времени отклика? Время, когда сервер завершил обработку запроса до момента, когда клиент получил его? Я не уверен, как это будет возможно - сама по себе поездка, dns и все, что является неотъемлемой частью цикла HTTP ... Лучше всего было бы время на сервере. –

+0

Хорошо, вот что я подумал. На самом деле, я действительно не знаю, как инструмент JMeter вычисляет это время, но мне хотелось бы что-то вроде этого. JMeter не проверяет сервер (в моем знании). Я тестировал с помощью wget, и время совпадает с тем, которое было рассчитано моей программой (около 5 с, включая разрешение DNS). – Devatoria

ответ

8

Вы можете измерить его открытия соединения TCP и "говорить" сырой HTTP (wikipedia, 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 пункта: когда считываются все заголовки ответов. Это определяется как чтение полных строк из ответа (соединение) и встреча с пустой строкой. Эта пустая строка отделяет заголовки ответов от тела ответа.

+0

Большое спасибо за этот ответ. Это то, что я искал.Я собираюсь улучшить его немного, чтобы быть более точным, но мне не нужно совершенства :) – Devatoria

12

Чтобы не принимать что-либо от полностью приемлемого принятого ответа, одна из альтернатив, которую следует знать, - это реализовать пользовательский RoundTripper, который обертывает по умолчанию http.Transport и net.Dialer. Это может быть полезно, если вы применяете код, который использует http.Client, или если вам нужно поддерживать прокси-серверы, TLS, поддерживать работоспособность или другие возможности 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

+0

Спасибо за эту интересную альтернативу, которая может помочь мне в дальнейших шагах в моем проекте :) – Devatoria

Смежные вопросы