如何 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 实现),因为大多数线程大部分时间都在休眠。