net / http:请求已取消(在等待标题时超出了Client.Timeout)为什么/如何处理此问题?

时间:2016-07-24 23:47:04

标签: http azure asp.net-web-api go azure-web-sites

TL:DR - 在编辑2中查看代码http客户端代码的C#等价物,导致〜同样的问题,所以Go http.Client不是真正的问题,但C#Web API曾经部署到Azure ... < / p>

部署到Azure Web App [2x标准版S3]后,我从C#Web API获得了非常糟糕的性能。起初我问的是:Go的http.Client超时,但在C#和NodeJs中编写类似的客户端给出了相同的结果。

这是我的http.Client:

func getWebClient() *http.Client {
    var netTransport = &http.Transport{
        Dial: (&net.Dialer{
            Timeout: 5 * time.Second,
        }).Dial,
        TLSHandshakeTimeout:   10 * time.Second,
        MaxIdleConnsPerHost:   2000,
        ResponseHeaderTimeout: 10 * time.Second,
    }

    var netClient = &http.Client{
        Timeout:   time.Second * 10,
        Transport: netTransport,
    }

    return netClient
}

我得到的错误:

net/http: request canceled (Client.Timeout exceeded while awaiting headers)

它可以在GET,POST,PUT上。我收到了这些错误,但是使用curl运行失败的GET立即得到了答复。

这是我用来调用API的示例Get函数:

func get(path string, result interface{}) error {
    req, err := http.NewRequest("GET", webDALAPIURL+path, nil)
    if err != nil {
        return err
    }

    req.Header.Set("Content-Type", "application/json")

    wc := getWebClient()
    res, err := wc.Do(req)
    if err != nil {
        return err
    }
    defer res.Body.Close()

    if res.StatusCode >= 400 {
        return fmt.Errorf("[GET] %d - %s", res.StatusCode, webDALAPIURL+path)
    }

    decoder := json.NewDecoder(res.Body)
    return decoder.Decode(result)
}

有趣的是,API在本地运行时从未遇到过这些错误。 API是一个C#ASP.NET Web API应用程序。

我开始有很多TLS握手错误,因此我删除了Azure应用端点的https。现在我收到了这个错误。

我正在为应用程序添加日志,并且没有任何事情发生[正在调用API]。似乎Go无法对同一主机进行多次调用。我不是在一个cmd中使用goroutine并在另一个cmd中使用它们的事件,两者都会导致相同的错误。

当API在同一网络中的Windows计算机上运行时,在开发过程中从未出现过该错误。

编辑1:

请注意,80-85%的请求运行良好,场景是(伪代码):

for item in items {
  http get /item/{id} => works 90% of the time, 10% timeout

  change item properties

  http PUT /item/{id} => works 80% of the time
}

我在get()函数中添加了一个重试,所以如果发生超时,它会重试get,这似乎有效。虽然,我根本不喜欢这种解决方法。

另请注意,我们正在谈论返回超时的快速GET,当我从curl运行它们时,它是&lt; 1秒。对于PUT来说,这些是非常简单的数据库SELECT * FROM TABLE WHERE ID = {id} AND UPDATE。

运行API的Azure Web应用程序是Standard S3的2个实例。

GET的重试工作看起来很像,这肯定是API / Azure应用程序没有承担负载,这对于简单性是不可能的,我们谈论的是少于10个请求/秒。

另一点不容忽视,当在dev服务器上运行时,使用了相同的Azure SQL数据库,因此SELECT / UPDATE perf应该在dev和Azure Web App上完全相同。

编辑2:

从本地到Azure比较相同的C#Web API的速度令人不安。我写了一个类似的C#http客户端来测试Azure vs Local Web API。

class Program
{
  static int fails = 0;
  static void Main(string[] args)
  {
    for (int i = 0; i < 2000; i++)
    {
      get(i);
    }
    Console.WriteLine("completed: " + fails.ToString());
    Console.ReadLine();
  }
  static void get(int id)
  {
    id += 22700;
    var c = new HttpClient();

    var resp = c.GetAsync("http://myapphere.azurewebsites.net/api/users/" + id).Result;
    if (!resp.IsSuccessStatusCode)
    {
      Console.WriteLine("");
      fails++;
      Console.WriteLine(string.Format("error getting /users status code {0}", resp.StatusCode));
    }
    else
    {
      Console.Write(".");
    }
  }
}

运行此控制台应用程序对抗Azure 我可以清楚地看到Go的时间超时,速度很慢,没有返回错误,但是Console.Write(“。”);需要永远打印,是周期性的,可以打印〜快3-4,而不是停止。

再次将更改为localhost:1078 使用相同的数据库没有暂停,并且Console.Write(“。”)打印速度比Azure的速度快20倍

这怎么可能?

编辑3:

刚刚在web api上添加了全局错误处理程序,可能是由于引发了太多Exception引起的邋。。添加了Trace.TraceError和我azure site log tail,再次没有显示任何内容。

我甚至会说本地web api的运行速度比Azure Standard S3的2x实例快25-30倍。显然这不可能是真的,但是web api非常简单,我不知道如何让Azure以全速运行它。

1 个答案:

答案 0 :(得分:0)

我认为您可能想使用上下文之类的东西,使用例程和通道来不锁定您的主线程。与c#中的Task类似,它还为您提供了更高的吞吐量和性能。

https://marcofranssen.nl/tags/go/

您可以找到我写的一堆博客文章。我也有c#背景知识,这些博客是我的一些经验教训。我确实在博客中提到了c#vs Go方法,以便您轻松进行比较。

查看go例程和优美的Web服务器帖子。您可能会在这里找到答案。

此外,由于调用.Result

,因此您的c#实现受到了阻止

您应该使用异步等待,这可以通过使用Task来提高代码效率。

我希望您的C#服务器可能也没有使用Task作为返回类型,这意味着它将无法应付大量的连接。一旦您在此处使用Task并在New线程中旋转代码,它们也将能够处理更多的并发请求并可能更频繁地成功。因此,基本上将db填充在单独的线程/任务中,并确保处置数据库连接以防止内存泄漏。

这只是一个假设,因为我注意到本文中的客户端代码也没有正确使用此代码。如果我对自己的假设有误,请原谅我。