Apio + asincio

Hola Habr! Quiero contar c贸mo resolv铆 el problema de la ejecuci贸n competitiva efectiva de las tareas de asincio en Celery.

KDPV

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
#839. . mode off.

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 ventajas

Vamos 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


All Articles