Uma alternativa limpa e leve à distorção do Python? [fechadas]

222

Há muito tempo atrás, escrevi uma teia de aranha que multithread para permitir que solicitações simultâneas ocorressem ao mesmo tempo. Isso foi na minha juventude em Python, nos dias antes de eu conhecer o GIL e os problemas associados que ele cria para o código multithread (IE, na maioria das vezes, as coisas acabam sendo serializadas!) ...

Gostaria de refazer esse código para torná-lo mais robusto e com melhor desempenho. Existem basicamente duas maneiras de fazer isso: eu poderia usar o novo módulo de multiprocessamento no 2.6+ ou eu poderia usar algum modelo baseado em reator / evento. Prefiro fazer o mais tarde, pois é muito mais simples e menos propenso a erros.

Portanto, a pergunta se refere a qual estrutura seria mais adequada às minhas necessidades. A seguir, é apresentada uma lista das opções que eu conheço até agora:

  • Twisted : O avô das estruturas dos reatores Python: parece complexo e um pouco inchado. Curva de aprendizado acentuada para uma pequena tarefa.
  • Eventlet : Dos caras do lindenlab . Estrutura baseada em greenlet voltada para esse tipo de tarefa. No entanto, dei uma olhada no código e não é muito bonito: não compatível com o pep8, repleto de impressões (por que as pessoas fazem isso em uma estrutura !?), a API parece um pouco inconsistente.
  • PyEv : Imaturo, parece não haver ninguém usando agora, embora seja baseado no libevent, por isso possui um back-end sólido.
  • asyncore : A partir do stdlib: über de baixo nível, parece ser muito trabalho braçal apenas para tirar algo do chão.
  • tornado : Embora seja um produto orientado ao servidor, projetado para servidores de sites dinâmicos, ele possui um cliente HTTP assíncrono e um ioloop simples . Parece que poderia fazer o trabalho, mas não para o que se destinava. [editar: infelizmente, não roda no Windows, o que é importante para mim - é um requisito para eu apoiar esta plataforma fraca]

Há algo que eu perdi? Certamente deve haver uma biblioteca por aí que se encaixe no ponto ideal de uma biblioteca de rede assíncrona simplificada!

[editar: grandes agradecimentos a intgr por seu ponteiro para esta página . Se você rolar até o final, verá uma lista muito boa de projetos que visam realizar essa tarefa de uma maneira ou de outra. Parece que as coisas realmente mudaram desde o início do Twisted: as pessoas agora parecem preferir uma solução baseada em rotina ao invés de uma solução tradicional orientada a reatores / retornos de chamada. Os benefícios dessa abordagem são um código mais claro e direto: eu certamente encontrei no passado, especialmente ao trabalhar com o boost.asioem C ++, esse código baseado em retorno de chamada pode levar a designs difíceis de seguir e relativamente obscuros para quem não é treinado. O uso de co-rotinas permite escrever um código que pareça um pouco mais síncrono. Acho que agora minha tarefa é descobrir qual dessas muitas bibliotecas eu gosto da aparência e experimentar! Ainda bem que perguntei agora ...]

[editar: talvez seja de interesse para quem seguiu ou tropeçou nessa questão ou se preocupa com esse tópico em qualquer sentido: eu encontrei uma excelente descrição do estado atual das ferramentas disponíveis para este trabalho]

jkp
fonte
14
O Python é multithread, apenas não permite que dois threads executem o código Python simultaneamente.
intgr
86
Aprendi muito mais com sua pergunta do que com respostas a ela.
Denis Otkidach 01/12/2009
2
@ Denis: heh, obrigado eu acho! Também houve algumas dicas boas nas respostas, especificamente no intgr. Eu sabia sobre um monte de opções lá fora e eu não só quero as respostas embalados com aqueles que eu pensei que eu iria para a dificuldade de soletrar o que eu sabia :)
jkp
5
> as pessoas agora parecem preferir uma solução baseada na rotina, em vez de uma solução tradicional orientada a reatores / retornos de chamada. Essa não é uma comparação sensata. "soluções baseadas em rotina" e soluções "orientadas a reatores" são ortogonais. (Ignorando o fato de o Python não possuir corotinas) Veja os inlineCallbacks do Twisted para ver como você pode ter o estilo de programação que prefere com uma camada de rede madura e robusta que não o exporá a idiossincrasias de plataformas complexas.
Jean-Paul Calderone
2
Alguns pontos a acrescentar: 1. O Tornado funciona muito bem no Windows. Ele não é tão eficiente e escalável, porque é usado selectpara a multiplexação de E / S. Mas você deve conseguir obter um desempenho decente com o tornado-pyuv . 2. Agora existe assíncrono no Python 3.3+ e seu trollius de backport, que permite executar qualquer aplicativo Tornado em seu loop de eventos (o Twisted será suportado em breve).
schlamar

