пинг ~ 100 000 серверов, многопоточность или многопроцессорность лучше? - PullRequest
5 голосов
/ 30 января 2020

Я создал простой скрипт, который перебирает список серверов, которые мне нужны как для ping, так и для nslookup. Проблема в том, что пинг может занять некоторое время, особенно пинговать больше сервера, чем секунд в день.

Я довольно новичок в программировании, и я понимаю, что многопроцессорная обработка или многопоточность могут помочь ускорить выполнение моей работы. ,

Я планирую взять мой список серверов и либо 1. Разбить его на списки четного размера с количеством списков, соответствующих потокам / процессам, либо 2. Если один из этих параметров поддерживает это, l oop через единственный список, передавая каждое новое имя сервера потоку или процессу после того, как он завершает свой предыдущий ping и nslookup. Это предпочтительнее, поскольку это гарантирует, что я трачу наименьшее количество времени, поскольку, как будто список 1 имеет 200 автономных серверов, а список 6 имеет 2000, ему нужно будет дождаться, пока процесс с использованием списка 6 завершится sh, даже если все остальные будут бесплатно в этот момент.

  1. Какой из них лучше для этой задачи и почему?

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

фрагмент кода, хотя и довольно простой прямо сейчас

import subprocess
import time
server_file = open(r"myfilepath", "r")
initial_time = time.time()
for i in range(1000):
    print(server_file.readline()[0:-1]+ ' '+str(subprocess.run('ping '+server_file.readline()[0:-1]).returncode)) #This returns a string with the server name, and return code,
print(time.time()-initial_time)

Проблема возникает из-за того, что сбойный эхо-запрос занимает в среднем более 3 секунд каждый , Кроме того, я знаю, что не помещая оператор печати, это ускорит его, но я хотел контролировать его для небольшого случая. Я что-то пингую на 100 000 серверов, и это нужно будет делать регулярно, и список будет продолжать расти

Ответы [ 4 ]

4 голосов
/ 31 января 2020

Для лучшей производительности вы не хотите ни; с 100 000 активных заданий лучше всего использовать асинхронную обработку, в одном или, возможно, в нескольких потоках или процессах (но не превышая количество доступных ядер).

С помощью асинхронного ввода-вывода c многие сетевые задачи могут выполняться в одном потоке, легко достигая скорости 100 000 и более благодаря экономии на переключении контекста (то есть теоретически вы можете пропинговать 100 000 машин за 1 секунду).

Python поддерживает асинхронный ввод / вывод через asyncio ( здесь хорошее введение в асинхронные и сопрограммы).

Это также важно для не зависит от внешнего процесса, такого как ping, потому что порождение нового процесса является очень дорогостоящей операцией.

aioping является примером собственного Python пинга, выполняемого с использованием asyncio (обратите внимание, что ping на самом деле является парой пакетов ICMP запроса / ответа). Должно быть легко настроить его для одновременного выполнения нескольких пингов.

4 голосов
/ 30 января 2020

TLDR; MultiThreading - решение для вас - модуль потоков использует потоки, а модуль многопроцессорных - процессы. Разница в том, что потоки работают в одном и том же пространстве памяти, а процессы имеют отдельную память.

Что касается вопроса 1-

Для задач ввода-вывода, таких как запрос базы данных или загрузка веб-страницы, ЦП просто ничего не делая, но ожидая ответа, и это пустая трата ресурсов, поэтому многопоточность - это ответ (:

Что касается вопроса 2-

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

1 голос
/ 31 января 2020

+ 1 к ответу Йоэля. Потоки - определенно путь к go.

Мне было любопытно, сколько времени на самом деле это сэкономит, поэтому просто написал скрипт для проверки связи с Google снова и снова:

import subprocess  
import os

import threading
from multiprocessing import Process

FNULL = open(os.devnull, 'w')
def ping(host):
    command = ['ping', '-c', '1', host]

    return subprocess.call(command, stdout=FNULL, stderr=subprocess.STDOUT) == 0

def ping_hosts(hosts, i):
    for h in hosts:
        ping(h)
        # print "%d: %s" % (i, str(ping(h)))

hosts = ["www.google.com"] * 1000
num_threads = 5

for i in range(num_threads):
    ping_hosts(hosts, i)

#for i in range(num_threads):
#    p = Process(target=ping_hosts, args=(hosts, i))
#    p.start()

#for i in range(num_threads):
#    t = threading.Thread(target=ping_hosts, args=(hosts, i))
#    t.start()

Результаты :

# no threading no multiprocessing
$ time python ping_hosts.py  # 5000 in a row
real    0m34.657s
user    0m5.817s
sys 0m11.436s

# multiprocessing
$ time python ping_hosts.py
real    0m8.119s
user    0m6.021s
sys 0m16.365s

# threading
$ time python ping_hosts.py
real    0m8.392s
user    0m7.453s
sys 0m16.376s

Очевидно, что в тесте есть fl aws, но совершенно очевидно, что вы получаете значительное повышение от добавления любой библиотеки. Обратите внимание, что экономия примерно одинакова. Но, как сказал Йоэль, поскольку большую часть времени вы просто проводите большую часть времени в ожидании потоков, это путь к go. Достаточно просто, чтобы просто записать имена хостов в очередь и создать поток рабочих потоков.

0 голосов
/ 31 января 2020

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

import subprocess
import threading
raw_list = []
def ping(host):
    raw_list.append(host+ ' '+ str((subprocess.run('ping '+host).returncode)))
with open(r'RedactedFilePath', "r") as server_list_file:
    hosts = server_list_file.read()
    hosts_list =hosts.split('\n')
num_threads = 75
number = 0
while number< len(hosts_list):
    print(number)
    for i in range(num_threads):
        t = threading.Thread(target=ping, args=(hosts_list[number+i],))
        t.start()
    t.join()
    number = number +75

. Возможно, существует еще один способ ping для Pythoni c, который может ускорить его, поскольку он не будет запускать подпроцессы для каждого

. настройка правильного количества потоков зависит от машины и, более того, зависит от того, что вы делаете с потоками.

И, конечно, должен быть оператор try, чтобы остановить ошибки, когда список серверов не делится напрямую. по количеству потоков.

Также, для удаления вашего разделителя должна использоваться функция split, в моем случае мой список серверов находится в текстовом файле, каждый из которых находится на новой строке, но csv будет .split (',') и tsv будут ('\ t') et c

...