Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Al desarrollar para Azure Functions mediante Python, debe comprender cómo funcionan las funciones y cómo afecta ese rendimiento a la forma en que se escala la aplicación de funciones. La necesidad es más importante al diseñar aplicaciones de alto rendimiento. Los principales factores que se deben tener en cuenta al diseñar, escribir y configurar las aplicaciones de funciones son las configuraciones de rendimiento y escalado horizontales.
Escalado horizontal
De forma predeterminada, Azure Functions supervisa automáticamente la carga en la aplicación y crea más instancias de host para Python según sea necesario. Azure Functions usa umbrales integrados para distintos tipos de desencadenadores para decidir cuándo agregar instancias, como la antigüedad de los mensajes y el tamaño de la cola para QueueTrigger. Estos umbrales no son configurables por el usuario. Para obtener más información, vea Escalado controlado por eventos en Azure Functions.
Mejora del rendimiento
Las configuraciones predeterminadas son adecuadas para la mayoría de las aplicaciones de Azure Functions. Sin embargo, puede mejorar el rendimiento del flujo de trabajo de sus aplicaciones empleando configuraciones ajustadas a su perfil de carga de trabajo. El primer paso es comprender el tipo de carga de trabajo que está ejecutando.
| Tipo de carga de trabajo | Características de la aplicación de funciones | Ejemplos |
|---|---|---|
| Limitado por E/S | • La aplicación necesita controlar muchas invocaciones simultáneas. • La aplicación procesa un gran número de eventos de E/S, como llamadas de red y lectura y escritura en disco. |
• API Web |
| Limitado por la CPU | • La aplicación realiza cálculos de larga duración, como el cambio de tamaño de la imagen. • La aplicación realiza la transformación de datos. |
• Procesamiento de datos • Inferencia de Aprendizaje automático |
Como las cargas de trabajo de las funciones reales suelen ser una combinación dependiente de E/S y CPU, debe generar un perfil de la aplicación bajo cargas de producción realistas.
Configuraciones específicas del rendimiento
Después de comprender el perfil de carga de trabajo de la aplicación de funciones, a continuación se muestran las configuraciones que puede usar para mejorar el rendimiento de las funciones.
- Async
- Empleado multilingüe
- Máximo de trabajos en un proceso de trabajo de lenguaje
- Bucle de eventos
- Escalado vertical
Async
Dado que Python es un entorno de ejecución de un solo subproceso, una instancia de host para Python solo puede procesar una invocación de función a la vez de forma predeterminada. En el caso de las aplicaciones que procesan un gran número de eventos de E/S o están enlazados a E/S, puede mejorar significativamente el rendimiento mediante la ejecución de funciones de forma asincrónica.
Para ejecutar una función de forma asincrónica, use la instrucción , que ejecuta la función directamente con asyncio:
async def main():
await some_nonblocking_socket_io_op()
Este es un ejemplo de una función con desencadenador HTTP que usa aiohttp http client:
import aiohttp
import azure.functions as func
async def main(req: func.HttpRequest) -> func.HttpResponse:
async with aiohttp.ClientSession() as client:
async with client.get("PUT_YOUR_URL_HERE") as response:
return func.HttpResponse(await response.text())
return func.HttpResponse(body='NotFound', status_code=404)
Una función sin la palabra clave se ejecuta automáticamente en un grupo de subprocesos ThreadPoolExecutor:
# Runs in a ThreadPoolExecutor threadpool. Number of threads is defined by PYTHON_THREADPOOL_THREAD_COUNT.
# The example is intended to show how default synchronous functions are handled.
def main():
some_blocking_socket_io()
Para lograr el beneficio completo de ejecutar funciones de forma asincrónica, la operación o biblioteca de E/S que se utiliza en el código también debe tener implementada la función asincrónica. El uso de operaciones de E/S sincrónicas en funciones definidas como asincrónicas puede afectar al rendimiento general. Si las bibliotecas que usa no tienen implementada la versión asincrónica, es posible que se beneficie de ejecutar el código de forma asincrónica mediante la administración del bucle de eventos en la aplicación.
Estos son algunos ejemplos de bibliotecas cliente que han implementado patrones asincrónicos:
- aiohttp : cliente http/servidor para asyncio
- Streams API: primitivas preparadas para async/await de alto nivel para trabajar con la conexión de red.
- Janus Queue: cola con reconocimiento de asyncio segura para subprocesos para Python
- pyzmq: enlaces de Python para ZeroMQ
Descripción de async en un trabajo de Python
Al definir async delante de una firma de función, Python marca la función como una corrutina. Cuando se llama a la corrutina, se puede programar como una tarea en un bucle de eventos. Cuando llamas a una función asincrónica, registra una continuación en el bucle de eventos, lo que permite que el bucle de eventos procese la siguiente tarea durante el tiempo de espera.
En nuestro Python Worker, el worker comparte el bucle de eventos con la función async del cliente y es capaz de manejar múltiples solicitudes simultáneamente. Recomendamos encarecidamente a nuestros clientes que usen bibliotecas compatibles con asyncio, como aiohttp y pyzmq. Estas recomendaciones aumentan el rendimiento de la función en comparación con esas bibliotecas cuando se implementan de forma sincrónica.
Nota:
Si la función se declara como async sin ningún await dentro de su implementación, el rendimiento de la función se verá afectado gravemente, ya que el bucle de eventos se bloqueará, lo que prohíbe que el trabajo de Python controle solicitudes simultáneas.
Uso de procesos de trabajo de varios lenguajes
De forma predeterminada, cada instancia de host de Functions tiene un único proceso de trabajo de lenguaje. Puede aumentar el número de procesos de trabajo por host (hasta 10) mediante la configuración de la aplicación. Azure Functions luego intenta distribuir uniformemente simultáneas invocaciones de funciones entre estos trabajadores.
En el caso de las aplicaciones dependientes de CPU, debe establecer el número de trabajos de lenguaje para que sea igual o mayor que el número de núcleos disponibles por aplicación de funciones. Para obtener más información, consulte los SKU de instancias disponibles.
Las aplicaciones enlazadas a E/S también pueden beneficiarse de aumentar el número de procesos de trabajo más allá del número de núcleos disponibles. Tenga en cuenta que configurar el número de trabajadores demasiado alto puede afectar al rendimiento general debido al aumento del número de cambios de contexto necesarios.
El FUNCTIONS_WORKER_PROCESS_COUNT se aplica a cada host que Azure Functions crea al escalar horizontalmente la aplicación para satisfacer la demanda.
Configuración de un máximo de trabajos en un proceso de trabajo de lenguaje
Como se mencionó en la sección asincrónica, el trabajador del lenguaje Python trata funciones y corutinas de manera diferente. Una corrutina se ejecuta dentro del mismo bucle de eventos en el que se ejecuta el trabajo del lenguaje. Por otro lado, una invocación de función se ejecuta dentro de ThreadPoolExecutor, que se mantiene mediante el trabajo del lenguaje, como un subproceso.
Puede establecer el valor de los trabajos máximos permitidos para ejecutar funciones de sincronización mediante la configuración de la aplicación PYTHON_THREADPOOL_THREAD_COUNT . Este valor establece el argumento max_worker del objeto ThreadPoolExecutor, que permite a Python usar un grupo de como máximo max_worker subprocesos para ejecutar llamadas de forma asincrónica. El PYTHON_THREADPOOL_THREAD_COUNT se aplica a cada trabajo que crea el host de Functions y Python decide cuándo crear un nuevo subproceso o reutilizar el subproceso inactivo existente. Para versiones anteriores de Python(es decir, 3.8, 3.7 y 3.6), max_worker valor se establece en 1. Para Python versión 3.9 , max_worker se establece en None.
En el caso de las aplicaciones dependientes de la CPU, debe mantener la configuración en un número bajo. Empiece por 1 y vaya aumentándolo a medida que experimente con la carga de trabajo. Esta sugerencia consiste en reducir el tiempo invertido en los conmutadores de contexto y permitir que finalicen las tareas enlazadas a la CPU.
En el caso de las aplicaciones dependientes de E/S, debería obtener beneficios considerables si aumenta el número de subprocesos que trabajan en cada invocación. La recomendación es empezar con el Python predeterminado (el número de núcleos) + 4 y, a continuación, ajustar en función de los valores de rendimiento que ve.
En el caso de las aplicaciones con cargas de trabajo mixtas, debe equilibrar las configuraciones de y para maximizar el rendimiento. Para comprender a qué dedican más tiempo las aplicaciones de funciones, se recomienda realizar perfiles de rendimiento y ajustar los valores según sus comportamientos. Para obtener información sobre esta configuración de aplicación, consulte Uso de varios procesos de trabajo.
Nota:
Aunque estas recomendaciones se aplican a las funciones desencadenadas por HTTP y no HTTP, es posible que tenga que ajustar otras configuraciones específicas del desencadenador para funciones desencadenadas que no son HTTP para obtener el rendimiento esperado de las aplicaciones de funciones. Para obtener más información sobre esto, consulte las Mejores prácticas para funciones confiables de Azure.
Administración del bucle de eventos
Debe usar bibliotecas de terceros compatibles con asyncio. Si ninguna de las bibliotecas de terceros satisface sus necesidades, también puede administrar los bucles de eventos en Azure Functions. La administración de bucles de eventos proporciona más flexibilidad en la administración de recursos de proceso y también permite encapsular bibliotecas de E/S sincrónicas en corrutinas.
Hay muchos documentos oficiales Python útiles que describen los Coroutines y Tasks y Event Loop mediante la biblioteca integrada asyncio.
Tome la siguiente biblioteca de solicitudes como ejemplo, este fragmento de código usa la biblioteca asincrónica para encapsular el método en una corrutina, ejecutando varias solicitudes web para SAMPLE_URL simultáneamente.
import asyncio
import json
import logging
import azure.functions as func
from time import time
from requests import get, Response
async def invoke_get_request(eventloop: asyncio.AbstractEventLoop) -> Response:
# Wrap requests.get function into a coroutine
single_result = await eventloop.run_in_executor(
None, # using the default executor
get, # each task call invoke_get_request
'SAMPLE_URL' # the url to be passed into the requests.get function
)
return single_result
async def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
eventloop = asyncio.get_event_loop()
# Create 10 tasks for requests.get synchronous call
tasks = [
asyncio.create_task(
invoke_get_request(eventloop)
) for _ in range(10)
]
done_tasks, _ = await asyncio.wait(tasks)
status_codes = [d.result().status_code for d in done_tasks]
return func.HttpResponse(body=json.dumps(status_codes),
mimetype='application/json')
Escalado vertical
Es posible que pueda obtener más unidades de procesamiento, especialmente en operaciones dependientes de la CPU, actualizando al plan premium con especificaciones más altas. Con unidades de procesamiento más altas, puede ajustar el número de procesos de trabajo según el número de núcleos disponibles y lograr un mayor grado de paralelismo.
Pasos siguientes
Para obtener más información sobre el desarrollo de Azure Functions Python, consulte los siguientes recursos:
- guía para desarrolladores Azure Functions Python
- Mejores prácticas para Azure Functions
- Azure Functions referencia para desarrolladores