Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Bei der Entwicklung für Azure Functions mit Python müssen Sie verstehen, wie Ihre Funktionen ausgeführt werden und wie sich diese Leistung auf die Art und Weise auswirkt, wie ihre Funktions-App skaliert wird. Die Notwendigkeit ist beim Entwerfen von hoch leistungsfähigen Apps wichtiger. Die wichtigsten Faktoren, die Sie beim Entwerfen, Schreiben und Konfigurieren Ihrer Funktionen-Apps berücksichtigen sollten, sind horizontale Skalierungs- und Durchsatzleistungskonfigurationen.
Horizontale Skalierung
Standardmäßig überwacht Azure Functions die Last ihrer Anwendung automatisch und erstellt bei Bedarf weitere Hostinstanzen für Python. Azure Functions verwendet integrierte Schwellenwerte für unterschiedliche Triggertypen, um zu entscheiden, wann Instanzen hinzugefügt werden sollen, z. B. das Alter von Nachrichten und die Warteschlangengröße für QueueTrigger. Diese Schwellenwerte können nicht vom Benutzer konfiguriert werden. Weitere Informationen finden Sie unter Event-driven scaling in Azure Functions.
Verbessern der Durchsatzleistung
Die Standardkonfigurationen eignen sich für die meisten Azure Functions Anwendungen. Sie können jedoch die Leistung des Durchsatzes Ihrer Anwendungen verbessern, indem Sie Konfigurationen basierend auf Ihrem Workloadprofil verwenden. Daher besteht der erste Schritt darin, zu verstehen, welcher Typ von Workload ausgeführt wird.
| Workload-Typ | Eigenschaften der Funktions-App | Beispiele |
|---|---|---|
| E/A-gebunden | • Die App muss viele gleichzeitige Aufrufe verarbeiten. • Die App verarbeitet eine große Anzahl von E/A-Ereignissen, z. B. Netzwerkaufrufe und Lese-/Schreibzugriff auf Datenträger. |
• Web-APIs |
| CPU-gebunden | • Die App führt langandauernde Berechnungen aus, wie z. B. Bildgrößenänderungen. • Die App führt die Datentransformation durch. |
• Datenverarbeitung • Rückschluss auf maschinelles Lernen |
Da Arbeitslasten in der realen Welt normalerweise eine Mischung aus I/O-gebundenen und CPU-gebundenen Prozessen sind, sollten Sie die App unter realistischen Produktionslasten profilieren.
Leistungsspezifische Konfigurationen
Nachdem Sie das Workloadprofil Ihrer Funktions-App verstanden haben, sind die folgenden Konfigurationen aufgeführt, die Sie verwenden können, um die Durchsatzleistung Ihrer Funktionen zu verbessern.
- Async
- Arbeitskraft mit mehreren Sprachen
- Maximale Arbeiteranzahl innerhalb eines Sprach-Worker-Prozesses
- Ereignisschleife
- Vertikale Skalierung
Async
Da Python eine Single-Threaded-Runtime ist, kann eine Hostinstanz für Python standardmäßig nur einen Funktionsaufruf gleichzeitig verarbeiten. Bei Anwendungen, die eine große Anzahl von E/A-Ereignissen verarbeiten und/oder E/A gebunden sind, können Sie die Leistung erheblich verbessern, indem Sie Funktionen asynchron ausführen.
Um eine Funktion asynchron auszuführen, verwenden Sie die async def Anweisung, die die Funktion mit asyncio direkt ausführt:
async def main():
await some_nonblocking_socket_io_op()
Hier ist ein Beispiel für eine Funktion mit HTTP-Trigger, die aiohttp http-Client verwendet:
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)
Eine Funktion ohne das async Schlüsselwort wird automatisch in einem ThreadPoolExecutor-Threadpool ausgeführt:
# 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()
Um den vollen Vorteil der asynchronen Ausführung von Funktionen zu erzielen, muss auch die in Ihrem Code verwendete E/A-Operation/Bibliothek asynchron implementiert sein. Die Verwendung synchroner E/A-Vorgänge in Funktionen, die als asynchron definiert sind, kann die Gesamtleistung beeinträchtigen . Wenn die von Ihnen verwendeten Bibliotheken keine asynchrone Version implementiert haben, können Sie von der asynchronen Ausführung des Codes profitieren, indem Sie die Ereignisschleife in Ihrer App verwalten.
Hier sind einige Beispiele für Clientbibliotheken, die asynchrone Muster implementiert haben:
- aiohttp – Http-Client/Server für asyncio
- Streams-API – Hochstufige, für Async/Await vorbereitete Primitiven zur Arbeit mit Netzwerkverbindungen
- Janus Queue – Threadsichere asyncio-fähige Warteschlange für Python
- pyzmq – Python Bindungen für ZeroMQ
Grundlegendes zu asynchronem Programmieren in Python-Worker
Wenn Sie async vor einer Funktionssignatur definieren, markiert Python die Funktion als Coroutine. Wenn Sie die Coroutine aufrufen, kann sie beim Aufruf als Task in einer Ereignisschleife geplant werden. Wenn Sie eine asynchrone Funktion aufrufen await , registriert sie eine Fortsetzung in der Ereignisschleife, wodurch die Ereignisschleife die nächste Aufgabe während der Wartezeit verarbeiten kann.
In unserem Python Worker teilt der Worker die Ereignisschleife mit der async-Funktion des Kunden und kann mehrere Anforderungen gleichzeitig verarbeiten. Wir ermutigen unsere Kunden nachdrücklich, asyncio kompatible Bibliotheken wie aiohttp und pyzmq zu nutzen. Wenn Sie diese Empfehlungen befolgen, erhöht sich der Durchsatz Ihrer Funktion im Vergleich zu diesen Bibliotheken, wenn sie synchron implementiert werden.
Hinweis
Wenn Ihre Funktion innerhalb der Implementierung als async ohne await deklariert wird, wird die Leistung Ihrer Funktion stark beeinträchtigt, da die Ereignisschleife blockiert wird, wodurch verhindert wird, dass der Python Worker gleichzeitige Anforderungen verarbeitet.
Verwenden von Arbeitsprozessen in mehreren Sprachen
Standardmäßig verfügt jede Funktionen-Hostinstanz über einen einzelnen Sprach-Arbeitsprozess. Sie können die Anzahl der Arbeitsprozesse pro Host (bis zu 10) mithilfe der FUNCTIONS_WORKER_PROCESS_COUNT Anwendungseinstellung erhöhen. Azure Functions versucht dann, gleichzeitige Funktionsaufrufe über diese Mitarbeiter hinweg gleichmäßig zu verteilen.
Für CPU-gebundene Apps sollten Sie festlegen, dass die Anzahl der Sprachmitarbeiter mit oder höher als die Anzahl der pro Funktions-App verfügbaren Kerne übereinstimmt. Weitere Informationen finden Sie unter "Verfügbare Instanz-SKUs".
I/O-gebundene Apps können auch davon profitieren, die Anzahl der Arbeitsprozesse zu erhöhen, die über die Anzahl der verfügbaren Kerne hinausgehen. Denken Sie daran, dass eine zu hohe Anzahl von Workern die Gesamtleistung aufgrund der höheren Anzahl der erforderlichen Kontextwechsel beeinträchtigen kann.
Die FUNCTIONS_WORKER_PROCESS_COUNT gilt für jeden Host, den Azure Functions beim Skalieren Ihrer Anwendung erstellt, um die Anforderungen zu erfüllen.
Festlegen der maximalen Workeranzahl in einem Sprachworkerprozess
Wie im asynchronen section erwähnt, behandelt der Python Sprachmitarbeiter Funktionen und coroutines unterschiedlich. Eine Coroutine wird in derselben Ereignisschleife ausgeführt, in der auch der Sprachworker ausgeführt wird. Andererseits wird ein Funktionsaufruf in einem ThreadPoolExecutor ausgeführt, der vom Spracharbeiter als Thread verwaltet wird.
Sie können den Wert der maximal zulässigen Mitarbeiter für die Ausführung von Synchronisierungsfunktionen mithilfe der PYTHON_THREADPOOL_THREAD_COUNT Anwendungseinstellung festlegen. Dieser Wert legt das Argument max_worker des ThreadPoolExecutor -Objekts fest, wodurch Python einen Pool mit höchstens max_worker Threads zum asynchronen Ausführen von Aufrufen verwenden kann. Das PYTHON_THREADPOOL_THREAD_COUNT gilt für jeden Worker, den der Funktionen-Host erstellt, und Python entscheidet, wann ein neuer Thread erstellt oder der vorhandene Leerlauf-Thread wiederverwendet werden soll. Bei älteren Python Versionen (d. h. 3.8, 3.7 und 3.6) wird max_worker Wert auf 1 festgelegt. Für Python Version 3.9 ist max_worker auf None festgelegt.
Bei CPU-gebundenen Apps sollten Sie die Einstellung auf eine niedrige Zahl halten, beginnend bei 1 und erhöhen, während Sie mit Ihrer Workload experimentieren. Dieser Vorschlag besteht darin, die für Kontextwechsel aufgewendete Zeit zu reduzieren und CPU-gebundene Aufgaben abschließen zu lassen.
Für I/O-gebundene Apps sollten Sie erhebliche Verbesserungen erzielen, indem Sie die Anzahl der Threads erhöhen, die bei jedem Aufruf arbeiten. Die Empfehlung besteht darin, mit der Python Standardeinstellung (die Anzahl der Kerne) + 4 zu beginnen und dann basierend auf den Durchsatzwerten zu optimieren, die Sie sehen.
Bei Anwendungen mit gemischten Workloads sollten Sie sowohl FUNCTIONS_WORKER_PROCESS_COUNT- als auch PYTHON_THREADPOOL_THREAD_COUNT-Konfigurationen ausgleichen, um den Durchsatz zu maximieren. Um zu verstehen, wofür Ihre Funktions-Apps die meiste Zeit verbringen, empfehlen wir, sie zu profilieren und die Werte entsprechend ihren Verhaltensweisen festzulegen. Weitere Informationen zu diesen Anwendungseinstellungen finden Sie unter Verwenden mehrerer Arbeitsprozesse.
Hinweis
Obwohl diese Empfehlungen sowohl für HTTP- als auch für nicht-HTTP ausgelöste Funktionen gelten, müssen Sie möglicherweise andere Triggerkonfigurationen für nicht-HTTP ausgelöste Funktionen anpassen, um die erwartete Leistung von Ihren Funktions-Apps zu erhalten. Weitere Informationen hierzu finden Sie in diesen Best Practices für zuverlässige Azure Functions.
Verwalten der Ereignisschleife
Sie sollten asynciokompatible Drittanbieterbibliotheken verwenden. Wenn keine der Drittanbieterbibliotheken Ihren Anforderungen entspricht, können Sie die Ereignisschleifen auch in Azure Functions verwalten. Die Verwaltung von Ereignisschleifen bietet Ihnen mehr Flexibilität bei der Rechenressourcenverwaltung und ermöglicht es auch, synchrone Eingabe-/Ausgabebibliotheken in Coroutinen einzubetten.
Es gibt viele nützliche Python offizielle Dokumente, die die Coroutines and Tasks und Event Loop mithilfe der integrierten asyncioBibliothek besprechen.
Nehmen Sie die folgende Bibliothek 'requests' als Beispiel. In diesem Codeausschnitt wird die 'asyncio' Bibliothek verwendet, um die requests.get() Methode in eine Coroutine zu umwickeln, sodass mehrere Webanforderungen gleichzeitig ausgeführt werden, um SAMPLE_URL durchzuführen.
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')
Vertikale Skalierung
Möglicherweise können Sie mehr Verarbeitungseinheiten, insbesondere im CPU-gebundenen Betrieb, erhalten, indem Sie ein Upgrade auf Premium-Plan mit höheren Spezifikationen durchführen. Mit höheren Verarbeitungseinheiten können Sie die Anzahl der Arbeitsprozesse entsprechend der Anzahl der verfügbaren Kerne anpassen und einen höheren Grad an Parallelität erzielen.
Nächste Schritte
Weitere Informationen zur Azure Functions Python Entwicklung finden Sie in den folgenden Ressourcen: