Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

254
Vistas
Los trabajadores asincrónicos de Uvicorn siguen trabajando sincrónicamente

pregunta en resumen

Migré mi proyecto de Django 2.2 a Django 3.2, y ahora quiero comenzar a usar la posibilidad de vistas asíncronas. Creé una vista asíncrona, configuré la configuración asgi y ejecuté gunicorn con un trabajador de Uvicorn. Al llenar este servidor con 10 usuarios al mismo tiempo, se sirven de forma sincrónica. ¿Qué necesito configurar para servir a 10 usuarios simultáneos una vista asíncrona?

pregunta en detalle

Esto es lo que hice hasta ahora en mi entorno local:

  • Estoy trabajando con Django 3.2.10 y Python 3.9.
  • He instalado gunicorn y uvicorn a través de pip
  • He creado un archivo asgi.py con los siguientes contenidos
 import os from django.core.asgi import get_asgi_application os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyService.settings.local') application = get_asgi_application()
  • Creé una vista con la siguiente implementación y la conecté en urlpatterns :
 import asyncio import json from django.http import HttpResponse async def async_sleep(request): await asyncio.sleep(1) return HttpResponse(json.dumps({'mode': 'async', 'time': 1).encode())
  • Ejecuto localmente un servidor de gunicorn con un trabajador de Uvicorn:
 gunicorn MyService.asgi:application -k uvicorn.workers.UvicornWorker [2022-01-26 14:37:14 +0100] [8732] [INFO] Starting gunicorn 20.1.0 [2022-01-26 14:37:14 +0100] [8732] [INFO] Listening at: http://127.0.0.1:8000 (8732) [2022-01-26 14:37:14 +0100] [8732] [INFO] Using worker: uvicorn.workers.UvicornWorker [2022-01-26 14:37:14 +0100] [8733] [INFO] Booting worker with pid: 8733 [2022-01-26 13:37:15 +0000] [8733] [INFO] Started server process [8733] [2022-01-26 13:37:15 +0000] [8733] [INFO] Waiting for application startup. [2022-01-26 13:37:15 +0000] [8733] [INFO] ASGI 'lifespan' protocol appears unsupported. [2022-01-26 13:37:15 +0000] [8733] [INFO] Application startup complete.
  • Golpeé la API de un cliente local una vez. Después de 1 segundo, obtengo un 200 OK, como se esperaba.
  • Configuré un servidor Locust para generar usuarios simultáneos. Cuando dejo que haga solicitudes con 1 usuario simultáneo, cada 1 segundo se completa una llamada a la API.
  • Cuando dejo que realice solicitudes con 10 usuarios simultáneos, cada 1 segundo se completa una llamada API. Todas las demás solicitudes están esperando.

Esto último no es lo que esperaba. Espero que el trabajador, mientras duerme de forma asíncrona, ya recoja la siguiente solicitud. ¿Me estoy perdiendo alguna configuración?

También lo probé usando Daphne en lugar de Uvicorn, pero con el mismo resultado.

Langosta

Así es como he puesto mi langosta.

  • Iniciar un nuevo virtualenv
  • pip install locust
  • Cree un locustfile.py con el siguiente contenido:
 from locust import HttpUser, task class SleepUser(HttpUser): @task def async_sleep(self): self.client.get('/api/async_sleep/')
  • Ejecute el ejecutable Locust desde el shell
  • Visite http://0.0.0.0:8089 en el navegador
  • Establezca el número de trabajadores en 10, la tasa de generación en 1 y el host en http://127.0.0.1:8000

software intermedio

Estas son mis configuraciones de middleware

 MIDDLEWARE = [ 'django_prometheus.middleware.PrometheusBeforeMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.gzip.GZipMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.security.SecurityMiddleware', 'shared.common.middleware.ApiLoggerMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware', ]

El ApiLoggerMiddleware de shared es de nuestro propio código, primero investigaré este. Esta es la implementación de la misma.

 import logging import os from typing import List from django.http import HttpRequest, HttpResponse from django.utils import timezone from shared.common.authentication_service import BaseAuthenticationService class ApiLoggerMiddleware: TOO_BIG_FOR_LOG_BYTES = 2 * 1024 def __init__(self, get_response): # The get_response callable is provided by Django, it is a function # that takes a request and returns a response. Plainly put, once we're # done with the incoming request, we need to pass it along to get the # response which we need to ultimately return. self._get_response = get_response self.logger = logging.getLogger('api') self.pid = os.getpid() self.request_time = None self.response_time = None def __call__(self, request: HttpRequest) -> HttpResponse: common_data = self.on_request(request) response = self._get_response(request) self.on_response(response, common_data) return response def truncate_body(self, request: HttpRequest) -> str: return f"{request.body[:self.TOO_BIG_FOR_LOG_BYTES]}" def on_request(self, request: HttpRequest) -> List[str]: self.request_time = timezone.now() remote_address = self.get_remote_address(request) user_agent = request.headers.get('User-Agent') or '' customer_uuid = self.get_customer_from_request_auth(request) method = request.method uri = request.get_raw_uri() common = [ remote_address, user_agent, customer_uuid, method, uri ] in_line = [ "IN", str(self.pid), str(self.request_time), ] + common + [ self.truncate_body(request) ] self.logger.info(', '.join(in_line)) return common def on_response(self, response: HttpResponse, common: List[str]) -> None: self.response_time = timezone.now() out_line = [ "OUT", str(self.pid), str(self.response_time) ] + common + [ str(self.response_time - self.request_time), str(response.status_code), ] self.logger.info(", ".join(out_line)) @classmethod def get_customer_from_request_auth(cls, request: HttpRequest) -> str: token = request.headers.get('Authorization') if not token: return 'no token' try: payload = BaseAuthenticationService.validate_access_token(token) return payload.get('amsOrganizationId', '') except Exception: return 'unknown' @classmethod def get_remote_address(cls, request: HttpRequest) -> str: if 'X-Forwarded-For' in request.headers: # in case the request comes in through a proxy, the remote address # will be just the last proxy that passed it along, that's why we # have to get the remote from X-Forwarded-For # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For addresses = request.headers['X-Forwarded-For'].split(',') client = addresses[0] return client else: return request.META.get('REMOTE_ADDR', '')

Fuentes

Fuentes que he usado:

  • Una guía de ASGI en Django 3.0 y su desempeño
  • Cómo usar Django con Uvicorn
over 4 years ago · Santiago Trujillo
1 Respuestas
Responde la pregunta

0

Al ejecutar el comando gunicorn , puede intentar agregar el parámetro de workers usando las opciones -w o --workers .

El valor predeterminado es 1 , como se indica en la documentación de gunicorn . Es posible que desee intentar aumentar ese valor.

Ejemplo de uso:

 gunicorn MyService.asgi:application -k uvicorn.workers.UvicornWorker -w 10

Además, es posible que deba verificar otros atributos de trabajadores en la documentación, como worker_class y threads .

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda