Python Асинхронный обратный DNS-запрос

Я хочу выполнить большое количество обратных DNS-запросов за небольшой промежуток времени. В настоящее время я реализовал асинхронный поиск с использованием пула потоков socket.gethostbyaddr и concurrent.futures, но до сих пор не вижу желаемой производительности. Например, выполнение скрипта на 2500 IP-адресах заняло около 22 минут.

Мне было интересно, есть ли более быстрый способ сделать это, не прибегая к чему-то вроде adns-python. Я нашел этот http://blog.schmichael.com/2007/09/18/a-lesson-on-python-dns-and-threads/, который предоставил дополнительную информацию.

Фрагмент кода:

ips = [...]
with concurrent.futures.ThreadPoolExecutor(max_workers = 16) as pool:
    list(pool.map(get_hostname_from_ip, ips))
def get_hostname_from_ip(ip):
    try:
        return socket.gethostbyaddr(ip)[0]
    except:
        return ""

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

socket.setdefaulttimeout(2.0)

но, похоже, это не имеет никакого эффекта.


person Wert    schedule 07.06.2014    source источник


Ответы (3)


Я обнаружил, что моя основная проблема заключалась в том, что IP-адреса не разрешались, и, следовательно, сокеты не подчинялись установленным тайм-аутам и терпели неудачу через 30 секунд. См. раздел ошибка тайм-аута Python 2.6 urlib2.

adns-python не использовался из-за отсутствия поддержки IPv6 (без исправлений).

После поиска я нашел это: Обратный поиск DNS с помощью dnspython и реализовал аналогичную версию в моем коде (его код также использует необязательный пул потоков и реализует тайм-аут).

В конце концов я использовал dnspython с пулом потоков concurrent.futures для асинхронного обратного поиска DNS (см. -hosting">Python: обратный поиск DNS на общем хостинге и Dnspython: настройка запроса время ожидания/время жизни). С тайм-аутом в 1 секунду это сократило время выполнения с 22 минут до 16 секунд на 2500 IP-адресах. Большая разница, вероятно, может быть связана с глобальной блокировкой интерпретатора на сокетах и ​​30-секундных тайм-аутах.

Фрагмент кода:

import concurrent.futures
from dns import resolver, reversename
dns_resolver = resolver.Resolver()
dns_resolver.timeout = 1
dns_resolver.lifetime = 1
ips = [...]
results = []

with concurrent.futures.ThreadPoolExecutor(max_workers = 16) as pool:
    results = list(pool.map(get_hostname_from_ip, ips))

def get_hostname_from_ip(ip):
    try:
        reverse_name = reversename.from_address(ip)
        return dns_resolver.query(reverse_name, "PTR")[0].to_text()[:-1]
    except:
        return ""
person Wert    schedule 08.06.2014

Из-за глобальной блокировки интерпретатора следует использовать ProcessPoolExecutor вместо. https://docs.python.org/dev/library/concurrent.futures.html#processpoolexecutor

person Addison    schedule 07.06.2014
comment
У меня очень ограниченное понимание Python GIL, но поскольку большая часть времени тратится на блокировку ввода-вывода, не должно ли это иметь значения? Изменить: я использую аналогичный код для асинхронных HTTP-запросов, и, похоже, это помогает. - person Wert; 07.06.2014
comment
Вы разобрались, в чем дело? - person Addison; 07.06.2014
comment
Да, это были IP-адреса, которые не удалось разрешить. Сокеты игнорировали свой тайм-аут, а некоторым требовалось до 30 секунд, прежде чем произошел сбой. Просматривая, я нашел это: stackoverflow.com/ вопросов/14127115/, который объясняет проблему. Я опубликую свое решение. - person Wert; 08.06.2014

пожалуйста, используйте асинхронный DNS, все остальное приведет к очень низкой производительности.

person lenik    schedule 07.06.2014
comment
Я обнаружил, что мой ответ с использованием dnspython дал вполне разумную производительность. Интересно, как будет выглядеть adns-python? - person Wert; 08.06.2014