Pensamientos de apertura
¿Cómo llegó a la conclusión de que a algunas partes del sistema les iría mejor en otro idioma? ¿Estás sufriendo problemas de rendimiento? ¿Qué tan graves son esos problemas? Si puede ser más rápido, ¿es esencial que sea más rápido?
Asincronía de un solo hilo
Hay varias preguntas y otros recursos web que ya abordan las diferencias, los pros y los contras de la asincronía de subproceso único frente a la concurrencia de subprocesos múltiples. Es interesante leer acerca de cómo funciona el modelo asincrónico de un solo hilo de Node.js cuando la E / S es el principal cuello de botella, y hay muchas solicitudes atendidas a la vez.
Twisted, Tornado y otros modelos asincrónicos hacen un excelente uso de un solo hilo. Dado que gran parte de la programación web tiene muchas E / S (red, base de datos, etc.), el tiempo que pasa esperando llamadas remotas se suma significativamente. Ese es el tiempo que podría dedicarse a hacer otras cosas, como iniciar otras llamadas a la base de datos, generar páginas y generar datos. La utilización de ese hilo único es extremadamente alta.
Uno de los mayores beneficios de la asincronía de un solo hilo es que usa mucha menos memoria. En la ejecución de subprocesos múltiples, cada subproceso requiere una cierta cantidad de memoria reservada. A medida que aumenta el número de subprocesos, también lo hace la cantidad de memoria necesaria solo para que existan los subprocesos. Como la memoria es finita, significa que hay límites en la cantidad de subprocesos que se pueden crear en cualquier momento.
Ejemplo
En el caso de un servidor web, imagine que cada solicitud recibe su propio hilo. Digamos que se requiere 1 MB de memoria para cada subproceso, y el servidor web tiene 2 GB de RAM. Este servidor web podría procesar (aproximadamente) 2000 solicitudes en cualquier momento antes de que simplemente no haya suficiente memoria para procesar más.
Si su carga es significativamente más alta que esto, las solicitudes tardarán mucho tiempo (cuando espere a que se completen las solicitudes más antiguas), o tendrá que lanzar más servidores al clúster para ampliar la cantidad de solicitudes simultáneas posibles .
Concurrencia multihilo
La concurrencia de subprocesos múltiples se basa en la ejecución de varias tareas al mismo tiempo. Eso significa que si un subproceso está bloqueado esperando que vuelva una llamada de la base de datos, se pueden procesar otras solicitudes al mismo tiempo. La utilización de subprocesos es menor, pero la cantidad de subprocesos que se ejecutan es mucho mayor.
El código multiproceso también es mucho más difícil de razonar. Hay problemas con el bloqueo, la sincronización y otros problemas divertidos de concurrencia. La asincronía de un solo hilo no sufre los mismos problemas.
Sin embargo, el código multiproceso es mucho más eficaz para tareas intensivas de CPU . Si no existen oportunidades para que un subproceso "ceda", como una llamada de red que normalmente se bloquearía, un modelo de un solo subproceso simplemente no tendrá ningún tipo de concurrencia.
Ambos pueden coexistir
Por supuesto, hay una superposición entre los dos; No son mutuamente exclusivos. Por ejemplo, el código de varios subprocesos se puede escribir de forma no bloqueante, para utilizar mejor cada subproceso.
La línea de fondo
Hay muchos otros problemas a considerar, pero me gusta pensar en los dos de esta manera:
- Si su programa está vinculado a E / S , entonces la asincronía de un solo hilo probablemente funcionará bastante bien.
- Si su programa está vinculado a la CPU , probablemente será mejor un sistema de subprocesos múltiples.
En su caso particular, debe determinar qué tipo de trabajo asincrónico se está completando y con qué frecuencia surgen esas tareas.
- ¿Ocurren en cada solicitud? Si es así, la memoria probablemente se convertirá en un problema a medida que aumente el número de solicitudes.
- ¿Se ordenan estas tareas? Si es así, tendrá que considerar la sincronización si usa múltiples hilos.
- ¿Estas tareas son intensivas en CPU? Si es así, ¿un solo hilo puede mantenerse al día con la carga?
No hay una respuesta simple. Debe considerar cuáles son sus casos de uso y diseñar en consecuencia. A veces, un modelo asincrónico de un solo hilo es mejor. Otras veces, se requiere el uso de varios subprocesos para lograr un procesamiento paralelo masivo.
Otras Consideraciones
Hay otros problemas que debe considerar también, en lugar de solo el modelo de concurrencia que elija. ¿Conoces Erlang o Clojure? ¿Crees que serías capaz de escribir código seguro de subprocesos múltiples en uno de estos idiomas para mejorar el rendimiento de tu aplicación? ¿Va a tomar mucho tiempo ponerse al día en uno de estos idiomas, y el idioma que aprenda le beneficiará en el futuro?
¿Qué hay de las dificultades asociadas con la comunicación entre estos dos sistemas? ¿Será demasiado complejo mantener dos sistemas separados en paralelo? ¿Cómo recibirá el sistema Erlang tareas de Django? ¿Cómo Erlang comunicará esos resultados a Django? ¿Es el rendimiento suficientemente significativo un problema que valga la pena la complejidad adicional?
Pensamientos finales
Siempre he encontrado que Django es lo suficientemente rápido, y lo usan algunos sitios con mucho tráfico. Hay varias optimizaciones de rendimiento que puede hacer para aumentar la cantidad de solicitudes concurrentes y el tiempo de respuesta. Es cierto que hasta ahora no he hecho nada con Celery, por lo que las optimizaciones de rendimiento habituales probablemente no resolverán los problemas que pueda tener con estas tareas asincrónicas.
Por supuesto, siempre existe la sugerencia de lanzar más hardware al problema. ¿Es el costo de aprovisionar un nuevo servidor más barato que el costo de desarrollo y mantenimiento de un subsistema completamente nuevo?
He hecho demasiadas preguntas en este momento, pero esa era mi intención. La respuesta no será fácil sin análisis y más detalles. Sin embargo, poder analizar los problemas se reduce a conocer las preguntas que se deben hacer ... así que espero haber ayudado en ese frente.
Mi instinto dice que una reescritura en otro idioma es innecesaria. La complejidad y el costo probablemente serán demasiado grandes.
Editar
Respuesta al seguimiento
Su seguimiento presenta algunos casos de uso muy interesantes.
1. Django trabajando fuera de las solicitudes HTTP
Su primer ejemplo consistió en leer etiquetas NFC y luego consultar la base de datos. No creo que escribir esta parte en otro idioma sea tan útil para usted, simplemente porque consultar la base de datos o un servidor LDAP estará sujeto a la E / S de la red (y posiblemente al rendimiento de la base de datos). Por otro lado, el número de solicitudes simultáneas estará vinculado por el propio servidor, ya que cada comando de administración se ejecutará como su propio proceso. Habrá tiempo de configuración y desmontaje que afectará el rendimiento, ya que no está enviando mensajes a un proceso que ya se está ejecutando. Sin embargo, podrá enviar múltiples solicitudes al mismo tiempo, ya que cada una será un proceso aislado.
Para este caso, veo dos caminos que puedes investigar:
- Asegúrese de que su base de datos sea capaz de manejar múltiples consultas a la vez con la agrupación de conexiones. (Oracle, por ejemplo, requiere que configure Django en consecuencia
'OPTIONS': {'threaded':True}
.) Puede haber opciones de configuración similares a nivel de base de datos o nivel de Django que puede ajustar para su propia base de datos. No importa en qué idioma escriba las consultas de su base de datos, tendrá que esperar a que estos datos vuelvan antes de poder encender los LED. Sin embargo, el rendimiento del código de consulta puede marcar la diferencia, y el Django ORM no es tan rápido ( pero , por lo general, lo suficientemente rápido).
- Minimice el tiempo de configuración / desmontaje. Tenga un proceso en ejecución constante y envíele mensajes. (Corríjame si me equivoco, pero esto es en lo que realmente se está enfocando su pregunta original). Si este proceso está escrito en Python / Django u otro lenguaje / marco está cubierto anteriormente. No me gusta la idea de usar comandos de administración con tanta frecuencia. ¿Es posible tener un pequeño fragmento de código ejecutándose constantemente, que empuja los mensajes de los lectores NFC a la cola de mensajes, que Celery luego lee y reenvía a Django? La configuración y desmontaje de un pequeño programa, incluso si está escrito en Python (¡pero no en Django!), Debería ser mejor que iniciar y detener un programa Django (con todos sus subsistemas).
No estoy seguro de qué servidor web está utilizando para Django. mod_wsgi
para Apache le permite configurar la cantidad de procesos y subprocesos dentro de los procesos que el servicio solicita. Asegúrese de ajustar la configuración relevante de su servidor web para optimizar la cantidad de solicitudes que se pueden atender.
2. "Pasar mensajes" con señales de Django
Su segundo caso de uso también es bastante interesante; No estoy seguro si tengo las respuestas para eso. Si está eliminando instancias de modelo y desea operar en ellas más tarde, podría ser posible serializarlas JSON.dumps
y luego deserializarlas JSON.loads
. Será imposible recrear completamente el gráfico de objetos más adelante (consultar modelos relacionados), ya que los campos relacionados se cargan lentamente desde la base de datos y ese enlace ya no existirá.
La otra opción sería marcar de alguna manera un objeto para su eliminación, y solo eliminarlo al final del ciclo de solicitud / respuesta (después de que todas las señales hayan sido atendidas). Puede requerir una señal personalizada para implementar esto, en lugar de confiar en ello post_delete
.