ASP.net HttpRequest 上的静态 HttpClient 线程安全
Static HttpClient thread safe on ASP.net HttpRequest
我们正在为 HttpClient 创建一个包装器。因为我们将遵循 https://github.com/mspnp/performance-optimization 中的性能优化指南。我们想避免 anti-pattern - 该文档中提到的不正确的实例化。我将此指南推荐给了我的团队以使用静态 HttpClient。我得到的反馈是 thread-safety。每个请求都有一个 header 包含用户声明。由于我有一个静态的 HttpClient,它会是 thread-safe 吗?如果我们有多个请求同时命中代码(例如 GET),设置 header 是否会成为竞争条件?我们有如下实现。
public class HttpClientHelper{
private static readonly HttpClient _HttpClient;
static HttpClientHelper() {
HttpClient = new HttpClient();
HttpClient.Timeout = TimeSpan.FromMinutes(SOME_CONFIG_VALUE);
}
public async Task<HttpResponseMessage> CallHttpClientPostAsync(string requestUri, HttpContent requestBody)
{
AddHttpRequestHeader(httpClient);
var response = await httpClient.PostAsync(requestUri, requestBody); //Potential thread synchronization issue???
return response;
}
public HttpResponseMessage CallHttpClientGet(string requestUri)
{
AddHttpRequestHeader(httpClient);
var response = httpClient.GetAsync(requestUri).Result; //Potential thread synchronization issue???
return response;
}
private void AddHttpRequestHeader(HttpClient client)
{
string HeaderName = "CorrelationId";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Properties.Settings.Default.HttpClientAuthHeaderScheme, GetTokenFromClaims()); //Race condition???
if (client.DefaultRequestHeaders.Contains(HeaderName))
client.DefaultRequestHeaders.Remove(HeaderName);
client.DefaultRequestHeaders.Add(HeaderName, Trace.CorrelationManager.ActivityId.ToString());
}
}
您的团队是正确的,这远非线程安全。考虑这种情况:
- 线程 A 将 CorrelationId header 设置为 "foo"。
- 线程 B 将 CorrelationId header 设置为 "bar"。
- 线程A发送请求,其中包含线程B的CorrelationId。
更好的方法是让您的 CallXXX 方法创建新的 HttpRequestMessage objects, and set the header on those, and use HttpClient.SendAsync 来进行调用。
另请记住,re-using HttpClient
实例仅在您对同一主机进行多次调用时才有用。
我们正在为 HttpClient 创建一个包装器。因为我们将遵循 https://github.com/mspnp/performance-optimization 中的性能优化指南。我们想避免 anti-pattern - 该文档中提到的不正确的实例化。我将此指南推荐给了我的团队以使用静态 HttpClient。我得到的反馈是 thread-safety。每个请求都有一个 header 包含用户声明。由于我有一个静态的 HttpClient,它会是 thread-safe 吗?如果我们有多个请求同时命中代码(例如 GET),设置 header 是否会成为竞争条件?我们有如下实现。
public class HttpClientHelper{
private static readonly HttpClient _HttpClient;
static HttpClientHelper() {
HttpClient = new HttpClient();
HttpClient.Timeout = TimeSpan.FromMinutes(SOME_CONFIG_VALUE);
}
public async Task<HttpResponseMessage> CallHttpClientPostAsync(string requestUri, HttpContent requestBody)
{
AddHttpRequestHeader(httpClient);
var response = await httpClient.PostAsync(requestUri, requestBody); //Potential thread synchronization issue???
return response;
}
public HttpResponseMessage CallHttpClientGet(string requestUri)
{
AddHttpRequestHeader(httpClient);
var response = httpClient.GetAsync(requestUri).Result; //Potential thread synchronization issue???
return response;
}
private void AddHttpRequestHeader(HttpClient client)
{
string HeaderName = "CorrelationId";
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(Properties.Settings.Default.HttpClientAuthHeaderScheme, GetTokenFromClaims()); //Race condition???
if (client.DefaultRequestHeaders.Contains(HeaderName))
client.DefaultRequestHeaders.Remove(HeaderName);
client.DefaultRequestHeaders.Add(HeaderName, Trace.CorrelationManager.ActivityId.ToString());
}
}
您的团队是正确的,这远非线程安全。考虑这种情况:
- 线程 A 将 CorrelationId header 设置为 "foo"。
- 线程 B 将 CorrelationId header 设置为 "bar"。
- 线程A发送请求,其中包含线程B的CorrelationId。
更好的方法是让您的 CallXXX 方法创建新的 HttpRequestMessage objects, and set the header on those, and use HttpClient.SendAsync 来进行调用。
另请记住,re-using HttpClient
实例仅在您对同一主机进行多次调用时才有用。