Respostas:

28

Gostei do módulo Python de concorrência , que depende de microtreads Stackless Python ou Greenlets para rosqueamento leve. Todas as E / S de rede de bloqueio são transparentemente assíncronas por meio de um único libeventloop, portanto devem ser quase tão eficientes quanto um servidor assíncrono real.

Suponho que seja semelhante ao Eventlet dessa maneira.

A desvantagem é que sua API é bem diferente dos sockets/ threadingmódulos do Python ; você precisa reescrever um pouco do seu aplicativo (ou escrever uma camada de compatibilidade)

Edit: Parece que também há cogen , que é semelhante, mas usa os geradores aprimorados do Python 2.5 para suas corotinas, em vez de Greenlets. Isso o torna mais portátil que a concorrência e outras alternativas. A E / S de rede é feita diretamente com epoll / kqueue / iocp.

intgr
fonte
@intgr: ótimos links. Eu já tinha visto os dois antes, esses são os tipos de coisas que eu esperava ver coradas. +1
jkp 01/12/2009
3
Parece que a simultaneidade é um projeto morto, sendo a última atualização há quatro anos.
Gewthen
projeto está morto, Hyves!
Bahadir Cambel
1
Muita coisa aconteceu desde o Python 2.5. assíncio em Python 3.5 é ótimo.
Joseph Sheedy
99

Torcido é complexo, você está certo sobre isso. Torcido não está inchado.

Se você der uma olhada aqui: http://twistedmatrix.com/trac/browser/trunk/twisted, você encontrará um conjunto organizado, abrangente e muito bem testado de muitos protocolos da Internet, bem como código auxiliar para escrever e implantar aplicativos de rede muito sofisticados. Eu não confundiria inchaço com abrangência.

É sabido que a documentação do Twisted não é a mais fácil de usar à primeira vista, e acredito que isso afasta um número infeliz de pessoas. Mas Twisted é incrível (IMHO) se você colocar o tempo. Eu fiz e provou valer a pena, e eu recomendo a outras pessoas que tentem o mesmo.

clemesha
fonte
4
@clemesha: talvez você esteja certo, e não esteja cheio, mas parece que há um pouco demais para me convencer a fazer algo simples. Eu entendo programação assíncrona, trabalhei em C ++ com boost :: asio para que os conceitos não sejam novos, mas é tudo o que se compara ao fazer coisas distorcidas: é um mundo totalmente novo, como django é para coisas da web. Novamente, quando estou fazendo coisas na Web, trabalho com código WSGI leve e conecto apenas o que preciso. Cavalos para os cursos, eu acho.
Jkp
7
@clemesha: erm, eu mergulhei hoje para dar uma olhada: torcido pesa 20MB! Até o núcleo é 12MB .... se isso não estiver inchado, não sei bem o que é.
jkp
29
As APIs básicas do Twisted são bem pequenas (reator, adiado, protocolo). A maior parte do código Twisted são implementações de protocolo assíncrono usando esses princípios. "Inchaço" não é um adjetivo útil aqui (ou de fato na maioria dos casos). O tamanho do Twisted é razoável para a quantidade de coisas que ele faz.
daf
56

gevent é limpo de eventos .

Em termos de API, ele segue as mesmas convenções da biblioteca padrão (em particular, módulos de encadeamento e multiprocessamento) onde faz sentido. Então você tem coisas familiares como Fila e Evento para trabalhar.

Ele suporta apenas o libevent ( update: libev desde 1.0 ) como implementação do reator, mas aproveita ao máximo, apresentando um servidor WSGI rápido baseado no libevent-http e resolvendo consultas DNS através do libevent-dns, em vez de usar um pool de threads como a maioria das outras bibliotecas Faz. ( atualização: já que 1.0 c-ares é usado para fazer consultas DNS assíncronas; o pool de threads também é uma opção.)

