TL:DR - 查看编辑 2 中与代码 http 客户端代码等效的 C#,导致〜相同的问题,因此 Go http.Client 不是真正的问题,而是 C# Web API 一旦部署到 Azure...
部署到 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:请求已取消(等待 header 时超出了 Client.Timeout)
它可以是 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 运行它们时,它的时间 < 1 秒。对于 PUT 来说也是如此,这些都是高度简单的数据库 SELECT * FROM TABLE WHERE ID = {id} AND UPDATE。
运行 API 的 Azure Web 应用程序是 2 个标准 S3 实例。
事实是,GET 的重试工作看起来很成功,这很可能是 API/Azure 应用程序没有承受负载,为了简单起见,这是不可能的,我们正在谈论少于 10 个请求/秒。
另一点不可忽视,在开发服务器上运行时,使用相同的 Azure SQL 数据库,因此 SELECT/UPDATE 性能在开发服务器和 Azure Web App 上应该完全相同。
编辑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上添加了全局错误处理程序,可能是由于抛出太多异常而导致的不严谨。添加了 Trace.TraceError
和我的 azure site log tail
它,再次没有显示任何内容。
我什至可以说,本地 Web API 的运行速度比 Azure 标准 S3 实例的运行速度快 25-30 倍。显然这不是真的,但 Web API 非常简单,我不知道如何让 Azure 全速运行它。
最佳答案
我认为你可能想使用上下文、go 例程和 channel 之类的东西来不锁定你的 main.与 C# 中的 Task 类似,它也为您提供更高的吞吐量和性能。
https://marcofranssen.nl/tags/go/
你可以找到我写的一堆博客文章。我也有 C# 背景,这些博客是我学到的一些经验教训。我在博客中确实提到了 C# 与 Go 方法,这样您就可以轻松进行比较。
查看 go 例程和优雅的网络服务器帖子。您可能会在那里找到答案。
此外,您的 C# 实现由于调用 .Result 而被阻塞
您应该使用异步等待,利用任务使代码更加高效。
我预计您的 C# 服务器可能也没有使用 Task 作为返回类型,这意味着它将无法处理大量连接。一旦您在那里使用任务并在新线程中旋转代码,新线程也将能够处理更多并发请求并可能更频繁地成功。因此,基本上在单独的线程/任务中执行数据库操作,并确保处理数据库连接以防止内存泄漏。
这只是一个假设,因为我注意到这篇文章中的客户端代码也没有正确使用它。因此,如果我的假设有误,请原谅我。
关于http - net/http : request canceled (Client. 等待 header 时超时)为什么/该怎么办?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38557968/