Tus metas son:
- Distribuya su trabajo a muchas máquinas (computación distribuida / procesamiento paralelo distribuido)
- Distribuir el trabajo en una máquina determinada en todas las CPU (multiprocesamiento / subprocesamiento)
El apio puede hacer ambas cosas con bastante facilidad. Lo primero que hay que entender es que cada trabajador de apio está configurado de forma predeterminada para ejecutar tantas tareas como núcleos de CPU haya disponibles en un sistema:
La simultaneidad es el número de procesos de trabajo previos a la bifurcación utilizados para procesar sus tareas al mismo tiempo, cuando todos ellos están ocupados haciendo trabajo, las nuevas tareas tendrán que esperar a que finalice una de las tareas antes de que pueda procesarse.
El número de concurrencia predeterminado es el número de CPU en esa máquina (incluidos los núcleos) , puede especificar un número personalizado usando la opción -c. No hay un valor recomendado, ya que el número óptimo depende de varios factores, pero si sus tareas están en su mayoría vinculadas a E / S, entonces puede intentar aumentarlo, la experimentación ha demostrado que agregar más del doble de la cantidad de CPU rara vez es eficaz, y en su lugar probablemente degradará el rendimiento.
Esto significa que cada tarea individual no necesita preocuparse por el uso de multiprocesamiento / subprocesamiento para hacer uso de múltiples CPU / núcleos. En cambio, el apio ejecutará suficientes tareas al mismo tiempo para usar cada CPU disponible.
Con eso fuera del camino, el siguiente paso es crear una tarea que maneje el procesamiento de algún subconjunto de su list_of_millions_of_ids
. Aquí tiene un par de opciones: una es hacer que cada tarea maneje una única ID, por lo que ejecuta N tareas, donde N == len(list_of_millions_of_ids)
. Esto garantizará que el trabajo se distribuya uniformemente entre todas sus tareas, ya que nunca habrá un caso en el que un trabajador termine antes de tiempo y simplemente esté esperando; si necesita trabajo, puede sacar una identificación de la cola. Puede hacer esto (como lo menciona John Doe) usando un apiogroup
.
tasks.py:
@app.task
def process_id(item):
id = item
database.objects(newid=id).save()
Y para ejecutar las tareas:
from celery import group
from tasks import process_id
jobs = group(process_id.s(item) for item in list_of_millions_of_ids)
result = jobs.apply_async()
Otra opción es dividir la lista en partes más pequeñas y distribuirlas a sus trabajadores. Este enfoque corre el riesgo de desperdiciar algunos ciclos, porque puede terminar con algunos trabajadores esperando mientras otros todavía están trabajando. Sin embargo, la documentación del apio señala que esta preocupación a menudo es infundada:
A algunos les puede preocupar que dividir sus tareas resulte en una degradación del paralelismo, pero esto rara vez es cierto para un clúster ocupado y, en la práctica, dado que está evitando la sobrecarga de la mensajería, puede aumentar considerablemente el rendimiento.
Por lo tanto, puede encontrar que fragmentar la lista y distribuir los fragmentos a cada tarea funciona mejor, debido a la reducción de la sobrecarga de mensajería. Probablemente también pueda aligerar un poco la carga en la base de datos de esta manera, calculando cada identificación, almacenándola en una lista y luego agregando la lista completa en la base de datos una vez que haya terminado, en lugar de hacerlo una identificación a la vez . El enfoque de fragmentación se vería así
tasks.py:
@app.task
def process_ids(items):
for item in items:
id = item
database.objects(newid=id).save()
Y para empezar las tareas:
from tasks import process_ids
jobs = process_ids.chunks(list_of_millions_of_ids, 30)
jobs.apply_async()
Puede experimentar un poco con qué tamaño de fragmento le da el mejor resultado. Desea encontrar un punto óptimo en el que reduzca la sobrecarga de mensajería y, al mismo tiempo, mantenga el tamaño lo suficientemente pequeño como para no terminar con los trabajadores terminando su parte mucho más rápido que otro trabajador y luego simplemente esperando sin nada que hacer.