如何在 ASP.Net Core 2.0 上创建 HttpClient 以防止 Azure WebApps 上的 SNAT 端口耗尽
How do I create HttpClient on ASP.Net Core 2.0 to prevent SNAT port exhaustion on Azure WebApps
为了重用 HttpClient,我在 ASP.Net Core 2.0 上创建了一个像这样的实用程序 class。
public class HttpClientUtility
{
private static HttpClientUtility _singleInstance = new HttpClientUtility();
public static HttpClientUtility GetInstance()
{
return _singleInstance;
}
private HttpClientUtility()
{
}
private static readonly ConcurrentDictionary<string, HttpClient> _HttpClientDict = new ConcurrentDictionary<string, HttpClient>();
private HttpClient GetHttpClient(
Uri uri)
{
return _HttpClientDict.GetOrAdd(GetHostCacheKeyFromUri(uri),
(n) =>
{
return new HttpClient();
});
}
private static string GetHostCacheKeyFromUri(
Uri uri)
{
return $"{uri.Scheme}://{uri.DnsSafeHost}:{uri.Port}";
}
private async Task<HttpRequestMessage> CreateRequestMessage(
Uri requestUri,
HttpMethod method,
IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers,
HttpContent content,
bool contentCopy = true
)
{
var requestMessage = new HttpRequestMessage(method, requestUri);
if (headers != null)
{
foreach (var header in headers)
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
if (content != null)
{
if (contentCopy)
{
var contentStream = new MemoryStream();
await content.CopyToAsync(contentStream);
contentStream.Position = 0;
requestMessage.Content = new StreamContent(contentStream);
if (requestMessage.Content.Headers != null)
{
foreach (var header in content.Headers)
{
requestMessage.Content.Headers.Add(header.Key, header.Value);
}
}
}
else
{
requestMessage.Content = content;
}
}
return requestMessage;
}
public async Task<HttpResponseMessage> SendRequestWithRetry(
Uri requestUri,
HttpMethod method,
HttpContent content,
IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers,
Func<HttpResponseMessage, bool> needRetry)
{
HttpResponseMessage response = null;
var httpClient = GetHttpClient(requestUri);
for (int i = 0; i <= MaxRetryCount; i++)
{
// Create request message for retry
var isPossblRetry = needRetry != null && MaxRetryCount > 0;
var request = await CreateRequestMessage(requestUri, method, headers, content, isPossblRetry).ConfigureAwait(false);
response = await httpClient.SendAsync(request).ConfigureAwait(false);
if (response.IsSuccessStatusCode || needRetry == null || !needRetry(response))
{
break;
}
}
return response;
}
}
使用此代码,我认为单个 HttpClient 实例将用于同一请求 url。
但是,Azure 门户上的诊断和解决问题 blade 表示发生 SNAT 端口耗尽。
当许多并发请求发生时,这段代码是否会导致此问题?
如果是这样,我如何在 ASP.Net Core 2.0(没有 HttpClientFactory)上创建 HttpClient。
[环境]
- ASP.Net 核心 2.0
- Azure Web 应用
请务必执行以下步骤:
- 将
HttpClientUtility
作为单例注入。
- 不要复杂化 - 只需将
new HttpClient()
用作静态变量即可。 GetHostCacheKeyFromUri
不是必需的。
感谢所有的回答和评论。
但我得到了MS的答复。
他们说,如果同一 url 有许多超过 128 的请求,即使我使用 HttpClientFactory 或静态 HttpClient,也会在 Azure WebApps 上发生 [SNAT 端口耗尽]。
唯一的解决方案似乎是扩展 WebApps 或使用 ASE。
所以我会执行建议的编码,然后横向扩展。
为了重用 HttpClient,我在 ASP.Net Core 2.0 上创建了一个像这样的实用程序 class。
public class HttpClientUtility
{
private static HttpClientUtility _singleInstance = new HttpClientUtility();
public static HttpClientUtility GetInstance()
{
return _singleInstance;
}
private HttpClientUtility()
{
}
private static readonly ConcurrentDictionary<string, HttpClient> _HttpClientDict = new ConcurrentDictionary<string, HttpClient>();
private HttpClient GetHttpClient(
Uri uri)
{
return _HttpClientDict.GetOrAdd(GetHostCacheKeyFromUri(uri),
(n) =>
{
return new HttpClient();
});
}
private static string GetHostCacheKeyFromUri(
Uri uri)
{
return $"{uri.Scheme}://{uri.DnsSafeHost}:{uri.Port}";
}
private async Task<HttpRequestMessage> CreateRequestMessage(
Uri requestUri,
HttpMethod method,
IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers,
HttpContent content,
bool contentCopy = true
)
{
var requestMessage = new HttpRequestMessage(method, requestUri);
if (headers != null)
{
foreach (var header in headers)
{
requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
if (content != null)
{
if (contentCopy)
{
var contentStream = new MemoryStream();
await content.CopyToAsync(contentStream);
contentStream.Position = 0;
requestMessage.Content = new StreamContent(contentStream);
if (requestMessage.Content.Headers != null)
{
foreach (var header in content.Headers)
{
requestMessage.Content.Headers.Add(header.Key, header.Value);
}
}
}
else
{
requestMessage.Content = content;
}
}
return requestMessage;
}
public async Task<HttpResponseMessage> SendRequestWithRetry(
Uri requestUri,
HttpMethod method,
HttpContent content,
IEnumerable<KeyValuePair<string, IEnumerable<string>>> headers,
Func<HttpResponseMessage, bool> needRetry)
{
HttpResponseMessage response = null;
var httpClient = GetHttpClient(requestUri);
for (int i = 0; i <= MaxRetryCount; i++)
{
// Create request message for retry
var isPossblRetry = needRetry != null && MaxRetryCount > 0;
var request = await CreateRequestMessage(requestUri, method, headers, content, isPossblRetry).ConfigureAwait(false);
response = await httpClient.SendAsync(request).ConfigureAwait(false);
if (response.IsSuccessStatusCode || needRetry == null || !needRetry(response))
{
break;
}
}
return response;
}
}
使用此代码,我认为单个 HttpClient 实例将用于同一请求 url。
但是,Azure 门户上的诊断和解决问题 blade 表示发生 SNAT 端口耗尽。
当许多并发请求发生时,这段代码是否会导致此问题?
如果是这样,我如何在 ASP.Net Core 2.0(没有 HttpClientFactory)上创建 HttpClient。
[环境]
- ASP.Net 核心 2.0
- Azure Web 应用
请务必执行以下步骤:
- 将
HttpClientUtility
作为单例注入。 - 不要复杂化 - 只需将
new HttpClient()
用作静态变量即可。GetHostCacheKeyFromUri
不是必需的。
感谢所有的回答和评论。 但我得到了MS的答复。 他们说,如果同一 url 有许多超过 128 的请求,即使我使用 HttpClientFactory 或静态 HttpClient,也会在 Azure WebApps 上发生 [SNAT 端口耗尽]。 唯一的解决方案似乎是扩展 WebApps 或使用 ASE。
所以我会执行建议的编码,然后横向扩展。