Go HTTP Response Time

In my Go program, I made some HTTP requests, and I need response time (and not request time).

Here is my current code (time request time):

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 } 

Actually, this code will show something about the 5s request time, including DNS resolution and other things ... If I run the same test using the Apache JMeter tool, the time is only about 100 ms (which is the real server response time, without worrying request time).

I really want to calculate the real server response time. How can I calculate this in Go?

+9
go
source share
3 answers

Edit: The following answer precedes Go 1.7. Go 1.7 added HTTP tracing, so be sure to check out the new answer: Getting the TTFB value (time to the first byte) in golang

The original answer follows.


You can measure it by opening a TCP connection and β€œspeaking” raw HTTP ( Wikipedia , rfc7230 , rfc7231 , rfc7232 , rfc7233 , rfc7234 , rfc7235 ). You establish a connection, send a request and start the timer here. And wait for an answer. In doing so, you will exclude DNS resolution and the time required to establish a connection.

Since you do not know if the server will start sending data immediately or only when everything is ready, and you do not have information about network delays, this will not be an accurate estimate.

I would measure in 2 points: when can I read the first byte and when everything is read:

 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)) 

Remarks:

It would be wise to measure from the 3rd moment: when all the response headers are read. This is defined as reading complete lines from a response (connection) and detecting an empty line. This empty line separates the response headers from the response body.

+10
source

Do not subtract anything from the fully acceptable accepted answer, one of the alternatives to be aware of is to implement a custom RoundTripper that wraps the standard http.Transport and net.Dialer. This can be useful if you are using code that uses http.Client, or if you need to support proxies, TLS, maintain health, or other HTTP features, but do not want / should not re-execute them all. You will not have as much control as you, with a fully configured client, but it is worth having in your toolbox.

Round tripper example:

 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) } 

I dropped this into a simple example program: https://github.com/skyec/go-instrumented-roundtripper/blob/master/main.go

+12
source

There is a package https://golang.org/pkg/net/http/httptrace/, starting with version 1.7; Supports synchronization of the following operations:

  • Create connection
  • Reuse connection
  • DNS lookup
  • Writing a wire request
  • Reading answer

Also take a look at https://github.com/davecheney/httpstat . This is a good utility for visualizing synchronization results.

+1
source

All Articles