Когда вы думаете, что скрипт медленный, вы должны измерить , что заставляет его работать медленно.Например, line-profiler .
Мой думаю, будет означать, что в этом случае это подпроцесс, который занимает большую часть времени.В идеале вы хотите снять накладные расходы на запуск процесса для каждого пинга.Поэтому вместо вызова программы ping
установите модуль pythonping .Это позволяет вам выполнить эхо-запрос ICMP от Python.Обратите внимание, что для этого используются необработанные сокеты, поэтому в зависимости от операционной системы вам может потребоваться запустить сценарий от имени пользователя root или настроить его на использование необработанных сокетов.Использование этого модуля устраняет издержки использования подпроцесса.
Далее, когда ваш скрипт делает это, он в основном ожидает ответа от сети.Поэтому мы используем concurrent.futures.ThreadPoolExecutor
для параллельного запуска более одного пинга
import concurrent.futures as cf
import os
from pythonping import ping
def pingworker(address):
rv = ping(address)
if rv.success():
return address, True
return address,False
with cf.ThreadPoolExecutor() as tp:
res = tp.map(pingworker, list_of_addresses)
После этого res
представляет собой список из двух кортежей, каждый из которых содержит адрес и логическое значение, если он завершился неудачно или успешно.
Обратите внимание, что начиная с Python 3.5 и далее ThreadPoolExecutor
запускает 5 * N потоков, где N - количество ядер на вашем компьютере.Таким образом, для четырехъядерной машины одновременно будет выполняться 20 вызовов ping.Вы можете поэкспериментировать с параметром max_workers
при создании ThreadPoolExecutor
, но в определенный момент вы будете насыщать свое сетевое соединение с помощью пинг-вызовов.
Редактировать
Функция pythonping.ping
требует IP-адрес , а не имя.Таким образом, вы должны сделать поиск имени в первую очередь.К счастью, это встроено в модуль socket
.Вы можете использовать, например, socket.gethostbyname_ex
для поиска IPv4-адреса.Или socket.getaddrinfo
для получения адресов IPv4 и IPv6.
Если у вас есть список имен, предполагая, что вы используете IPv4, вы можете изменить работника следующим образом:
import concurrent.futures as cf
import socket
import os
from pythonping import ping
def pingworker(name):
"""
Ping a hostname.
Arguments:
name (str): hostname:
Returns:
a 3-tuple (hostname, IP-address, ping-result)
where hostname and IP-address are strings and
ping-result is a bool.
"""
try:
_, _, IPs = socket.gethostbyname_ex(name)
address = IPs[0]
except socket.gaierror:
return name, None, False # Name lookup failed.
rv = ping(address)
if rv.success():
return name, address, True
return name, address, False # Host doesn't respond.
with cf.ThreadPoolExecutor() as tp:
res = tp.map(pingworker, list_of_names)
Я также изменил рабочую функцию, чтобы она также возвращала IP-адрес.Таким образом, вы можете отличить хост, который не возвращает эхо-запросы, от хоста, имя которого не может быть разрешено.