Como o eventlet, torna desnecessários os retornos de chamada e adiados usando greenlets .

Confira os exemplos: download simultâneo de vários URLs , webchat de pesquisas longas .

Denis Bilenko
fonte
4
Vou fazer o segundo evento - Depois de revisar muitas das soluções, o gevent funcionou muito bem para mim. Ele me permitiu manter a melhor parte do meu programa existente, e as alterações necessárias foram triviais - o melhor de tudo é que, se o código precisar ser mantido em 3, 4, 5, ... anos, ele ainda fará sentido para quem não está familiarizado com GEvent, o maior empecilho para a torcida é a forte curva de aprendizagem, isso causa problemas não apenas na implementação, mas também mais abaixo da linha durante a manutenção ...
Martin Tournoij
27

Uma comparação realmente interessante de tais estruturas foi compilada por Nicholas Piël em seu blog: vale a pena ler!

jkp
fonte
2
Embora eu concorde que o artigo tenha sido uma leitura interessante, acho que vale a pena considerar a validade dos parâmetros de referência apresentados. Veja os comentários aqui: reddit.com/r/programming/comments/ahepg/…
clemesha
1
@clemesha, embora o ponto nessa página do reddit seja digno de nota, o benchmark foi feito em uma máquina de núcleo duplo e provavelmente não estava sofrendo de falha fatal descrita. Suponho que seja possível que o cliente e o servidor tenham executado no mesmo núcleo, mas não parece provável.
Peter Hansen
15

Nenhuma dessas soluções evitará o fato de o GIL impedir o paralelismo da CPU - elas são apenas maneiras melhores de obter o paralelismo de E / S que você já possui com os threads. Se você acha que pode fazer melhor IO, siga um destes procedimentos, mas se o seu gargalo estiver no processamento dos resultados, nada aqui ajudará, exceto o módulo de multiprocessamento.

Adam Hupp
fonte
O que há de errado em usar vários processos?
Emil Ivanov
3
Nada, daí a sugestão de usar o módulo de multiprocessamento.
Adam Hupp
11

Eu não chegaria ao ponto de chamar Twisted inchado, mas é difícil entender o que você pensa. Evitei realmente me envolver em um aprendizado por um bom tempo, pois sempre desejei algo um pouco mais fácil para 'pequenas tarefas'.

No entanto, agora que trabalhei um pouco mais, tenho que dizer que incluir todas as baterias é MUITO legal.

Todas as outras bibliotecas assíncronas com as quais trabalhei acabam sendo muito menos maduras do que parecem. O loop de eventos do Twisted é sólido.

Não sei ao certo como resolver a curva de aprendizado íngreme do Twisted. Pode ajudar se alguém o forçar e limpar algumas coisas, como remover todo o cruft de compatibilidade com versões anteriores e os projetos mortos. Mas essa é a natureza do software maduro, eu acho.

rhettg
fonte
Se você já visse como o reator gtk é implementado no Windows (pesquisa a cada 10ms: twistedmatrix.com/trac/browser/trunk/twisted/internet/… ), não chamaria isso de "maduro" ...
schlamar
2
Olá @schlamar. Esse hack desagradável foi implementado como uma solução alternativa para alguns erros bastante sérios no GTK +, na época em que havia muito menos preocupação com a eficiência de energia :). Mas, a beleza do Twisted é que podemos ter esse bug uma vez , corrigi-lo na estrutura e nossos usuários não precisam se preocupar com isso. Deseja contribuir com uma correção que resolva esse problema e se livre de (desaprova e depois remove) PortableGtkReactor?
Glyph
1
@Glyph Adicionei conselhos úteis em twistedmatrix.com/trac/ticket/4744#comment:2 se alguém quiser resolver esse problema, porque ainda existem alguns desses problemas. BTW, você poderia ter resolvido isso com muito mais eficiência agendando retornos de chamada entre os dois loops de eventos.
schlamar
7

Kamaelia ainda não foi mencionada. Seu modelo de simultaneidade baseia-se na conexão de componentes com a passagem de mensagens entre as caixas de entrada e as de saída. Aqui está uma breve visão geral.

