net/http:请求已取消(等待标头时超出 Client.Timeout)为啥/如何处理?
Posted
技术标签:
【中文标题】net/http:请求已取消(等待标头时超出 Client.Timeout)为啥/如何处理?【英文标题】:net/http: request canceled (Client.Timeout exceeded while awaiting headers) why/what to do with this?net/http:请求已取消(等待标头时超出 Client.Timeout)为什么/如何处理? 【发布时间】:2016-11-28 05:14:05 【问题描述】:TL:DR - 查看编辑 2 的代码 http 客户端代码的 C# 等效代码,导致〜相同的问题,所以 Go http.Client 不是真正的问题,但 C# Web API 一旦部署到 Azure...
一旦将 C# Web API 部署到 Azure Web App [2x Standard S3],我的性能就很差。起初我问的是: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 并在另一个中使用它们,两者都会导致相同的错误。
当 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,当我从 curl 运行它们时,它是
运行 API 的 Azure Web 应用是标准 S3 的 2 个实例。
GET 重试成功的事实看起来,很可能是 API / Azure 应用程序没有承担负载,这对于简单来说是不可能的,我们正在谈论少于 10 个请求/秒。
另一点不可忽略,在开发服务器上运行时,使用了相同的 Azure SQL 数据库,因此 SELECT/UPDATE 性能在开发和 Azure Web 应用上应该完全相同。
编辑 2:
比较相同的 C# Web API 从本地到 Azure 的速度令人不安。我编写了一个类似的 C# http 客户端来测试 Azure 与本地 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 标准 S3 的 2 倍实例快 25-30 倍。显然这不可能是真的,但是 Web api 是如此简单,以至于我看不出我可以做些什么来让 Azure 全速运行它。
【问题讨论】:
一个需要确认的问题:Fiddler,您能否使用 IE 或其他浏览器访问托管在 Azure 上的 ASP.NET API?我认为第一件事是我们应该确保您的 API 在 Azure Web App 上运行良好。 API 工作正常,80% 的请求有效,它是周期性的,在收到错误后立即通过 curl 运行失败的请求。 Will Shao 编写了一个 C# 等效应用程序,当使用 Azure 端点运行时速度非常慢,问题根本不在于 Go,但事实上,具有相同数据库的相同应用程序在部署到 Azure 后运行不佳。 您可以尝试将 Application Insights 添加到 API 并使用它来尝试找出正在发生的事情。 【参考方案1】:我认为您可能希望使用上下文、执行例程和频道等内容来不锁定您的 main。与 c# 中的 Task 类似,它也为您提供更高的吞吐量和性能。
https://marcofranssen.nl/tags/go/
你可以找到一堆我写的博客文章。我也有 c# 背景,这些博客是我的一些经验教训。我确实在博客中提到了 c# vs Go 方法,以便您进行比较。
查看 go 例程和优雅的网络服务器帖子。您可能会在那里找到答案。
由于调用 .Result,您的 c# 实现也会阻塞
您应该使用 async await,这使得代码更加高效,利用 Task。
我希望您的 c# 服务器也可能没有使用 Task 作为返回类型,这意味着它将无法处理大量连接。只要您在那里使用任务并在新线程中旋转代码,这些代码也将能够处理更多并发请求并可能更频繁地成功。所以基本上在单独的线程/任务中做 db 的东西,并确保处理数据库连接以防止内存泄漏。
这只是一个假设,因为我注意到这篇文章中的客户端代码也没有正确使用它。如果我的假设有误,请原谅我。
【讨论】:
以上是关于net/http:请求已取消(等待标头时超出 Client.Timeout)为啥/如何处理?的主要内容,如果未能解决你的问题,请参考以下文章
Docker Toolbox Tutorial Client.Timeout 在等待标头时超出
ECS 任务未启动 - 已停止(CannotPullContainerError:“来自守护程序请求的错误响应在等待连接时取消”