如何 ping 数千台主机并尽快显示状态
How do I ping thousands of hosts and show the status as quick as possible
我需要在 rails 中执行一项任务,该任务会 ping 大量主机并检查其是否可达。结果必须尽快显示,主机数量可以扩展到大约 10k 甚至更多。
到目前为止,我已经尝试使用线程池并花费了很长时间。
work_q = Queue.new
@hosts.each{|x| work_q.push x }
workers = (0...200).map do
Thread.new do
begin
while host = work_q.pop(true)
ping_count = 1
server = host.address
result = `ping -q -c #{ping_count} #{server}`
if ($?.exitstatus == 0)
@res[host.hostname] = "up"
else
@res[host.hostname] = "down"
end
end
rescue ThreadError
end
end
end;
我也尝试过使用sidekiq将其实现为异步任务。这是我想到的实施方式。
1.将host ip传入sidekiq中的job queue,找到最后一个job id/worker id
2.检查最后一个worker id的状态并以某种方式持久化。(不确定如何以更好的方式持久化worker id)
3. 安排任务检查最后一个工人的完成情况。一旦最后一个工作人员完成,重新启动 sidekiq 并再次 ping 主机。
4. 通过这种方式,您可以根据最小时间间隔内的 ping 结果获得所有主机的最新状态(启动或关闭)。
5. 每当用户点击查看主机的状态时,显示结果,并且是最新的。
任何人都可以提供任何其他想法,他们可以想出更好的方法。
感谢您的帮助。
我从来没有用过它,但经过一些搜索发现了一个名为 PacketFu 的库,它允许您从 Ruby 发送 ICMP 数据包(它依赖于 libpcap)。它还允许您嗅探来自 Ruby.
的数据包
所以,这是一个想法:
与其为每个要 ping 的主机创建一个新的 ping
进程,不如使用 PacketFu 直接从主 Ruby 进程向每个主机发送 ICMP 回应请求。同时,在另一个线程中,使用 PacketFu 的 Capture
class 嗅探数据包并将源 IP 与您尝试 ping 的地址进行匹配。
您必须确保以线程安全的方式访问所有程序状态,否则事情会变得糟糕!如果您还有一个 Web 服务器或面向用户的界面的进程中的东西 运行,它也必须发挥良好和安全的作用,并且不要在没有锁定的情况下将其小手伸入程序状态!
确保您使用的内存量也有限制!不要试图记录每一个返回的 ICMP 回显回复,否则你将遇到一个吞噬记忆的怪物!最好只记录每个主机最后一次回复的时间。
在我送你上路之前再提一条建议。许多主机都有防火墙规则,限制它们每秒接受多少 ping。即使他们不这样做,我也认为您是一个好人并且不想 DOS 任何人。所以不要失去控制,开始用机枪扫射那些可怜的无辜者。
添加评论作为答案
我认为问题可能在于您正在为每个 ping 执行一个新进程,因此 OS 在进程分配上花费了相当多的时间。您是否尝试过使用诸如 net-ping 之类的库?这种方法应该大大减少时间。此外,由于工作是 I/O 绑定的,您可以将线程数增加到大约 2k(取决于 ruby 实现),因为大多数线程大部分时间都在休眠。
我需要在 rails 中执行一项任务,该任务会 ping 大量主机并检查其是否可达。结果必须尽快显示,主机数量可以扩展到大约 10k 甚至更多。
到目前为止,我已经尝试使用线程池并花费了很长时间。
work_q = Queue.new
@hosts.each{|x| work_q.push x }
workers = (0...200).map do
Thread.new do
begin
while host = work_q.pop(true)
ping_count = 1
server = host.address
result = `ping -q -c #{ping_count} #{server}`
if ($?.exitstatus == 0)
@res[host.hostname] = "up"
else
@res[host.hostname] = "down"
end
end
rescue ThreadError
end
end
end;
我也尝试过使用sidekiq将其实现为异步任务。这是我想到的实施方式。 1.将host ip传入sidekiq中的job queue,找到最后一个job id/worker id 2.检查最后一个worker id的状态并以某种方式持久化。(不确定如何以更好的方式持久化worker id) 3. 安排任务检查最后一个工人的完成情况。一旦最后一个工作人员完成,重新启动 sidekiq 并再次 ping 主机。 4. 通过这种方式,您可以根据最小时间间隔内的 ping 结果获得所有主机的最新状态(启动或关闭)。 5. 每当用户点击查看主机的状态时,显示结果,并且是最新的。
任何人都可以提供任何其他想法,他们可以想出更好的方法。 感谢您的帮助。
我从来没有用过它,但经过一些搜索发现了一个名为 PacketFu 的库,它允许您从 Ruby 发送 ICMP 数据包(它依赖于 libpcap)。它还允许您嗅探来自 Ruby.
的数据包所以,这是一个想法:
与其为每个要 ping 的主机创建一个新的 ping
进程,不如使用 PacketFu 直接从主 Ruby 进程向每个主机发送 ICMP 回应请求。同时,在另一个线程中,使用 PacketFu 的 Capture
class 嗅探数据包并将源 IP 与您尝试 ping 的地址进行匹配。
您必须确保以线程安全的方式访问所有程序状态,否则事情会变得糟糕!如果您还有一个 Web 服务器或面向用户的界面的进程中的东西 运行,它也必须发挥良好和安全的作用,并且不要在没有锁定的情况下将其小手伸入程序状态!
确保您使用的内存量也有限制!不要试图记录每一个返回的 ICMP 回显回复,否则你将遇到一个吞噬记忆的怪物!最好只记录每个主机最后一次回复的时间。
在我送你上路之前再提一条建议。许多主机都有防火墙规则,限制它们每秒接受多少 ping。即使他们不这样做,我也认为您是一个好人并且不想 DOS 任何人。所以不要失去控制,开始用机枪扫射那些可怜的无辜者。
添加评论作为答案
我认为问题可能在于您正在为每个 ping 执行一个新进程,因此 OS 在进程分配上花费了相当多的时间。您是否尝试过使用诸如 net-ping 之类的库?这种方法应该大大减少时间。此外,由于工作是 I/O 绑定的,您可以将线程数增加到大约 2k(取决于 ruby 实现),因为大多数线程大部分时间都在休眠。