Estou abrindo um arquivo com 100.000 URLs. Preciso enviar uma solicitação HTTP para cada URL e imprimir o código de status. Estou usando o Python 2.6 e, até agora, olhei para as muitas maneiras confusas em que o Python implementa threading / simultaneidade. Eu até olhei para a biblioteca de concorrência python , mas não consigo descobrir como escrever este programa corretamente. Alguém já se deparou com um problema semelhante? Acho que geralmente preciso saber como executar milhares de tarefas no Python o mais rápido possível - suponho que isso signifique 'simultaneamente'.
python
http
concurrency
IgorGanapolsky
fonte
fonte
requests.get
erequests.head
(ou seja, uma solicitação de página vs um pedido cabeça) para retornar diferentes códigos de status, de modo que este não é o melhor conselhoRespostas:
Solução Twistedless:
Este é um pouco mais rápido que a solução distorcida e usa menos CPU.
fonte
concurrent*2
?conn.close()
. Abrir muitas conexões http pode interromper seu script em algum momento e consumir memória.Queue
módulo foi renomeado paraqueue
no Python 3. Esse é o código do Python 2.Uma solução usando a biblioteca de rede assíncrona tornado
fonte
As coisas mudaram bastante desde 2010, quando isso foi publicado e eu não tentei todas as outras respostas, mas tentei algumas e achei que funcionava melhor para mim usando python3.6.
Consegui buscar cerca de 150 domínios únicos por segundo em execução na AWS.
fonte
time1 = time.time()
no topo do loop for etime2 = time.time()
logo após o loop for.Threads não são absolutamente a resposta aqui. Eles fornecerão gargalos no processo e no kernel, além de limites de taxa de transferência que não são aceitáveis se o objetivo geral for "o caminho mais rápido".
Um pouco
twisted
e seuHTTP
cliente assíncrono forneceriam resultados muito melhores.fonte
Eu sei que essa é uma pergunta antiga, mas no Python 3.7 você pode fazer isso usando
asyncio
eaiohttp
.Você pode ler mais sobre isso e ver um exemplo aqui .
fonte
urls= [fetch(construct_fetch_url(u),idx) for idx, u in enumerate(some_URI_list)]
results = await asyncio.gather(*urls)
Use grequests , é uma combinação de pedidos + módulo Gevent.
O GRequests permite usar solicitações com Gevent para fazer solicitações HTTP assíncronas facilmente.
O uso é simples:
Crie um conjunto de solicitações não enviadas:
Envie todos eles ao mesmo tempo:
fonte
Uma boa abordagem para solucionar esse problema é primeiro escrever o código necessário para obter um resultado e incorporar o código de segmentação para paralelizar o aplicativo.
Em um mundo perfeito, isso significaria simplesmente iniciar simultaneamente 100.000 threads que produzem seus resultados em um dicionário ou lista para processamento posterior, mas na prática você está limitado em quantas solicitações HTTP paralelas você pode emitir dessa maneira. Localmente, você tem limites em quantos soquetes pode abrir simultaneamente, quantos threads de execução o seu interpretador Python permitirá. Remotamente, você pode estar limitado no número de conexões simultâneas se todas as solicitações forem contra um servidor ou muitos. Essas limitações provavelmente exigirão que você escreva o script de forma a pesquisar apenas uma pequena fração dos URLs a qualquer momento (100, como outro pôster mencionado, é provavelmente um tamanho decente do conjunto de encadeamentos, embora você possa achar que pode implantar com sucesso muito mais).
Você pode seguir este padrão de design para resolver o problema acima:
list
oudict
no CPython, você poderá anexar ou inserir com segurança itens exclusivos de seus encadeamentos sem bloqueios , mas se gravar em um arquivo ou precisar de uma interação de dados entre encadeamentos mais complexa, use um bloqueio de exclusão mútua para proteger esse estado da corrupção .Eu sugiro que você use o módulo de segmentação . Você pode usá-lo para iniciar e rastrear threads em execução. O suporte de encadeamento do Python é simples, mas a descrição do seu problema sugere que ele é completamente suficiente para suas necessidades.
Finalmente, se você gostaria de ver uma aplicação direta bonita de um aplicativo de rede paralela escrito em Python, veja ssh.py . É uma pequena biblioteca que usa o Python threading para paralelizar muitas conexões SSH. O design está próximo o suficiente de seus requisitos, para que você possa ser um bom recurso.
fonte
Se você deseja obter o melhor desempenho possível, considere usar E / S assíncrona em vez de threads. A sobrecarga associada a milhares de threads do sistema operacional não é trivial e a alternância de contexto no interpretador Python acrescenta ainda mais. O encadeamento certamente fará o trabalho, mas suspeito que uma rota assíncrona fornecerá melhor desempenho geral.
Especificamente, eu sugeriria o cliente da Web assíncrono na biblioteca Twisted ( http://www.twistedmatrix.com ). Ele tem uma curva de aprendizado reconhecidamente íngreme, mas é bastante fácil de usar quando você conhece bem o estilo de programação assíncrona do Twisted.
Um API de cliente da Web assíncrono do HowTo na Twisted está disponível em:
http://twistedmatrix.com/documents/current/web/howto/client.html
fonte
Uma solução:
Hora do teste:
Pingtime:
fonte
Usar um pool de threads é uma boa opção e facilitará bastante isso. Infelizmente, o python não possui uma biblioteca padrão que torna os pools de threads ultra fáceis. Mas aqui está uma biblioteca decente que deve ajudar você a começar: http://www.chrisarndt.de/projects/threadpool/
Exemplo de código do site:
Espero que isto ajude.
fonte
q_size
> 0, o tamanho da fila de solicitações de trabalho for limitado e o conjunto de encadeamentos bloquear quando a fila estiver cheia e tentar colocar mais solicitações de trabalho nela (consulte oputRequest
método), a menos que você também use umtimeout
valor positivoputRequest
."Criar
epoll
objeto,abertos muitos sockets cliente TCP,
ajustar seus buffers de envio a ser um pouco mais de cabeçalho de solicitação,
envie um cabeçalho de solicitação - deve ser imediata, apenas colocar em um buffer, cadastre-se tomada no
epoll
objeto,fazer
.poll
emepoll
obect,li pela primeira vez 3 bytes de cada soquete
.poll
,escreva-os para
sys.stdout
seguidos de\n
(não libere), feche o soquete do cliente.Número limite de soquetes abertos simultaneamente - lida com erros quando os soquetes são criados. Crie um novo soquete somente se outro estiver fechado.
Ajuste os limites do SO.
Tente entrar em alguns processos (não muitos): isso pode ajudar a usar a CPU com um pouco mais de eficiência.
fonte
No seu caso, o encadeamento provavelmente fará o truque, pois você provavelmente estará gastando mais tempo aguardando uma resposta. Existem módulos úteis como a fila na biblioteca padrão que podem ajudar.
Eu fiz uma coisa semelhante com o download paralelo de arquivos antes e foi bom o suficiente para mim, mas não estava na escala que você está falando.
Se sua tarefa foi mais ligada à CPU, convém examinar o módulo de multiprocessamento , que permitirá utilizar mais CPUs / núcleos / threads (mais processos que não se bloqueiam, uma vez que o bloqueio é por processo)
fonte
Considere usar o moinho de vento , embora o Windmill provavelmente não possa fazer tantos tópicos.
Você poderia fazer isso com um script Python rolado manualmente em 5 máquinas, cada uma conectando a saída usando as portas 40000-60000, abrindo 100.000 conexões de porta.
Além disso, pode ajudar a fazer um teste de amostra com um aplicativo de controle de qualidade bem segmentado, como o OpenSTA , para ter uma idéia de quanto cada servidor pode suportar.
Além disso, tente usar o Perl simples com a classe LWP :: ConnCache. Você provavelmente obterá mais desempenho (mais conexões) dessa maneira.
fonte
Esse cliente da Web assíncrono distorcido é muito rápido.
fonte
Descobri que usar o
tornado
pacote é a maneira mais rápida e simples de conseguir isso:fonte
A maneira mais fácil seria usar a biblioteca de threads embutida do Python.
Eles não são threads "reais" / do kernel.Eles têm problemas (como serialização), mas são bons o suficiente. Você deseja um pool de filas e threads. Uma opção está aqui , mas é trivial escrever sua própria. Você não pode paralelizar todas as 100.000 chamadas, mas pode disparar 100 (aproximadamente) delas ao mesmo tempo.fonte