Generar perfiles de uso de memoria de aplicaciones de Python en Azure Functions

Durante el desarrollo o después de implementar el proyecto de aplicación de función de Python local en Azure, es recomendable analizar los posibles cuellos de botella de memoria en las funciones. Estos cuellos de botella pueden reducir el rendimiento de las funciones y provocar errores. Las siguientes instrucciones muestran cómo usar el paquete de Python del generador de perfiles de memoria, que proporciona análisis de consumo de memoria de línea por línea de las funciones a medida que se ejecutan.

Nota

La generación de perfiles de memoria solo está diseñada para el análisis de la superficie de memoria en entornos de desarrollo. No aplique el generador de perfiles de memoria en las aplicaciones de función de producción.

Prerrequisitos

Antes de empezar a desarrollar una aplicación de función de Python, debe cumplir estos requisitos:

Si no tiene una suscripción a Azure, cree una cuenta gratuita de Azure antes de empezar.

Proceso de generación de perfiles de memoria

  1. En el requirements.txt, agregue memory-profiler para asegurarse de que el paquete se agrupa con la implementación. Si está desarrollando en el equipo local, puede que desee activar un entorno virtual de Python y realizar una resolución de paquetes mediante pip install -r requirements.txt.

  2. En el script de función (por ejemplo, __init__.py para el modelo de programación de Python v1 y function_app.py para el modelo v2), agregue las líneas siguientes encima de la función main(). Estas líneas garantizan que el registrador raíz informe de los nombres del registrador secundario, de modo que el prefijo pueda distinguir los registros de generación de perfiles de memoria memory_profiler_logs.

    import logging
    import memory_profiler
    root_logger = logging.getLogger()
    root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
    profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)
    
  3. Aplique el siguiente decorador sobre cualquier función que necesite la generación de perfiles de memoria. El decorador no funciona directamente en el método main() del punto de entrada del desencadenador. Debe crear subfunciones y decorarlas. Asimismo, debido a un problema conocido de memory-profiler, al aplicar a una corrutina asincrónica, el valor devuelto de la corrutina siempre es None.

    @memory_profiler.profile(stream=profiler_logstream)
    
  4. Pruebe el generador de perfiles de memoria en el equipo local mediante el comando de Azure Functions Core Tools func host start. Al invocar las funciones, deben generar un informe de uso de memoria. El informe contiene el nombre de archivo, la línea de código, el uso de memoria, el incremento de memoria y el contenido de línea que contiene.

  5. Para comprobar los registros de perfil de memoria en una instancia de aplicación de función existente en Azure, puede consultar los registros de generación de perfiles de la memoria para las invocaciones recientes con las consultas de Kusto en Application Insights y en los registros.

    Screenshot showing the query memory usage of a Python app in Application Insights.

    traces
    | where timestamp > ago(1d)
    | where message startswith_cs "memory_profiler_logs:"
    | parse message with "memory_profiler_logs: " LineNumber "  " TotalMem_MiB "  " IncreMem_MiB "  " Occurrences "  " Contents
    | union (
        traces
        | where timestamp > ago(1d)
        | where message startswith_cs "memory_profiler_logs: Filename: "
        | parse message with "memory_profiler_logs: Filename: " FileName
        | project timestamp, FileName, itemId
    )
    | project timestamp, LineNumber=iff(FileName != "", FileName, LineNumber), TotalMem_MiB, IncreMem_MiB, Occurrences, Contents, RequestId=itemId
    | order by timestamp asc
    

Ejemplo

Este es un ejemplo de cómo realizar la generación de perfiles de memoria en un desencadenador HTTP asincrónicos y sincrónicos, denominados "HttpTriggerAsync" y "HttpTriggerSync", respectivamente. Crearemos una aplicación de función de Python que simplemente envíe solicitudes GET a la Página principal de Microsoft.

Creación de la aplicación de funciones de Python

Una aplicación de funciones de Python debe seguir la estructura de carpetasde Azure Functions especificada. Para aplicar la técnica scaffolding al proyecto, se recomienda usar el Azure Functions Core Tools mediante la ejecución de los siguientes comandos:

func init PythonMemoryProfilingDemo --python
cd PythonMemoryProfilingDemo
func new -l python -t HttpTrigger -n HttpTriggerAsync -a anonymous
func new -l python -t HttpTrigger -n HttpTriggerSync -a anonymous

