¿Una alternativa limpia y liviana al trenzado de Python? [cerrado]

222

Hace (mucho tiempo) escribí una araña web que multiprocedí para permitir que se produjeran solicitudes concurrentes al mismo tiempo. Eso fue en mi juventud de Python, en los días antes de que supiera sobre el GIL y los problemas asociados que crea para el código multiproceso (IE, ¡la mayoría de las veces las cosas terminan en serie!) ...

Me gustaría volver a trabajar este código para hacerlo más robusto y tener un mejor rendimiento. Básicamente, podría hacer esto de dos maneras: podría usar el nuevo módulo de multiprocesamiento en 2.6+ o podría optar por un modelo basado en un reactor / evento de algún tipo. Prefiero hacerlo más tarde, ya que es mucho más simple y menos propenso a errores.

Entonces, la pregunta se refiere a qué marco sería el más adecuado para mis necesidades. La siguiente es una lista de las opciones que conozco hasta ahora:

  • Twisted : El abuelo de los marcos de reactores Python: parece complejo y un poco hinchado Curva de aprendizaje empinada para una tarea pequeña.
  • Eventlet : De los chicos de lindenlab . Marco basado en Greenlet orientado a este tipo de tareas. Sin embargo, eché un vistazo al código y no es demasiado bonito: no cumple con pep8, está disperso con impresiones (¿por qué la gente hace esto en un marco?), API parece un poco inconsistente.
  • PyEv : Inmaduro, parece que nadie lo está usando en este momento, aunque está basado en liberant, por lo que tiene un backend sólido.
  • asyncore : De stdlib: über de bajo nivel, parece mucho trabajo de campo involucrado solo para sacar algo del suelo.
  • tornado : Aunque este es un producto orientado al servidor diseñado para servidores de sitios web dinámicos, presenta un cliente HTTP asíncrono y un simple ioloop . Parece que podría hacer el trabajo pero no para lo que estaba destinado. [editar: desafortunadamente no se ejecuta en Windows, lo cual lo cuenta para mí, es un requisito para mí admitir esta plataforma aburrida]

¿Hay algo que me haya perdido? ¡Seguramente debe haber una biblioteca por ahí que se ajuste al punto óptimo de una biblioteca de red asincrónica simplificada!

[edit: muchas gracias a intgr por su puntero a esta página . Si se desplaza hacia abajo, verá que hay una lista muy buena de proyectos que tienen como objetivo abordar esta tarea de una forma u otra. En realidad, parece que las cosas han avanzado desde el inicio de Twisted: las personas ahora parecen favorecer una solución basada en la co-rutina en lugar de una solución tradicional orientada al reactor / devolución de llamada. Los beneficios de este enfoque son un código más directo más claro: ciertamente lo he encontrado en el pasado, especialmente cuando trabajo con boost.asioen C ++, ese código basado en la devolución de llamada puede conducir a diseños que pueden ser difíciles de seguir y son relativamente oscuros para el ojo inexperto. El uso de co-rutinas le permite escribir código que parece un poco más sincrónico al menos. ¡Supongo que ahora mi tarea es averiguar cuál de estas bibliotecas me gusta y probarla! Me alegro de haber preguntado ahora ...]

[editar: tal vez sea de interés para cualquiera que haya seguido o tropezado con esta pregunta o que se preocupe por este tema en algún sentido: encontré una excelente reseña del estado actual de las herramientas disponibles para este trabajo]

jkp
fuente
14
Python es multiproceso, simplemente no permite que dos hilos ejecuten código Python simultáneamente.
intgr
86
He aprendido mucho más de tu pregunta que de las respuestas a ella.
Denis Otkidach 01 de
2
@ Denis: je, ¡gracias, supongo! También ha habido algunos buenos indicadores en las respuestas, específicamente las de intgr. Conocía muchas de las opciones disponibles y no solo quería que las respuestas estuviesen empaquetadas, así que pensé en tomar la molestia de explicar lo que sabía :)
jkp
55
> las personas ahora parecen preferir una solución basada en co-rutina en lugar de una solución tradicional orientada a reactores / retrollamadas. Esta no es una comparación sensata. Las "soluciones basadas en co-rutina" y las soluciones "orientadas al reactor" son ortogonales. (Ignorando el hecho de que Python no tiene rutinas) Eche un vistazo a las llamadas en línea de Twisted para ver cómo puede tener el estilo de programación que parece preferir con una capa de red sólida y madura que no lo expondrá a idiosincrasias de plataforma complejas.
Jean-Paul Calderone
2
Algunos puntos para agregar: 1. Tornado funciona muy bien en Windows. Simplemente no es tan eficiente y escalable porque se usa selectpara la multiplexación de E / S. Pero debería poder obtener un rendimiento decente con tornado-pyuv . 2. Ahora hay asyncio en Python 3.3+ y su backport trollius que permite ejecutar cualquier aplicación Tornado en su bucle de eventos (Twisted será compatible pronto).
schlamar