Steven Kryskalla
fonte
5
Eu usei a kamaelia em um aplicativo - foi extremamente doloroso. IMHO há outras, melhores opções para concurrenct em python (a maioria dos quais são mencionados acima)
Ben Ford
7

Comecei a usar o twisted para algumas coisas. A beleza disso quase é porque está "inchado". Existem conectores para praticamente qualquer um dos principais protocolos existentes. Você pode ter um jabber bot que receberá comandos e será postado em um servidor irc, enviá-los por e-mail a alguém, executar um comando, ler em um servidor NNTP e monitorar uma página da Web em busca de alterações. A má notícia é que ele pode fazer tudo isso e tornar as coisas excessivamente complexas para tarefas simples, como o OP explicou. A vantagem do python é que você inclui apenas o que precisa. Portanto, enquanto o download pode ter 20 MB, você pode incluir apenas 2 MB de bibliotecas (o que ainda é muito). Minha maior reclamação com o twisted é que, embora incluam exemplos, qualquer coisa além de um servidor tcp básico você está por sua conta.

Embora não seja uma solução python, vi o node.js ganhar muito mais força ultimamente. Na verdade, eu considerei investigar projetos menores, mas me encolho quando ouço javascript :)

vrillusions
fonte
Eu sou um grande fã de Python. - Confira "Javascript - As boas partes" de Douglas Crockford (3, 4 vídeos). E dê uma olhada no CoffeeScript. Acontece que o JS tem coisas que o Python deveria ter, exceto a sintaxe, haha. CS tentou atenuar isso, mas é um pouco desajeitado em que ...
Robert Siemer
4

Há um bom livro sobre o assunto: "Twisted Network Programming Essentials", de Abe Fettig. Os exemplos mostram como escrever códigos muito pitonicos e, para mim, pessoalmente, não me parecem baseados em uma estrutura inchada. Veja as soluções do livro, se não estiverem limpas, não sei o que significa limpar.

Meu único enigma é o mesmo que tenho com outros frameworks, como Ruby. Eu me preocupo, isso aumenta? Detestaria comprometer um cliente com uma estrutura que apresentará problemas de escalabilidade.

mrsmoothie
fonte
4

O Whizzer é uma pequena estrutura de soquete assíncrona que usa pyev. É muito rápido, principalmente por causa do pyev. Ele tenta fornecer uma interface semelhante, distorcida com algumas pequenas alterações.

bfrog
fonte
2

Tente também o Syncless . É baseado em corotina (portanto, é semelhante a Concurrence, Eventlet e gevent). Ele implementa substituições sem bloqueio de entrada para socket.socket, socket.gethostbyname (etc.), ssl.SSLSocket, time.sleep e select.select. É rápido. Ele precisa de Stackless Python e libevent. Ele contém uma extensão Python obrigatória escrita em C (Pyrex / Cython).

pts
fonte
2

Confirmo a bondade da sincronia . Ele pode usar a libev (a versão mais recente, limpa e de melhor desempenho da libevent). Algumas vezes atrás, ele não tem tanto suporte quanto o libevent, mas agora o processo de desenvolvimento vai além e é muito útil.

Robert Zaremba
fonte
1

Se você quer apenas uma biblioteca de solicitações HTTP simplificada e leve, acho o Unirest realmente bom

ejectamenta
fonte
0

Você pode dar uma olhada no PyWorks, que tem uma abordagem bem diferente. Ele permite que instâncias de objetos sejam executadas em seu próprio encadeamento e faz chamadas de função para esse objeto assíncronas.

Apenas deixe uma classe herdar da tarefa em vez do objeto e ela é assíncrona; todas as chamadas de métodos são proxies. Os valores de retorno (se você precisar deles) são proxies futuros.

res = obj.method( args )
# code continues here without waiting for method to finish
do_something_else( )
print "Result = %d" % res # Code will block here, if res not calculated yet

O PyWorks pode ser encontrado em http://bitbucket.org/raindog/pyworks

renejsum
fonte
1
Embora isso seja interessante e possa ser adequado para algumas tarefas, o uso de threads para redes apresenta um desempenho ruim (especialmente no Python devido ao GIL). E essa era exatamente a pergunta: uma estrutura de eventos ou com multiprocessamento. Portanto, sua resposta é claramente fora do escopo ...
schlamar