这些 HttpClient 调用是否并行调用?
Does these HttpClient call be called in parallel?
我有一个 .NET Core web 应用程序,一旦调用了一个 web 方法(即 Test()),就会调用另一个远程 api。
基本上,我是这样做的(这里有一个 POST 示例,在 Web 方法 Test() 中调用):
public T PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
T returnValue = default(T);
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
HttpResponseMessage res = null;
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var task = Task.Run(() => client.PostAsync($"{baseAddress}/{url}", body));
task.Wait();
res = task.Result;
succeded = res.IsSuccessStatusCode;
if (succeded)
{
returnValue = JsonConvert.DeserializeObject<T>(res.Content.ReadAsStringAsync().Result);
}
else
{
Log($"PostRead failed, error: {JsonConvert.SerializeObject(res)}");
}
}
}
catch (Exception ex)
{
Log($"PostRead error: {baseAddress}/{url} entity: {JsonConvert.SerializeObject(entity)}");
}
return returnValue;
}
问题是:如果我的 Web 应用程序中的 Test() 被并行调用 10 次,那么对远程服务器的 POST 请求是并行调用还是串行调用?
因为如果串行调用,最后一个会占用前面几个的时间,这不是我想要的。
事实上,当我有大量的请求列表时,我经常收到消息A connection attempt failed because the connected party didn't properly respond after a period time, or established connection failed because connected主机未能响应。
Question is: if Test() in my Web App is called 10 times, in parallel, do the POST requests to the remote server are be called in parallel or in serial?
它们将被并行调用。代码中的任何内容都不会阻止它。
In fact, when I have huge list of requests, I often receive the message A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
这里至少有两件事需要解决:
- 正确使用
async
/await
- 正确使用
HttpClient
第一个很简单:
public async Task<T> PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var responseMessage = await client.PostAsync($"{baseAddress}/{url}", body);
var responseContent = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
succeeded = true;
return JsonConvert.DeserializeObject<T>();
}
else
{
// log...
}
}
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}
第二个有点棘手,很可能是您遇到的问题的原因。通常,除非您有一些非常具体的情况,否则 new HttpClient()
是完全错误的。它浪费资源,隐藏依赖关系并防止模拟。正确的方法是使用 IHttpClientFactory
,在注册服务时使用 AddHttpClient
扩展方法可以很容易地抽象出来。
基本上,这看起来像这样:
services.AddHttpClient<YourClassName>(x =>
{
x.BaseAddress = new Uri(baseAddress);
x.DefaultRequestHeaders.Add("Authorization", MyApiKey);
}
然后就是删除 class 中的所有 using HttpClient
并且只是:
private readonly HttpClient _client;
public MyClassName(HttpClient client) { _client = client; }
public async Task<T> PostRead<T>(string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
string json = JsonConvert.SerializeObject(entity);
var responseMessage = await _client.PostAsync($"{baseAddress}/{url}", body);
//...
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}
我有一个 .NET Core web 应用程序,一旦调用了一个 web 方法(即 Test()),就会调用另一个远程 api。
基本上,我是这样做的(这里有一个 POST 示例,在 Web 方法 Test() 中调用):
public T PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
T returnValue = default(T);
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
HttpResponseMessage res = null;
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var task = Task.Run(() => client.PostAsync($"{baseAddress}/{url}", body));
task.Wait();
res = task.Result;
succeded = res.IsSuccessStatusCode;
if (succeded)
{
returnValue = JsonConvert.DeserializeObject<T>(res.Content.ReadAsStringAsync().Result);
}
else
{
Log($"PostRead failed, error: {JsonConvert.SerializeObject(res)}");
}
}
}
catch (Exception ex)
{
Log($"PostRead error: {baseAddress}/{url} entity: {JsonConvert.SerializeObject(entity)}");
}
return returnValue;
}
问题是:如果我的 Web 应用程序中的 Test() 被并行调用 10 次,那么对远程服务器的 POST 请求是并行调用还是串行调用?
因为如果串行调用,最后一个会占用前面几个的时间,这不是我想要的。
事实上,当我有大量的请求列表时,我经常收到消息A connection attempt failed because the connected party didn't properly respond after a period time, or established connection failed because connected主机未能响应。
Question is: if Test() in my Web App is called 10 times, in parallel, do the POST requests to the remote server are be called in parallel or in serial?
它们将被并行调用。代码中的任何内容都不会阻止它。
In fact, when I have huge list of requests, I often receive the message A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
这里至少有两件事需要解决:
- 正确使用
async
/await
- 正确使用
HttpClient
第一个很简单:
public async Task<T> PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var responseMessage = await client.PostAsync($"{baseAddress}/{url}", body);
var responseContent = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
succeeded = true;
return JsonConvert.DeserializeObject<T>();
}
else
{
// log...
}
}
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}
第二个有点棘手,很可能是您遇到的问题的原因。通常,除非您有一些非常具体的情况,否则 new HttpClient()
是完全错误的。它浪费资源,隐藏依赖关系并防止模拟。正确的方法是使用 IHttpClientFactory
,在注册服务时使用 AddHttpClient
扩展方法可以很容易地抽象出来。
基本上,这看起来像这样:
services.AddHttpClient<YourClassName>(x =>
{
x.BaseAddress = new Uri(baseAddress);
x.DefaultRequestHeaders.Add("Authorization", MyApiKey);
}
然后就是删除 class 中的所有 using HttpClient
并且只是:
private readonly HttpClient _client;
public MyClassName(HttpClient client) { _client = client; }
public async Task<T> PostRead<T>(string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
string json = JsonConvert.SerializeObject(entity);
var responseMessage = await _client.PostAsync($"{baseAddress}/{url}", body);
//...
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}