代码之家  ›  专栏  ›  技术社区  ›  Aditya Singh

如何在Go中模拟测试中HTTP请求的504超时错误?

  •  2
  • Aditya Singh  · 技术社区  · 6 年前

    我正在尝试添加 timeout 在Go中选择一个库,并编写了下面的测试来模拟行为。

    func TestClientTimeout(t *testing.T) {
        backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            d := map[string]interface{}{
                "id":    "12",
                "scope": "test-scope",
            }
    
            time.Sleep(100 * time.Millisecond)
            e := json.NewEncoder(w)
            err := e.Encode(&d)
            if err != nil {
                t.Error(err)
            }
            w.WriteHeader(http.StatusOK)
        }))
    
        url := backend.URL
        ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
        defer cancel()
        req, err := http.NewRequest("GET", url, nil)
        if err != nil {
            t.Error("Request error", err)
        }
    
        resp, err := http.DefaultClient.Do(req.WithContext(ctx))
        if err != nil {
            t.Error("Response error", err)
        }
    
        defer resp.Body.Close()
    
        t.Log(">>>>>>>Response is: ", resp)
    }
    

    但我总是低于错误,而不是 http.StatusGatewayTimeout

    ==运行testclienttimeout

    ---失败:testclienttimeout(0.05s)

    client_test.go:37: Timestamp before req 2018-07-13 09:10:14.936898 +0200 CEST m=+0.002048937
    client_test.go:40: Response error Get http://127.0.0.1:49597: context deadline exceeded
    

    死机:运行时错误:内存地址无效或无指针取消引用[已恢复]

    死机:运行时错误:内存地址无效或无指针取消引用

    如何修复此测试,以返回响应 http.statusgateway超时 (504)状态代码?

    1 回复  |  直到 6 年前
        1
  •  3
  •   Aditya Singh    6 年前

    你出错的原因 context deadline exceeded 是因为 context.Context 请求的客户端比服务器端处理程序中的超时时间短。这意味着 上下文。上下文 因此客户方 http.DefaultClient 在写任何回应之前放弃。

    这个 panic: runtime error: invalid memory address... 是因为您延迟关闭响应的主体,但响应是 nil 如果客户端返回错误。

    这里的响应为零,如果错误为非零,则更改 t.Error t.Fatal

    resp, err := http.DefaultClient.Do(req.WithContext(ctx))
    if err != nil {
        // this should be t.Fatal, or don't do the body close if there's an error
        t.Error("Response error", err)
    }
    
    defer resp.Body.Close()
    

    找到问题的真正根源, http.StatusGatewayTimeout 是服务器端超时,这意味着创建的任何超时都必须在服务器端。客户 默认客户端 永远不会创建自己的服务器错误响应代码。

    要创建服务器端超时,可以将处理程序函数包装在 http.TimeoutHandler :

    handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        d := map[string]interface{}{
            "id":    "12",
            "scope": "test-scope",
        }
    
        time.Sleep(100 * time.Millisecond)
        e := json.NewEncoder(w)
        err := e.Encode(&d)
        if err != nil {
            t.Error(err)
        }
        w.WriteHeader(http.StatusOK)
    })
    
    backend := httptest.NewServer(http.TimeoutHandler(handlerFunc, 20*time.Millisecond, "server timeout"))
    

    但是,这将创建一个 503 - Service Unavailable 错误响应代码。

    了解504的重要一点是,这是一个“网关”或“代理”错误响应代码。这意味着这段代码不太可能来自实际处理请求的服务器。这个代码在负载平衡器和代理中更常见。

    504网关超时 服务器在充当网关或代理时,没有收到上游服务器的及时响应,该服务器需要访问才能完成请求。

    你已经嘲笑了 http.Server 在试验方法中使用 httptest.NewServer(...) 所以你可以手动返回 http.statusgateway超时 处理程序函数中的响应状态。

    handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusGatewayTimeout)
    })
    
    backend := httptest.NewServer(handlerFunc)