【问题标题】:How do I create HttpClient on ASP.Net Core 2.0 to prevent SNAT port exhaustion on Azure WebApps如何在 ASP.Net Core 2.0 上创建 HttpClient 以防止 Azure WebApps 上的 SNAT 端口耗尽
【发布时间】:2020-11-17 05:39:07
【问题描述】:

为了重用 HttpClient,我在 ASP.Net Core 2.0 上创建了一个类似这样的实用程序类。

    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 门户上的诊断和解决问题刀片显示 SNAT PORT Exhaustion 发生。

当许多并发请求发生时,这段代码是否会导致此问题?

如果是这样,我如何在 ASP.Net Core 2.0(没有 HttpClientFactory)上创建 HttpClient。

[环境]

  • ASP.Net Core 2.0
  • Azure WebApps

【问题讨论】:

  • 升级到 2.1+ 并然后使用IHttpClientFactory
  • @TheGeneral 抱歉..我们的项目无法轻松升级。所以我想知道.net core 2.0 的解决方案。

标签: c# azure azure-web-app-service httpclient asp.net-core-2.0


【解决方案1】:

请确保您执行以下步骤:

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

【讨论】:

  • 谢谢。第二个意思是我不需要为每个url创建一个HttpClient?
  • 好的。我会试试的。但是我从 MS 那里得到了另一个答案。
  • 这是什么?这两种方法你都试过了吗?
  • 是的。我尝试了这两种方法。但就我而言,这些方法不足以解决问题。 Azure WebApps 不支持超过 128 的出站 SNAT 端口,因此如果我尝试向同一个 URL 发送多个请求,无论我在代码中做什么都会出现问题。
  • 你没有提到你没有使用缩放。对于高负载系统,您应该始终使用缩放。否则,它会抛出这些错误,因为单个系统有自己的硬件限制。
【解决方案2】:

感谢所有答案和 cmets。 但我得到了MS的回答。 他们说,如果对同一个 url 有超过 128 个请求,即使我使用 HttpClientFactory 或静态 HttpClient,Azure WebApps 上也会发生 [SNAT Port Exhaustion]。 唯一的解决方案似乎是扩展 WebApp 或使用 ASE。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-25
    • 2018-06-25
    • 2019-05-06
    • 2022-01-03
    • 1970-01-01
    相关资源
    最近更新 更多