如何在 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。

[环境]

请务必执行以下步骤:

  • HttpClientUtility 作为单例注入。
  • 不要复杂化 - 只需将 new HttpClient() 用作静态变量即可。 GetHostCacheKeyFromUri 不是必需的。

感谢所有的回答和评论。 但我得到了MS的答复。 他们说,如果同一 url 有许多超过 128 的请求,即使我使用 HttpClientFactory 或静态 HttpClient,也会在 Azure WebApps 上发生 [SNAT 端口耗尽]。 唯一的解决方案似乎是扩展 WebApps 或使用 ASE。

所以我会执行建议的编码,然后横向扩展。