Pergunta

Eu tenho dois scripts que usam Mechanize para buscar uma página de índice do Google. Eu assumi que a EventMachine seria mais rápida que um fio de rubi, mas não é.

Custos do código do EventMachine: "0.24s user 0.08s system 2% cpu 12.682 total"

CÓDIGOS DE CÓDIGOS DE LINHAS RUBY: "0.22s user 0.08s system 5% cpu 5.167 total "

Estou usando o EventMachine da maneira errada?

EventMachine:

require 'rubygems'
require 'mechanize'
require 'eventmachine'

trap("INT") {EM.stop}

EM.run do 
  num = 0
  operation = proc {
    agent = Mechanize.new
    sleep 1
    agent.get("http://google.com").body.to_s.size
  }
  callback = proc { |result|
    sleep 1
    puts result
    num+=1
    EM.stop if num == 9
  }

  10.times do 
    EventMachine.defer operation, callback
  end
end

Ruby Thread:

require 'rubygems'
require 'mechanize'


threads = []
10.times do 
  threads << Thread.new do 
    agent = Mechanize.new
    sleep 1
    puts agent.get("http://google.com").body.to_s.size
    sleep 1
  end
end


threads.each do |aThread| 
  aThread.join
end
Foi útil?

Solução

Sim, você está usando errado. A EventMachine funciona fazendo chamadas de IO assíncronas que retornam imediatamente e notificam o "reator" (o loop do evento iniciado por em.run) quando eles são concluídos. Você tem duas chamadas de bloqueio que derrotam o objetivo do sistema, dormir e mecanizar.get. Você precisa usar bibliotecas especiais assíncronas/não bloqueadoras para derivar qualquer valor da EventMachine.

Outras dicas

Todas as respostas neste thread estão faltando um ponto -chave: seus retornos de chamada estão sendo executados dentro do encadeamento do reator, em vez de em um encadeamento diferido separado. Executando solicitações mecanizadas em um defer A chamada é a maneira certa de não bloquear o loop, mas você deve ter cuidado para que seu retorno de chamada também não bloqueie o loop.

Quando você corre EM.defer operation, callback, a operação é executada dentro de um fio de rubi, que faz o trabalho e, em seguida, o retorno de chamada é emitido dentro do loop principal. Portanto, o sleep 1 dentro operation corre em paralelo, mas o retorno de chamada é em série. Isso explica a diferença próxima de 9 segundos no tempo de execução.

Aqui está uma versão simplificada do código que você está executando.

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  10.times { EM.defer work, callback }
}

Isso leva cerca de 12 segundos, que é 1 segundo para o sono paralelo, 10 segundos para o serial dorme e 1 segundo para sobrecarga.

Para executar o código de retorno de chamada em paralelo, você deve gerar novos threads usando um retorno de chamada proxy que usa EM.defer igual a:

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  proxy_callback = proc { EM.defer callback }

  10.times { EM.defer work, proxy_callback }
}

No entanto, você pode ter problemas com isso se o seu retorno de chamada deve executar o código no loop do evento, porque ele é executado dentro de um encadeamento adiado e separado. Se isso acontecer, mova o código do problema para o retorno de chamada do proxy_callback Proc.

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop_event_loop if (times += 1) >= 5
  }

  proxy_callback = proc { EM.defer callback, proc { "do_eventmachine_stuff" } }

  10.times { EM.defer work, proxy_callback }
}

Esta versão foi executada em cerca de 3 segundos, que representam 1 segundo de sono para operação em paralelo, 1 segundo de dormir para retorno de chamada em paralelo e 1 segundo para sobrecarga.

Você deve usar algo como em-http-request http://github.com/igrigorik/em-http-request

A EventMachine "adiar" realmente gera rosca de rubi de um threadpool que ele consegue lidar com sua solicitação. Sim, o EventMachine foi projetado para operações de IO não bloqueador, mas o comando adie é uma exceção - ele foi projetado para permitir que você faça operações de longa execução sem bloquear o reator.

Então, será um pouco mais lento que os fios nus, porque na verdade está apenas lançando tópicos com a sobrecarga do gerente de Threadpool da EventMachine.

Você pode ler mais sobre adiar aqui: http://eventmachine.rubyforge.org/eventmachine.html#m000486

Dito isto, buscar páginas é um ótimo uso da EventMachine, mas como outros pôsteres disseram, você precisa usar uma biblioteca de IO não bloqueada e depois usar o Next_Tick ou semelhante para iniciar suas tarefas, em vez de adiar, o que divide sua tarefa do loop do reator.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top