Блокирование методов метода Dns.BeginGetHost...

Итак, я хочу сделать много запросов DNS.

Я создаю (тысячи) Заданий из пары async Begin/EndGetHostEntry:

var lookupTask = Task.Factory.FromAsync
   ( Dns.BeginGetHostEntry,
     (Func<IAsyncResult, IPHostEntry>) Dns.EndGetHostEntry,
     "google.com", 
     null
   )

затем Task.WaitAll для завершения всего. Я вижу, что число потоков ThreadPool резко возрастает в ответ на мои запросы. Если я нажимаю ThreadPool minThreads до 500, рабочая нагрузка потребляется значительно быстрее. Все это указывает на блокировку асинхронной реализации Dns.

Если я заменил Dns на управляемый клиент Dns, я могу использовать одну и ту же рабочую нагрузку только с 1 или 2 потоками в ThreadPool с процессором практически бездействия.

Дело в том, что реализация Dns является абсолютно явной для многих сетевых API (HttpWebRequest, WebClient, HttpClient), и все они, похоже, подвержены этой проблеме. Если я разрешу DNS с помощью сторонней библиотеки и сделаю HTTP-запросы с использованием IP-адреса в качестве хоста в uri, измените заголовок Host, чтобы исправить запрос, я получаю взрывную производительность по сравнению с чем-либо, связанным с System.Net.Dns.

Что здесь происходит? Я что-то пропустил или действительно ли это реализация System.Net.Dns, что плохо?

Ответы

Ответ 1

System.Net.Dns использует функцию windows gethostbyname для DNS-запросов и вообще не имеет асинхронных функций. Функция BeginGetHostEntry в основном представляет собой оболочку для синхронного вызова GetHostEntry в пуле потоков.

В прошлый раз у меня была такая же проблема с медленными/синхронными поисками DNS. В конечном итоге я просто использовал большой ThreadPool для выполнения этой работы, поскольку ни один встроенный Windows или .net связанная с DNS функция не поддерживает правильное (параллельное) асинхронное выполнение.

Ответ 2

Это может быть не полный ответ, но:

DNS, разрешающий внутри .net, открывает соединение с dns, задает вопрос и закрывается. Примеры для управляемого клиента dns, с которым вы связаны, ясно показывают, что эта библиотека создает соединение, а затем, пока она остается открытой, вы можете задать много вопросов, например, делать

nslookup -

>hostname1
>hostname2
...

в разделе dos/unix

Часто при открытии это может занять некоторое время, совершив несколько вызовов к уже открытому соединению, вам не нужно делать реверсирование на себе и себе, а также на весь другой мусор, когда соединение с сервером DNS выполняется, когда оно сначала соединяет. Например: если первый DNS-сервер в моем списке занят, моя машина часто требует времени, чтобы разрешить доступ к другому серверу, который был доступен, в результате, если вы столкнулись с этим каждый раз, когда вы просматривали под .net библиотеки, вы увидите долгое ожидание, и потребуется столько потоков, и, конечно же, увеличить загрузку процессора, в то время как на самом деле это не так много.

Реализация не является "плохим", она просто не предназначена для нескольких пакетных заданий. Если нет звонков, я тоже пропустил.

Ответ 3

У меня нет набора данных из 1000 URL-адресов для проверки вашего кода, и повторное обращение к одному и тому же URL-адресу должно привести к удалению кеша (а не к DNS-серверу для моей сети). Поэтому, пожалуйста, прокомментируйте успех/неудачу, если вы проверите это.

Моя рекомендация по тестированию этого (или любой другой гипотезы) заключалась бы в создании тестового набора данных из 1000 URL-адресов, которые вы хотите разрешить, и их номера. Затем настройте некоторые протоколирования (то есть: log4net или аналогичные) и напишите инструкцию, когда заканчивается каждая задача разрешения DNS, включая индекс завершенной задачи. Я считаю, что вы увидите, что эти 1000 задач выполняются несколько синхронно. Или, по крайней мере, в группах по 2-8 асинхронных результатов за раз, где все группы из 2-8 являются синхронными.

Причиной этого является управление соединением. Внутренне .Net будет разрешать так много одновременных подключений к одной и той же конечной точке. Если вы откроете 1000 соединений с вашим DNS-сервером, только несколько из них будут успешными одновременно. Остальным нужно подождать, пока некоторые более ранние соединения не будут закрыты, прежде чем они смогут установить другое соединение с той же конечной точкой (ваш DNS-сервер).

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

Вы можете открыть это ограничение с помощью этой конфигурации:

<configuration>
  <system.net>
    <connectionManagement>
      <add address="*" maxconnection="100"/>
    </connectionManagement>
  </system.net>
</configuration>

MSDN для System.Net.ConnectionManagement

Вы можете указать конкретный адрес конечной точки (URL или IP) и максимальные соединения с этим адресом. Некоторые приложения для тестирования нагрузки будут использовать только подстановочный знак * и 65535, чтобы открыть его для всего.

Я подозреваю, что реализация управляемого DNS либо повторно использует одно и то же соединение с DNS-сервером, либо имеет некоторую внутреннюю конфигурацию, как указано выше.

Некоторые дополнительные сведения, которые вы можете включить в свой вопрос, - это вопрос о том, запрашиваете ли вы локальный DNS-сервер в той же физической сети или DNS-сервер от вашего локального интернет-провайдера или публичный DNS-сервер, например OpenDNS. Конфигурация этих конкретных DNS-серверов может налагать там свои ограничения (провайдеры могут оценивать лимит, я не знаю).

Ответ 4

Обычное использование обычно не имеет лучшей производительности, когда поиск dns асинхронен, так как код нуждается в ответе, чтобы продолжить работу. Параллельно ничего не получается. Только когда вы только хотите искать несколько DNS'ов, это становится реальной проблемой.

Для чего это немного медленнее, и повышение производительности проверяет это SO Вопрос и ответ (ы) GetHostEntry очень медленный