Ошибки тайм-аута с несколькими экземплярами HttpClientHandler

#c# #.net #timeout #.net-framework-4.8 #httpclienthandler

#c# #.net #тайм-аут #.net-4.8 #httpclienthandler

Вопрос:

Мы написали приложение, которое подключается к внешнему API. У вас должен быть токен для связи с этим api. Токен должен быть уникальным для каждого пользователя. Чтобы получить токен, нам нужно вызвать конечную точку с клиентским сертификатом, который принадлежит этому пользователю.

Мы реализовали это, используя 1 статический экземпляр HttpClientHandler для всех запросов API. Каждый запрос настраивает HttpClient с указанным статическим обработчиком; и только клиент будет удален, обработчик будет сохранен в рабочем состоянии. Однако, чтобы получить токен; нам нужно добавить сертификат в обработчик. Поэтому при получении токена; мы используем новый экземпляр HttpClientHandler, специфичный для получения токена. Как только токен получен, мы удаляем клиент и экземпляр обработчика.

Теперь многие вызовы завершаются ошибкой со следующей ошибкой:

Попытка подключения завершилась неудачей, поскольку подключенная сторона не ответила должным образом по истечении определенного периода времени, или сбой установленного соединения, поскольку подключенный хост не смог ответить [ip]: 443

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

Другая сторона также исследовала эту проблему; и последний вывод, который они нам дали: заключается в том, что в конце запроса мы сохраняем соединение активным; поэтому порт зарезервирован для этого соединения. Однако иногда с нашей стороны пытаются настроить новый запрос (я полагаю, новое соединение) с тем же портом. Брандмауэр блокирует / удаляет этот новый запрос; потому что этот порт уже используется. И именно поэтому мы заканчиваем с этой ошибкой тайм-аута.

Что ж, это имеет смысл; у нас нет контроля над портами, которые использует соединение. Но почему это происходит? У меня есть теория, что проблема заключается в сочетании статического обработчика (который поддерживает 10 соединений parallels и сохраняет эти соединения неактивными, но активными) и второго (или более) других обработчиков, которые могут думать, что порт свободен (поскольку на этом порту нет действий), может попытатьсяустановите соединение с этим портом. Но это всего лишь теория; я не могу найти доказательства этого.

Есть ли кто-нибудь, кто может подтвердить эту теорию; или, может быть, дать другое объяснение, почему может возникнуть эта проблема? И / или есть ли решение / обходной путь для этой проблемы?

Ответ №1:

Хо, я думаю, вы столкнулись с проблемой, называемой исчерпанием сокетов. Хотя вы dispose() используете HTTP-клиент, используемый сокет все равно будет зарезервирован на определенное время. Чтобы предотвратить это, вам нужен механизм, позволяющий повторно использовать HttpClients и, следовательно, базовые сокеты, которые они используют.

Рекомендуемый способ — использовать HttpClientFactory . Эта фабрика создаст пул с HttpClients и предоставит вам один по требованию, сохраняя при этом пул клиентов для вас.

Дополнительную информацию можно найти на веб-сайте Microsoft Docs: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests

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

1. Спасибо; однако, насколько я понял, это исчерпание сокета; у вас должна быть эта ошибка в журнале событий Windows. И не должно ли это быть проблемой при большом количестве подключений? Потому что мы повторно используем обработчик почти для каждого вызова; только для получения токена нам нужен другой экземпляр HttpClientHandler. Netstat указывает максимум 20 подключений к api. Но, возможно, я ошибся в этом; и, чтобы быть уверенным; я снова повторю это.