2015-05-29 20 views
7

Trong chương trình Go của tôi, tôi đã thực hiện một số yêu cầu HTTP và tôi cần thời gian phản hồi (và không yêu cầu thời gian).Phản hồi HTTP Thời gian trong Go

Đây là mã hiện tại của tôi (thời gian thời gian theo yêu cầu):

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 
} 

Trên thực tế, mã này sẽ hiển thị một cái gì đó về thời gian 5s yêu cầu, bao gồm độ phân giải DNS và những thứ khác ... Nếu tôi thực hiện các thử nghiệm tương tự với một công cụ như Apache JMeter, thời gian chỉ khoảng 100ms (đó là thời gian phản hồi thực của máy chủ, mà không cần quan tâm đến thời gian yêu cầu).

Điều tôi thực sự muốn là tính toán thời gian phản hồi thực của máy chủ. Làm thế nào tôi có thể tính toán điều này trong Go?

+3

Ý anh là gì bởi thời gian đáp ứng? Thời gian từ khi máy chủ hoàn tất việc xử lý yêu cầu đến khi máy khách nhận được yêu cầu đó? Tôi không chắc chắn làm thế nào mà có thể có được - các chuyến đi vòng chính nó, dns và tất cả những gì là một phần vốn có của chu kỳ HTTP ... Đặt cược tốt nhất của bạn sẽ là thời gian nó trên máy chủ. –

+0

Ok đây là những gì tôi nghĩ. Trong thực tế, tôi không thực sự biết làm thế nào công cụ JMeter là tính toán thời gian này, nhưng tôi muốn một cái gì đó như thế này. JMeter không kiểm tra máy chủ (trong xác nhận của tôi). Tôi thử nghiệm với wget và thời gian là như nhau mà một trong những tính toán của chương trình của tôi (khoảng 5s bao gồm cả độ phân giải DNS). – Devatoria

Trả lời

8

Bạn có thể đo nó bằng cách mở một kết nối TCP và "nói" HTTP thô (wikipedia, rfc7230, rfc7231, rfc7232, rfc7233, rfc7234, rfc7235). Bạn tạo kết nối, gửi yêu cầu và bắt đầu hẹn giờ tại đây. Và chờ phản hồi. Bằng cách đó, bạn sẽ loại trừ khả năng phân giải DNS và thời gian cần thiết để thực hiện kết nối.

Vì bạn không có thông tin chi tiết cho dù máy chủ bắt đầu gửi dữ liệu trở lại ngay lập tức hoặc chỉ khi mọi thứ đã sẵn sàng và bạn không có thông tin về sự chậm trễ mạng, nó sẽ không chính xác thay vì ước tính.

tôi sẽ đo tại 2 điểm: khi byte đầu tiên có thể được đọc và khi mọi thứ đã được đọc:

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

Lưu ý:

Nó có thể là hợp lý để đo tại một điểm thứ 3: khi tất cả các tiêu đề phản hồi được đọc. Điều này được phát hiện khi đọc dòng đầy đủ từ phản hồi (kết nối) và gặp phải một dòng trống. Dòng trống này phân tách các tiêu đề phản hồi khỏi phần thân phản hồi.

+0

Cảm ơn bạn rất nhiều vì câu trả lời này. Đây là những gì tôi đang tìm kiếm.Tôi sẽ cải thiện nó một chút để chính xác hơn nhưng tôi không cần sự hoàn hảo :) – Devatoria

12

Không được lấy bất kỳ thứ gì từ câu trả lời được chấp nhận hoàn toàn hợp lệ, một giải pháp thay thế cần lưu ý là triển khai RoundTripper tùy chỉnh kết thúc http.Transport và net.Dialer mặc định. Điều này có thể hữu ích nếu bạn đang sử dụng mã công cụ sử dụng http.Client hoặc nếu bạn cần hỗ trợ proxy, TLS, khả năng lưu giữ hoặc các khả năng HTTP khác nhưng không muốn/cần phải triển khai lại tất cả. Bạn sẽ không có quyền kiểm soát nhiều như bạn sẽ với một khách hàng hoàn toàn tùy chỉnh nhưng nó có giá trị trong hộp công cụ của bạn.

Ví dụ vòng tripper:

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

Tôi đã giảm đó vào một ví dụ chương trình đơn giản ở đây: https://github.com/skyec/go-instrumented-roundtripper/blob/master/main.go

+0

Cảm ơn bạn đã thay thế thú vị này có thể giúp tôi cho các bước tiếp theo trong dự án của tôi :) – Devatoria

Các vấn đề liên quan