Hola Habr! Quiero contar c贸mo resolv铆 el problema de la ejecuci贸n competitiva efectiva de las tareas de asincio en Celery.
Introducci贸n
El apio es un gran proyecto con una historia compleja y una gran carga de compatibilidad con versiones anteriores. Las decisiones arquitect贸nicas clave se tomaron mucho antes de que apareciera Asyncio en Python. Por lo tanto, es a煤n m谩s interesante que de alguna manera pueda ejecutar la tarea de asincio en apio fuera de la caja.Modo de orificio activado Ejecute tareas asincio en apio de vainilla
Puede iniciar la tarea asincio desde el cuadro:import asyncio
from .celeryapp import celeryapp
async def async_task():
await asyncio.sleep(42)
@celeryapp.task
def regular_task():
coro = async_task()
asyncio.run(coro)
Ventajas obvias:- 隆Funciona!
- Simplemente
- Sin dependencias externas adicionales
驴Qu茅 est谩 realmente mal?- Event Loop se crea dentro de cada trabajador.
- No hay cambio de contexto entre la ejecuci贸n de corutinas
- En un momento dado, el trabajador ejecuta no m谩s de una rutina
- Los recursos compartidos no se comparten entre tareas
- Repetitivo
Es decir, asyncio en este caso se usa para asyncio, pero no hay ventajasVamos a intentar m谩s complicado?
Luch茅 por el rendimiento:import asyncio
import threading
from .celeryapp import celeryapp
celeryapp.loop = asyncio.get_event_loop()
celeryapp.loop_runner = threading.Thread(
target=celeryapp.loop.run_forever,
daemon=True,
)
celeryapp.loop_runner.start()
async def async_task():
await asyncio.sleep(42)
@celeryapp.task
def regular_task():
coro = async_task()
asyncio.run_coroutine_threadsafe(
coro=coro,
loop=celeryapp.loop,
)
隆Bingo! Pros:- A煤n trabajando
- A煤n m谩s o menos efectivo
- Incluso los recursos se mezclan entre las rutinas dentro del trabajador.
- Todav铆a no hay dependencias externas adicionales
驴Bingo? Los problemas no tardaron en llegar:- El apio no sabe nada sobre correr corutina
- Pierdes el control sobre la ejecuci贸n de tareas
- Pierdes el control de las excepciones.
- Repetitivo
Para ir con una soluci贸n de ingenier铆a tan maravillosa en prod, mi mano de alguna manera no se levant贸Formulaci贸n del problema
- Deberia trabajar
- Las rutinas deben ser competitivas
- Los recursos deben compartirse entre muchos ejecutables
- Sin repetitivo
- API predecible simple
Es decir, mis expectativas:import asyncio
from .celeryapp import celeryapp
@celeryapp.task
async def async_task():
await asyncio.sleep(42)
Resumen
Implement茅 mis ideas en la biblioteca celery-pool-asyncio . Nuestro equipo utiliza esta biblioteca en el proyecto actual y ya la hemos implementado.Caracter铆sticas brevemente
Adem谩s de ejecutar directamente tareas de asyncio, celery-pool-asyncio tambi茅n resuelve problemas:- Eliminaci贸n de tareas asincr贸nicas
- Soporte de corutina en se帽ales de apio
Para que el apio-pool-asyncio funcione, us茅 parches de mono . Para cada parche utilizado en tiempo de ejecuci贸n, es posible deshabilitarlo.Puede leer m谩s sobre todo esto en la documentaci贸n.Planes
En el buen sentido, debe integrar la piscina en el apio. Por otro lado, los desarrolladores de apio est谩n creando prototipos de apio 5.0, que ser谩 as铆ncrono. 驴Vale la pena el juego?Ejemplos
Para demostrar las capacidades de mis bibliotecas celery-pool-asyncio y celery-decorator-taskcls ( art铆culo ), se implement贸 un proyecto de prueba .Otro
Trat茅 de decir lo mismo en el mitap