Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Lors du développement pour Azure Functions à l’aide de Python, vous devez comprendre comment vos fonctions fonctionnent et comment ces performances affectent la façon dont votre application de fonction est mise à l’échelle. Le besoin est plus important lors de la conception d’applications hautement performantes. Les principaux facteurs à prendre en compte lors de la conception, du développement et de la configuration de vos applications de fonction sont les configurations de mise à l’échelle horizontale et les performances de débit.
Mise à l’échelle horizontale
Par défaut, Azure Functions surveille automatiquement la charge sur votre application et crée davantage d’instances d’hôte pour Python si nécessaire. Azure Functions utilise des seuils intégrés pour différents types de déclencheurs afin de décider quand ajouter des instances, telles que l’âge des messages et la taille de file d’attente pour QueueTrigger. Ces seuils ne sont pas configurables par l’utilisateur. Pour plus d’informations, consultez Mise à l’échelle basée sur les événements dans Azure Functions.
Amélioration des performances du débit
Les configurations par défaut conviennent à la plupart des applications Azure Functions. Toutefois, vous pouvez améliorer les performances du débit de vos applications en utilisant des configurations basées sur votre profil de charge de travail. La première étape consiste à comprendre le type de charge de travail que vous exécutez.
| Type de charge de travail | Caractéristiques de l'application fonctionnelle | Exemples |
|---|---|---|
| Lié aux E/S | • L’application doit gérer de nombreux appels simultanés. • L’application traite un grand nombre d’événements d’E/S, tels que les appels réseau et les lectures/écritures de disque. |
• API web |
| Limité par le processeur | • L’application effectue des calculs de longue durée, tels que le redimensionnement d’images. • L’application effectue une transformation de données. |
• Traitement des données • Inférence de Machine Learning |
Étant donné que les charges de travail des fonctions réelles sont généralement une combinaison d’E/S et d’UC, vous devez profiler l’application sous des charges de production réalistes.
Configurations spécifiques aux performances
Une fois que vous avez compris le profil de charge de travail de votre application de fonction, voici les configurations que vous pouvez utiliser pour améliorer les performances de débit de vos fonctions.
- Asynchrone
- Travail de langue multiple
- Nombre maximal de workers au sein d’un processus de travail linguistique
- Boucle d’événements
- Mise à l’échelle verticale
Asynchrone
Étant donné que Python est un runtime à thread unique, une instance d’hôte pour Python ne peut traiter qu’un seul appel de fonction à la fois par défaut. Pour les applications qui traitent un grand nombre d’événements d’E/S et/ou qui sont liés aux E/S, vous pouvez améliorer considérablement les performances en exécutant des fonctions de manière asynchrone.
Pour exécuter une fonction de façon asynchrone, utilisez l’instruction async def , qui exécute la fonction avec asyncio directement :
async def main():
await some_nonblocking_socket_io_op()
Voici un exemple de fonction avec un déclencheur HTTP qui utilise 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)
Une fonction sans le async mot clé est exécutée automatiquement dans un pool de threads 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()
Pour bénéficier pleinement des fonctions en cours d’exécution de manière asynchrone, l’opération/bibliothèque d’E/S utilisée dans votre code doit également être implémentée de manière asynchrone. L’utilisation d’opérations d’E/S synchrones dans les fonctions définies comme asynchrones peut nuire aux performances globales. Si les bibliothèques que vous utilisez n’ont pas de version asynchrone implémentée, vous pouvez toujours tirer parti de l’exécution de votre code de manière asynchrone en gérant la boucle d’événements dans votre application.
Voici quelques exemples de bibliothèques clientes qui ont implémenté des modèles asynchrones :
- aiohttp - Client/serveur http pour asyncio
- API Streams - Primitives de haut niveau, prêtes pour async/await, pour travailler avec la connexion réseau
- Janus Queue - File d’attente asynchrone thread-safe pour Python
- pyzmq - liaisons de Python pour ZeroMQ
Présentation de l’async dans Python Worker
Lorsque vous définissez async devant une signature de fonction, Python marque la fonction en tant que coroutine. Lors de votre appel à la coroutine, elle peut être planifiée en tant que tâche dans une boucle d’événements. Lorsque vous appelez await une fonction asynchrone, elle inscrit une continuation dans la boucle d’événements, ce qui permet à la boucle d’événement de traiter la tâche suivante pendant le temps d’attente.
Dans notre Python Worker, le worker partage la boucle d'événement avec la fonction async du client et il est capable de gérer plusieurs requêtes simultanément. Nous encourageons fortement nos clients à utiliser des bibliothèques compatibles asyncio, telles qu’aiohttp et pyzmq. La mise en œuvre de ces recommandations augmente la performance de votre fonction comparée à celles des bibliothèques lorsqu'elles sont implémentées de manière synchrone.
Note
Si votre fonction est déclarée comme async sans aucune await à l’intérieur de son implémentation, les performances de votre fonction sont gravement affectées, car la boucle d’événements est bloquée, ce qui empêche le worker Python de gérer les requêtes simultanées.
Utiliser plusieurs processus de travail linguistique
Par défaut, chaque instance hôte Functions a un processus de travail de langage unique. Vous pouvez augmenter le nombre de processus de travail par hôte (jusqu’à 10) à l’aide du FUNCTIONS_WORKER_PROCESS_COUNT paramètre d’application. Azure Functions tente ensuite de distribuer uniformément des appels de fonction simultanés entre ces travailleurs.
Pour les applications liées au processeur, vous devez définir le nombre de Workers de langage de manière à ce qu’il soit supérieur ou égal au nombre de cœurs disponibles par application de fonction. Pour en savoir plus, consultez Les références SKU d’instance disponibles.
Les applications liées aux E/S peuvent également tirer parti de l’augmentation du nombre de processus de travail au-delà du nombre de cœurs disponibles. N’oubliez pas que la définition du nombre de travailleurs trop élevé peut affecter les performances globales en raison du nombre accru de commutateurs de contexte requis.
Le FUNCTIONS_WORKER_PROCESS_COUNT s’applique à chaque hôte créé par Azure Functions lors du scale-out de votre application pour répondre à la demande.
Configurer le nombre maximal de Workers au sein d’un processus Worker de langage
Comme mentionné dans la section async, l'ouvrier de langage Python traite les fonctions et les coroutines différemment. Une coroutine est exécutée dans la même boucle d’événements que le Worker de langage. En revanche, un appel de fonction est exécuté dans un ThreadPoolExecutor, qui est géré par le worker du langage en tant que thread.
Vous pouvez définir la valeur du nombre maximal de workers autorisés pour exécuter des fonctions de synchronisation à l’aide du paramètre d’application PYTHON_THREADPOOL_THREAD_COUNT . Cette valeur définit l’argument max_worker de l’objet ThreadPoolExecutor, qui permet Python utiliser un pool de threads max_worker pour exécuter des appels de manière asynchrone. Le PYTHON_THREADPOOL_THREAD_COUNT s’applique à chaque worker créé par l’hôte Functions, et Python décide quand créer un thread ou réutiliser le thread inactif existant. Pour les versions antérieures Python(autrement dit, 3.8, 3.7 et 3.6), max_worker valeur est définie sur 1. Pour Python version 3.9, max_worker est défini sur None.
Pour les applications liées au processeur, vous devez conserver le paramètre sur un nombre faible, en commençant par 1 et en augmentant à mesure que vous expérimentez votre charge de travail. Cette suggestion consiste à réduire le temps passé sur les commutateurs de contexte et à permettre la fin des tâches liées au processeur.
Pour les applications liées aux E/S, vous devez constater des gains substantiels en augmentant le nombre de threads travaillant sur chaque appel. La recommandation consiste à commencer par la Python par défaut (le nombre de cœurs) + 4, puis à ajuster en fonction des valeurs de débit que vous voyez.
Pour les applications avec des charges de travail mixtes, vous devez équilibrer les configurations FUNCTIONS_WORKER_PROCESS_COUNT et PYTHON_THREADPOOL_THREAD_COUNT pour optimiser le débit. Pour comprendre sur quoi vos applications de fonction consacrent le plus de temps, nous vous recommandons de les profiler et de définir les valeurs selon leurs comportements. Pour en savoir plus sur ces paramètres d’application, consultez Utiliser plusieurs processus de travail.
Note
Bien que ces recommandations s’appliquent à la fois aux fonctions déclenchées par HTTP et non HTTP, vous devrez peut-être ajuster d’autres configurations spécifiques au déclencheur pour les fonctions non déclenchées HTTP pour obtenir les performances attendues de vos applications de fonction. Pour plus d’informations sur ce sujet, reportez-vous à Meilleures pratiques pour des Azure Functions fiables.
Gestion de la boucle d’événements
Vous devez utiliser des bibliothèques tierces compatibles asyncio. Si aucune des bibliothèques tierces ne répond à vos besoins, vous pouvez également gérer les boucles d’événements dans Azure Functions. La gestion des boucles d’événements vous offre une plus grande flexibilité dans la gestion des ressources de calcul, et permet également d’encapsuler des bibliothèques d’E/S synchrones dans des coroutines.
Il existe de nombreux documents officiels Python utiles qui abordent les Coroutines et tâches et Event Loop à l’aide de la bibliothèque intégrée asyncio.
Prenez la bibliothèque de requêtes suivante comme exemple, cet extrait de code utilise la bibliothèque asyncio pour encapsuler la requests.get() méthode dans une coroutine, exécutant plusieurs requêtes web pour SAMPLE_URL simultanément.
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')
Mise à l’échelle verticale
Vous pouvez peut-être obtenir davantage d’unités de traitement, en particulier dans l’opération liée au processeur, en effectuant une mise à niveau vers un plan Premium avec des spécifications plus élevées. Avec des unités de traitement plus élevées, vous pouvez ajuster le nombre de processus de travail en fonction du nombre de cœurs disponibles et obtenir un degré plus élevé de parallélisme.
Prochaines étapes
Pour plus d’informations sur le développement Azure Functions Python, consultez les ressources suivantes :
- Azure Functions Python guide du développeur
- Meilleures pratiques pour Azure Functions
- Référence développeur pour Azure Functions