Как мне создать HttpClient на ASP.Net Ядро 2.0 для предотвращения исчерпания портов SNAT в веб-приложениях Azure

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

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

Вопрос:

Для повторного использования HttpClient я создал класс утилиты, подобный этому, на ASP.Net Ядро 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 amp;amp; 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.

Является ли этот код причиной этой проблемы, когда возникло много одновременных запросов?

Если да, то как мне создать HttpClient на ASP.Net Ядро 2.0 (без HttpClientFactory).

[Среда]

  • ASP.Net Ядро 2.0
  • Веб-приложения Azure

Комментарии:

1. Обновите до версии 2.1 , а затем используйте IHttpClientFactory .

2. @TheGeneral Извините .. наш проект не может легко обновиться. Итак, я хочу знать решения на .net core 2.0.

Ответ №1:

Пожалуйста, убедитесь, что вы выполнили следующие действия:

  • Вводится HttpClientUtility как одноэлементный.
  • Не усложняйте — просто используйте new HttpClient() в качестве статической переменной. GetHostCacheKeyFromUri не требуется.

Комментарии:

1. Спасибо. Второе означает, что мне не нужно создавать HttpClient для каждого URL-адреса?

2. ОК. Я бы попробовал. Но я получил другой ответ от MS.

3. Что это? Вы пробовали оба эти метода?

4. Да. Я пробовал оба метода. Но в моем случае этих методов было недостаточно для решения проблемы. Azure WebApps не поддерживает исходящие порты SNAT более 128, поэтому, если я попытаюсь отправить много запросов на один и тот же URL-адрес, ошибка произойдет независимо от того, что я делаю в своих кодах.

5. Вы не упомянули, что не используете масштабирование. Вы всегда должны использовать масштабирование для систем с высокой нагрузкой. В противном случае он будет выдавать эти ошибки, поскольку отдельная система имеет собственные аппаратные ограничения.

Ответ №2:

Спасибо за все ответы и комментарии. Но я получил ответ от MS. Они говорят, что если для одного и того же URL-адреса поступает много запросов более 128, в веб-приложениях Azure произойдет [Исчерпание порта SNAT], даже если я использую HttpClientFactory или статический HttpClient. Похоже, единственным решением является масштабирование веб-приложений или использование ASE.

Итак, я бы выполнил предложенное кодирование и масштабировал.