Respuestas:

28

Me gustó el módulo Python de concurrencia que se basa en microthreads Stackless Python o Greenlets para un enhebrado liviano. Todas las E / S de red de bloqueo se hacen asíncronas de forma transparente a través de un solo libeventbucle, por lo que deberían ser casi tan eficientes como un servidor asíncrono real.

Supongo que es similar a Eventlet de esta manera.

La desventaja es que su API es bastante diferente de los módulos sockets/ de Python threading; necesita reescribir un poco de su aplicación (o escribir una capa de compatibilidad de compatibilidad)

Editar: Parece que también hay cogen , que es similar, pero utiliza los generadores mejorados de Python 2.5 para sus corutinas, en lugar de Greenlets. Esto lo hace más portátil que la concurrencia y otras alternativas. La E / S de red se realiza directamente con epoll / kqueue / iocp.

intgr
fuente
@intgr: excelentes enlaces. Había visto ambos antes, ése era el tipo de cosas que esperaba ver enrojecidas. +1
jkp
3
Parece que la concurrencia es un proyecto muerto, ya que fue la última actualización hace cuatro años.
Gewthen
el proyecto está muerto, ¡Hyves también!
Bahadir Cambel
1
Mucho ha sucedido desde Python 2.5. asyncio en Python 3.5 es genial.
Joseph Sheedy
99

Twisted es complejo, tienes razón en eso. Twisted no está hinchado.

Si echas un vistazo aquí: http://twistedmatrix.com/trac/browser/trunk/twisted encontrarás un conjunto organizado, completo y muy bien probado de muchos protocolos de Internet, así como un código auxiliar para escribir e implementar aplicaciones de red muy sofisticadas. No confundiría hinchazón con amplitud.

Es bien sabido que la documentación de Twisted no es la más fácil de usar a primera vista, y creo que esto rechaza a un número desafortunado de personas. Pero Twisted es increíble (en mi humilde opinión) si pones el tiempo. Lo hice y demostró que valió la pena, y recomendaría a otros que prueben lo mismo.

clemesha
fuente
44
@clemesha: tal vez tengas razón, y no está cargado, pero parece que hay un poco demasiado para entender cómo hacer algo simple. Entiendo la programación asincrónica, he trabajado en C ++ con boost :: asio, por lo que los conceptos no son nuevos, pero es todo lo que compite con hacer cosas retorcidas: es un mundo completamente nuevo, al igual que django es para cosas web. Nuevamente, cuando estoy haciendo cosas web, trabajo con código WSGI liviano y conecto solo lo que necesito. Caballos para los cursos, supongo.
jkp
77
@clemesha: erm, hoy me lancé para echar un vistazo: ¡retorcido pesa 20 MB! Incluso el núcleo es de 12 MB ... si eso no está hinchado, no estoy seguro de qué es.
jkp
29
Las API Twisted básicas son bastante pequeñas (reactor, diferido, protocolo). La mayor parte del código Twisted es implementaciones de protocolos asíncronos que usan esos conceptos básicos. "Hinchar" no es un adjetivo útil aquí (o de hecho en la mayoría de los casos). El tamaño de Twisted es razonable por la cantidad de cosas que hace.
daf
56

gevent es eventlet limpiado .

En cuanto a API, sigue las mismas convenciones que la biblioteca estándar (en particular, módulos de subprocesamiento y multiprocesamiento) donde tiene sentido. Entonces tiene cosas familiares como Cola y Evento para trabajar.

