ping ~ 100,000 台服务器,多线程或多处理哪个更好?
pinging ~ 100,000 servers, is multithreading or multiprocessing better?
我创建了一个简单的脚本,它遍历我需要 ping 和 nslookup 的服务器列表。问题是,ping 可能需要一些时间,尤其是 ping 的服务器比一天中的秒数还要多。
我对编程还很陌生,我知道多处理或多线程可能是使我的工作 运行 更快的解决方案。
我的计划是采用我的服务器列表,并且 1. 将其分成大小均匀的列表,列表的数量与线程/进程相匹配,或者 2. 如果这些选项之一支持它,循环遍历单个list 在完成其先前的 ping 和 nslookup 后将每个新服务器名称传递给线程或进程。这是更可取的,因为它确保我花费最少的时间,如果列表 1 有 200 个离线服务器而列表 6 有 2000 个,它将需要等待使用列表 6 的过程完成,即使所有其他人在那个时候都是免费的点.
哪个更适合这项任务,为什么?
如果可能,我将如何确保每个线程或进程具有基本相同的运行时间
代码片段,尽管现在相当简单
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)
出现此问题是因为每次 ping 失败平均需要超过 3 秒。我也知道不放置打印语句会使它更快,但我想在一个小案例中对其进行监控。我正在 ping 100,000 台服务器的结果,这需要定期完成,并且列表会不断增长
TLDR; 多线程 是适合您的解决方案-
线程模块使用线程,多处理模块使用进程。
区别在于线程运行在同一个内存space,而进程有单独的内存。
关于问题1-
对于 IO 任务,例如查询数据库或加载网页,CPU 除了等待答案什么都不做,这是一种资源浪费,因此多线程就是答案 (:
关于问题2-
您只需创建线程池即可同时管理 运行 个线程,而无需费力。
+1 到 Yoel 的回答。线程绝对是要走的路。
我很好奇它实际上会节省多少时间,所以只写了一个脚本来一遍又一遍地 ping 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
显然测试中存在缺陷,但很明显,添加任一库都可以显着提升。请注意,节省的费用大致相同。但是,正如 Yoel 所说,因为大多数时候你只是花大部分时间等待线程是要走的路。很简单,只需将您的主机名转储到队列中,并让工作线程池在其中搅动。
为了获得最佳性能,您两者都不想要;有 100,000 个活动作业时,最好使用 异步 处理,在 单个 或可能的少数线程或进程中(但不超过可用的数量核心)。
使用异步 I/O 可以在单个线程中执行许多网络任务,由于节省了上下文切换,很容易达到 100,000 或更高的速率(也就是说,理论上您可以在 1 秒内 ping 100,000 台机器) .
Python 通过 asyncio
(here's 对 asyncio 和协程的一个很好的介绍支持异步 I/O。
不要依赖像 ping
这样的外部进程也很重要,因为生成新进程是一项非常昂贵的操作。
aioping
是使用 asyncio
完成的本机 Python ping 示例(注意 ping 实际上是一对 ICMP request/reply 数据包)。让它同时执行多个 ping 应该很容易。
多线程可以 运行 比多处理更快,因为 ping 不是 CPU 密集的。可以运行 大量的线程。下面是我的工作代码
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
可能有更 pythonic 的 ping 方式,这可能会使它更快,因为它不会为每个单独的进程启动子进程
此外,调整适当的线程数取决于机器,更取决于您对线程的处理方式。
当然,当服务器列表不能直接被线程数整除时,应该有一个try语句来阻止错误。
此外,拆分功能应该用于删除分隔符,在我的例子中,我的服务器列表在一个文本文件中,每个文件都在一个新行中,但 csv 将是 .split(',')而 tsv 将是 ('\t') etc
我创建了一个简单的脚本,它遍历我需要 ping 和 nslookup 的服务器列表。问题是,ping 可能需要一些时间,尤其是 ping 的服务器比一天中的秒数还要多。
我对编程还很陌生,我知道多处理或多线程可能是使我的工作 运行 更快的解决方案。
我的计划是采用我的服务器列表,并且 1. 将其分成大小均匀的列表,列表的数量与线程/进程相匹配,或者 2. 如果这些选项之一支持它,循环遍历单个list 在完成其先前的 ping 和 nslookup 后将每个新服务器名称传递给线程或进程。这是更可取的,因为它确保我花费最少的时间,如果列表 1 有 200 个离线服务器而列表 6 有 2000 个,它将需要等待使用列表 6 的过程完成,即使所有其他人在那个时候都是免费的点.
哪个更适合这项任务,为什么?
如果可能,我将如何确保每个线程或进程具有基本相同的运行时间
代码片段,尽管现在相当简单
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)
出现此问题是因为每次 ping 失败平均需要超过 3 秒。我也知道不放置打印语句会使它更快,但我想在一个小案例中对其进行监控。我正在 ping 100,000 台服务器的结果,这需要定期完成,并且列表会不断增长
TLDR; 多线程 是适合您的解决方案- 线程模块使用线程,多处理模块使用进程。 区别在于线程运行在同一个内存space,而进程有单独的内存。
关于问题1-
对于 IO 任务,例如查询数据库或加载网页,CPU 除了等待答案什么都不做,这是一种资源浪费,因此多线程就是答案 (:
关于问题2-
您只需创建线程池即可同时管理 运行 个线程,而无需费力。
+1 到 Yoel 的回答。线程绝对是要走的路。
我很好奇它实际上会节省多少时间,所以只写了一个脚本来一遍又一遍地 ping 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
显然测试中存在缺陷,但很明显,添加任一库都可以显着提升。请注意,节省的费用大致相同。但是,正如 Yoel 所说,因为大多数时候你只是花大部分时间等待线程是要走的路。很简单,只需将您的主机名转储到队列中,并让工作线程池在其中搅动。
为了获得最佳性能,您两者都不想要;有 100,000 个活动作业时,最好使用 异步 处理,在 单个 或可能的少数线程或进程中(但不超过可用的数量核心)。
使用异步 I/O 可以在单个线程中执行许多网络任务,由于节省了上下文切换,很容易达到 100,000 或更高的速率(也就是说,理论上您可以在 1 秒内 ping 100,000 台机器) .
Python 通过 asyncio
(here's 对 asyncio 和协程的一个很好的介绍支持异步 I/O。
不要依赖像 ping
这样的外部进程也很重要,因为生成新进程是一项非常昂贵的操作。
aioping
是使用 asyncio
完成的本机 Python ping 示例(注意 ping 实际上是一对 ICMP request/reply 数据包)。让它同时执行多个 ping 应该很容易。
多线程可以 运行 比多处理更快,因为 ping 不是 CPU 密集的。可以运行 大量的线程。下面是我的工作代码
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
可能有更 pythonic 的 ping 方式,这可能会使它更快,因为它不会为每个单独的进程启动子进程
此外,调整适当的线程数取决于机器,更取决于您对线程的处理方式。
当然,当服务器列表不能直接被线程数整除时,应该有一个try语句来阻止错误。
此外,拆分功能应该用于删除分隔符,在我的例子中,我的服务器列表在一个文本文件中,每个文件都在一个新行中,但 csv 将是 .split(',')而 tsv 将是 ('\t') etc