День 1135. #ЗаметкиНаПолях
Избегаем Проблем с DNS в HttpClient
HttpClient позволяет отправлять HTTP-запросы. Подразумевается, что он создаётся однократно и повторно используется на протяжении всего жизненного цикла приложения. HttpClient имеет пул для повторного использования соединений. Если вы отправите несколько запросов на один и тот же хост, они будут повторно использовать одно и то же соединение. Таким образом, приложение экономит доступные ему сокеты. Кроме того, это повышает производительность приложения, избегая рукопожатий TCP и TLS для каждого запроса к одному и тому же хосту.
Сохранение соединений открытыми - хорошо с точки зрения производительности, но вы должны быть осторожны, чтобы не хранить устаревшие соединения. Если срок жизни DNS истёк, хост может изменить свой IP-адрес. В этом случае открытые соединения должны быть закрыты. HttpClient не делает этого автоматически, поскольку ему ничего не известно о сроке жизни DNS.
Когда происходит изменение DNS?
- При сине-зелёном развёртывании (в Azure, когда вы выполняете развёртывание в промежуточном слоте, а затем переключаете рабочий/промежуточный слоты).
- При изменении настроек в диспетчере трафика Azure.
- В случае отказа основного сервера и перехода на бекап и т.п.
Чтобы исправить эту проблему, вы можете указать время ожидания для автоматического закрытия соединения. SocketsHttpHandler используется для настройки поведения HttpClient и его пула соединений. Необходимо настроить 2 свойства:
- PooledConnectionIdleTimeout,
- PooledConnectionLifetime.
Эти свойства заставляют HttpClient закрыть соединение через определённое время. Таким образом, следующий запрос к тому же хосту должен будет открыть новое соединение и использовать новый DNS или другие изменения в сети.
По умолчанию простаивающие соединения закрываются через 1 минуту. Однако активные соединения никогда не закрываются. Вы должны явно установить для PooledConnectionLifetime желаемое значение.
using System.Threading;
using System.Net.Http;
using var handler = new SocketsHttpHandler()
{
// Максимальное время простоя соединения в пуле. Если в течение этого времени нет запросов, соединение разрывается. По умолчанию: 1 минута
PooledConnectionIdleTimeout =
TimeSpan.FromMinutes(1),
// Максимальное время жизни соединения в пуле, независимо от активности. Периодически соединение пересоздаётся для обновления DNS или других сетевых изменений. По умолчанию: никогда.
PooledConnectionLifetime =
TimeSpan.FromMinutes(1)
};
using var client = new HttpClient(handler);
var timer = new PeriodicTimer(
TimeSpan.FromSeconds(10));
while (await timer.WaitForNextTickAsync())
{
_ = await client
.GetStringAsync("https://www.google.com");
}
Замечание: Другой способ избежать проблем с изменённым DNS – использовать IHttpClientFactory:
services
.AddHttpClient<IMyService, MyService>()
.SetHandlerLifetime(TimeSpan.FromMinutes(1));
Источник: www.meziantou.net/avoid-d…tnet.htm