Actualizar contenidos del archivo

El requirements.txt define los paquetes que se usan en nuestro proyecto. Además del SDK de Azure Functions y el generador de perfiles de memoria, se introduce aiohttp para las solicitudes HTTP asincrónicas y requests para las llamadas HTTP sincrónicas.

# requirements.txt

azure-functions
memory-profiler
aiohttp
requests

Cree el desencadenador HTTP asincrónico.

Reemplace el código del desencadenador HTTP asincrónico HttpTriggerAsync/__init__.py por el código siguiente, que configura el generador de perfiles de memoria, el formato del registrador raíz y el enlace de streaming del registrador.

# HttpTriggerAsync/__init__.py

import azure.functions as func
import aiohttp
import logging
import memory_profiler

# Update root logger's format to include the logger name. Ensure logs generated
# from memory profiler can be filtered by "memory_profiler_logs" prefix.
root_logger = logging.getLogger()
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)

async def main(req: func.HttpRequest) -> func.HttpResponse:
    await get_microsoft_page_async('https://microsoft.com')
    return func.HttpResponse(
        f"Microsoft page loaded.",
        status_code=200
    )

@memory_profiler.profile(stream=profiler_logstream)
async def get_microsoft_page_async(url: str):
    async with aiohttp.ClientSession() as client:
        async with client.get(url) as response:
            await response.text()
    # @memory_profiler.profile does not support return for coroutines.
    # All returns become None in the parent functions.
    # GitHub Issue: https://github.com/pythonprofilers/memory_profiler/issues/289

Cree el desencadenador HTTP sincrónico.

Reemplace el código del desencadenador HTTP asincrónico HttpTriggerSync/__init__.py por el código siguiente.

# HttpTriggerSync/__init__.py

import azure.functions as func
import requests
import logging
import memory_profiler

# Update root logger's format to include the logger name. Ensure logs generated
# from memory profiler can be filtered by "memory_profiler_logs" prefix.
root_logger = logging.getLogger()
root_logger.handlers[0].setFormatter(logging.Formatter("%(name)s: %(message)s"))
profiler_logstream = memory_profiler.LogFile('memory_profiler_logs', True)

def main(req: func.HttpRequest) -> func.HttpResponse:
    content = profile_get_request('https://microsoft.com')
    return func.HttpResponse(
        f"Microsoft page response size: {len(content)}",
        status_code=200
    )

@memory_profiler.profile(stream=profiler_logstream)
def profile_get_request(url: str):
    response = requests.get(url)
    return response.content

Generar perfiles de la aplicación de funciones de Python en el entorno de desarrollo local

Después de realizar todos los cambios anteriores, hay algunos pasos más para inicializar un entorno virtual de Python para el tiempo de ejecución de Azure Functions.

  1. Abra Windows PowerShell o cualquier Shell de Linux de su preferencia.

  2. Cree un entorno virtual de Python py -m venv .venv en Windows o python3 -m venv .venv en Linux.

  3. Active el entorno virtual de Python con .venv\Scripts\Activate.ps1 en Windows PowerShell o source .venv/bin/activate en el shell de Linux.

  4. Restaure las dependencias de Python con pip install -r requirements.txt

  5. Inicie el tiempo de ejecución de Azure Functions de forma local con Azure Functions Core Tools func host start

  6. Envíe una solicitud GET a la aplicación https://localhost:7071/api/HttpTriggerAsync o https://localhost:7071/api/HttpTriggerSync.

  7. Debería mostrar un informe de generación de perfiles de memoria similar a la sección siguiente en Azure Functions Core Tools.

    Filename: <ProjectRoot>\HttpTriggerAsync\__init__.py
    Line #    Mem usage    Increment  Occurrences   Line Contents
    ============================================================
        19     45.1 MiB     45.1 MiB           1   @memory_profiler.profile
        20                                         async def get_microsoft_page_async(url: str):
        21     45.1 MiB      0.0 MiB           1       async with aiohttp.ClientSession() as client:
        22     46.6 MiB      1.5 MiB          10           async with client.get(url) as response:
        23     47.6 MiB      1.0 MiB           4               await response.text()
    

Pasos siguientes

Para obtener más información sobre el desarrollo de Python de Azure Functions, consulte los siguientes recursos: