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以全速运行它。
答案 0 :(得分:0)
我认为您可能想使用上下文之类的东西,使用例程和通道来不锁定您的主线程。与c#中的Task类似,它还为您提供了更高的吞吐量和性能。
https://marcofranssen.nl/tags/go/
您可以找到我写的一堆博客文章。我也有c#背景知识,这些博客是我的一些经验教训。我确实在博客中提到了c#vs Go方法,以便您轻松进行比较。
查看go例程和优美的Web服务器帖子。您可能会在这里找到答案。
此外,由于调用.Result
,因此您的c#实现受到了阻止您应该使用异步等待,这可以通过使用Task来提高代码效率。
我希望您的C#服务器可能也没有使用Task作为返回类型,这意味着它将无法应付大量的连接。一旦您在此处使用Task并在New线程中旋转代码,它们也将能够处理更多的并发请求并可能更频繁地成功。因此,基本上将db填充在单独的线程/任务中,并确保处置数据库连接以防止内存泄漏。
这只是一个假设,因为我注意到本文中的客户端代码也没有正确使用此代码。如果我对自己的假设有误,请原谅我。