es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Gunicorn + ThreadPoolExecutor: ¿Cómo manejar múltiples solicitudes de usuario simultáneas?

Quiero servir una API de Falcon que pueda manejar múltiples solicitudes de usuario simultáneas.
Cada solicitud desencadena una tarea de procesamiento larga, por lo que utilicé el ThreadPoolExecutor de concurrent.futures de la siguiente manera:

import falcon
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=10)

class Resource:

<pre><code>def on_post(self, req, resp):

    def some_long_task():
        # aquí está el código de la tarea larga

    executor.submit(some_long_task)

    resp.body = 'OK'
    resp.status = falcon.HTTP_201
</code></pre>

app = falcon.App()

resource = Resource()

app.add_route('/', resource)

<h1>Servir...</h1>

Estoy usando gunicorn para servir la API, con los siguientes parámetros: gunicorn main:app --timeout 10000.

Cuando ejecuto 2 solicitudes a la API sucesivamente, ambas tareas largas se activan en segundo plano en una fila.
Sin embargo, tan pronto como se finaliza la primera tarea larga lanzada, detiene la ejecución de la segunda. ¿Cómo puedo evitar esto?

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Para evitar que la ejecución del segundo largo proceso se detenga por el primero, debes utilizar el método as_completed del módulo concurrent.futures. Este método devuelve un iterador sobre los objetos futuros en el orden en que completan, lo que te permite procesar los resultados a medida que estén disponibles sin esperar que se complete todo el conjunto de tareas.

    Aquí te mostramos cómo modificar tu código existente para utilizar as_completed:

    import falcon
    from concurrent.futures import ThreadPoolExecutor, as_completed
    
    executor = ThreadPoolExecutor(max_workers=10)
    
    class Resource:
    
        def on_post(self, req, resp):
    
            def some_long_task():
                # aquí está el código para el largo proceso
    
            future = executor.submit(some_long_task)
    
            resp.body = 'OK'
            resp.status = falcon.HTTP_201
    
            for f in as_completed([future]):
                f.result()
    
    app = falcon.App()
    
    resource = Resource()
    
    app.add_route('/', resource)
    
    # Sirve...
    

    Con esta modificación, cada tarea se enviará al grupo de hilos como antes, pero también esperarás a que cada tarea se complete antes de devolver la respuesta al cliente. Esto garantiza que todas las tareas se ejecuten y completen, independientemente del orden en que finalicen.

Comments are closed.