Solo admite libevent ( actualización: libev desde 1.0 ) como implementación del reactor, pero lo aprovecha al máximo, presentando un servidor WSGI rápido basado en libevent-http y resolviendo consultas DNS a través de libevent-dns en lugar de usar un grupo de subprocesos como la mayoría de las otras bibliotecas hacer. ( actualización: dado que 1.0 c-ares se usa para hacer consultas DNS asíncronas; threadpool también es una opción).

Al igual que eventlet, hace que las devoluciones de llamada y los aplazamientos sean innecesarios mediante el uso de greenlets .

Vea los ejemplos: descarga simultánea de múltiples URL , webchat de sondeo largo .

Denis Bilenko
fuente
44
En segundo lugar, Gevent: después de revisar muchas de las soluciones, Gevent funcionó muy bien para mí. Me permitió retener la mayor parte de mi programa existente, y los cambios que se requirieron fueron triviales: lo mejor de todo es que si el código necesita mantenerse en 3, 4, 5, ... años, todavía hace Para cualquiera que no esté familiarizado con gevent, el mayor obstáculo para Twisted es la fuerte curva de aprendizaje, esto causa problemas no solo al implementar, sino también más adelante durante el mantenimiento ...
Martin Tournoij
27

Nicholas Piël compiló una comparación realmente interesante de tales marcos en su blog: ¡vale la pena leerlo!

jkp
fuente
2
Si bien estoy de acuerdo en que el artículo fue una lectura interesante, creo que vale la pena considerar la validez de los puntos de referencia presentados. Vea los comentarios aquí: reddit.com/r/programming/comments/ahepg/…
clemesha
1
@clemesha, aunque vale la pena señalar el punto en esa página de reddit, el punto de referencia se realizó en una máquina de doble núcleo y probablemente no estaba sufriendo el defecto fatal descrito. Supongo que es posible que tanto el cliente como el servidor se ejecuten en el mismo núcleo, pero no parece probable.
Peter Hansen
15

Ninguna de estas soluciones evitará el hecho de que el GIL previene el paralelismo de la CPU: son solo mejores formas de obtener el paralelismo IO que ya tiene con los subprocesos. Si crees que puedes mejorar IO, busca uno de estos, pero si tu cuello de botella está en el procesamiento de los resultados, nada aquí ayudará, excepto el módulo de multiprocesamiento.

Adam Hupp
fuente
¿Qué hay de malo en usar múltiples procesos?
Emil Ivanov
3
Nada en absoluto, de ahí la sugerencia de utilizar el módulo de multiprocesamiento.
Adam Hupp
11

No iría tan lejos como para llamar a Twisted hinchado, pero es difícil entenderlo. Evité realmente establecerme en un aprendizaje durante bastante tiempo ya que siempre quise algo un poco más fácil para 'pequeñas tareas'.

Sin embargo, ahora que he trabajado un poco más, debo decir que tener todas las baterías incluidas es MUY agradable.

Todas las otras bibliotecas asíncronas con las que he trabajado terminan siendo mucho menos maduras de lo que parecen. El bucle de eventos de Twisted es sólido.

No estoy muy seguro de cómo resolver la curva de aprendizaje Twisted empinada. Podría ayudar si alguien lo bifurca y limpia algunas cosas, como eliminar todos los problemas de compatibilidad hacia atrás y los proyectos muertos. Pero esa es la naturaleza del software maduro, supongo.

rhettg
fuente
Si alguna vez buscó cómo se implementa el reactor gtk en Windows (sondeo hardcore cada 10 ms: twistedmatrix.com/trac/browser/trunk/twisted/internet/… ), no llamaría a eso "maduro" ...
schlamar
2
Hola @schlamar Este truco desagradable se implementó como una solución para algunos errores bastante graves en GTK +, en el día en que había mucha menos preocupación por la eficiencia energética :). Pero, la belleza de Twisted es que podemos tener este error una vez , solucionarlo en el marco, y nuestros usuarios no necesitan preocuparse por ello. ¿Le gustaría contribuir con una solución que solucione este problema y se deshaga de él (lo deprecia y luego lo elimina) PortableGtkReactor?
Glifo
1
@Glyph Agregué consejos útiles en twistedmatrix.com/trac/ticket/4744#comment:2 si alguien más quiere abordar este problema, porque todavía existen algunos de estos problemas. Por cierto, podría haber resuelto esto mucho más eficientemente programando devoluciones de llamada entre los dos bucles de eventos.
schlamar
7

