2014-09-30 5 views
0

У меня есть сценарий, который генерирует заданное пользователем количество IP-адресов и пытается подключиться ко всем их на каком-то порту. Я использую Celluloid с этим скриптом для обеспечения разумной скорости, поскольку синхронно сканирование 2000 хостов может занять много времени. Однако, скажем, я скажу скрипту сканировать 2000 случайных хостов. То, что я нахожу, это то, что на самом деле оно заканчивается сканированием примерно на половину этого числа. Если я скажу, что сканирование 3000, я получаю те же основные результаты. Кажется, он работает намного лучше, если я делаю 1000 или меньше, но даже если я просто сканирую 1000 хостов, он обычно заканчивает примерно 920 с относительной согласованностью. Я понимаю, что генерация случайных IP-адресов, очевидно, не сработает с некоторыми из них, но мне трудно поверить, что существует около 70 неправильно созданных IP-адресов каждый раз. Так вот код:Неприятные условия гонки с Celluloid

class Scan 
include Celluloid 

def initialize(arg1) 
    @arg1 = arg1 
    @host_arr = [] 
    @timeout = 1 
end 


def popen(host) 
    addr = Socket.getaddrinfo(host, nil) 
    sock = Socket.new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0) 

    begin 
     sock.connect_nonblock(Socket.pack_sockaddr_in(22, addr[0][3])) 

    rescue Errno::EINPROGRESS 
     resp = IO.select(nil, [sock], nil, @timeout.to_i) 

     if resp.nil? 
      puts "#{host}:Firewalled" 
     end 

     begin 
      if sock.connect_nonblock(Socket.pack_sockaddr_in(22, addr[0][3])) 
       puts "#{host}:Connected" 
      end 

     rescue Errno::ECONNREFUSED 
      puts "#{host}:Refused" 
      rescue 
       false 
     end 
    end 
    sock 
end 


def asynchronous 
    s = 1 
    threads = [] 

     while s <= @arg1.to_i do 
      @host_arr << Array.new(4){rand(254)}.join('.') 
      s += 1 
     end 

     @host_arr.each do |ip| 
      threads << Thread.new do 
       begin 
        popen(ip) 
       rescue 
       end 
      end 
     end 

     threads.each do |thread| 
      thread.join 
     end 
end 

end 

scan = Scan.pool(size: 100, args: [ARGV[0]]) 

(0..20).to_a.map { scan.future.asynchronous } 

Примерно половину времени я получаю это:

D [2014-09-30T17: 06: 12,810856 # 30077] DEBUG -: Нагрузочный 11 актеров .. W, [2014-09-30T17: 06: 12.812151 # 30077] WARN -: Завершение задачи: type =: finalizer, meta = {: method_name =>: shutdown}, status =: получение Celluloid :: BackFlash TaskFiber недоступен. Пожалуйста, попробуйте Celluloid.task_class = Celluloid::TaskThread, если вам нужны обратные трассы.

и сценарий ничего не делает. В остальное время (только если я укажу более 1000), я получаю следующее: http://pastebin.com/wTmtPmc8

Итак, мой вопрос в этом. Как я могу избежать условий гонки и тупиковой ситуации, сохраняя при этом то, что я хочу в этом конкретном сценарии?

ответ

2

Запуск низкоуровневых потоков сам по себе мешает функциональности Celluloid. Вместо этого создайте объекты «Пул сканирования» и сразу же передайте им все IP-адреса. Они будут стоять в очереди за имеющимися

class Scan 
    def popen 
    … 
    end 
end 

scanner_pool = Scan.pool(50) 
resulsts = @host_arr.map { |host| scanner_pool.scan(host) } 
Смежные вопросы