Kamaelia no ha sido mencionada todavía. Su modelo de concurrencia se basa en el cableado de componentes con mensajes que pasan entre las bandejas de entrada y las de salida. Aquí hay una breve descripción general.

Steven Kryskalla
fuente
55
Usé kamaelia para una aplicación, fue extremadamente doloroso. En mi humilde opinión, hay otras mejores opciones para concurrenct en Python (la mayoría de los cuales se mencionan anteriormente)
Ben Ford
7

Empecé a usar retorcido para algunas cosas. La belleza de esto es casi porque está "hinchada". Hay conectores para casi cualquiera de los protocolos principales que existen. Puede tener un robot jabber que tomará comandos y los publicará en un servidor IRC, se los enviará por correo electrónico a alguien, ejecutará un comando, leerá desde un servidor NNTP y supervisará los cambios en una página web. La mala noticia es que puede hacer todo eso y puede hacer que las cosas sean demasiado complejas para tareas simples como lo explicó el OP. Sin embargo, la ventaja de Python es que solo incluye lo que necesita. Entonces, aunque la descarga puede ser de 20 MB, solo puede incluir 2 MB de bibliotecas (que todavía es mucho). Mi mayor queja con Twisted es que, aunque incluyen ejemplos, cualquier cosa más allá de un servidor tcp básico está solo.

Aunque no es una solución de Python, últimamente he visto que node.js ganaba mucha más tracción. De hecho, he considerado buscarlo para proyectos más pequeños, pero me estremezco cuando escucho javascript :)

vrillusions
fuente
Soy un gran fanático de Python. - Echa un vistazo a "Javascript - Las partes buenas" de Douglas Crockford (3, 4 videos). Y echa un vistazo a CoffeeScript. Resulta que JS tiene cosas que Python debería tener, excepto la sintaxis, jaja. CS trató de mitigar eso, pero es un poco torpe al respecto ...
Robert Siemer
4

Hay un buen libro sobre el tema: "Twisted Network Programming Essentials", de Abe Fettig. Los ejemplos muestran cómo escribir código muy pitónico y, personalmente, no me parecen basados ​​en un marco hinchado. Mire las soluciones en el libro, si no están limpias, entonces no sé qué significa limpio.

Mi único enigma es el mismo que tengo con otros marcos, como Ruby. Me preocupa, ¿se amplía? Odiaría comprometer a un cliente a un marco que tendrá problemas de escalabilidad.

mrsmoothie
fuente
4

Whizzer es un pequeño marco de socket asíncrono que utiliza pyev. Es muy rápido, principalmente debido a pyev. Intenta proporcionar una interfaz similar como retorcida con algunos pequeños cambios.

bfrog
fuente
2

Prueba también Syncless . Está basado en la rutina (por lo que es similar a Concurrence, Eventlet y gevent). Implementa reemplazos sin bloqueo para socket.socket, socket.gethostbyname (etc.), ssl.SSLSocket, time.sleep y select.select. Es rápido. Necesita Stackless Python y libevent. Contiene una extensión obligatoria de Python escrita en C (Pyrex / Cython).

pts
fuente
2

Confirmo la bondad de syncless . Puede usar libev (la versión más nueva, limpia y de mejor rendimiento de libevent). Algunas veces, no tiene tanto apoyo como libevent, pero ahora el proceso de desarrollo va más allá y es muy útil.

Robert Zaremba
fuente
1

Si solo desea una biblioteca de solicitudes HTTP simplificada y liviana, entonces encuentro que Unirest es realmente bueno

eyectamenta
fuente
0

Le invitamos a echar un vistazo a PyWorks, que adopta un enfoque bastante diferente. Permite que las instancias de objetos se ejecuten en su propio hilo y realiza llamadas de función a ese objeto de forma asíncrona.

Simplemente deje que una clase herede de Task en lugar de objeto y es asíncrona, todas las llamadas a métodos son Proxies. Los valores de retorno (si los necesita) son 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

PyWorks se puede encontrar en http://bitbucket.org/raindog/pyworks

renejsum
fuente
1
Si bien esto es interesante y podría ser adecuado para algunas tareas, el uso de subprocesos para la creación de redes funciona mal (especialmente en Python debido a la GIL). Y esta era exactamente la pregunta: un marco igualado o con multiprocesamiento. Entonces su respuesta está claramente fuera de alcance ...
schlamar