Przewodnik dla deweloperów usługi Azure Functions pracujących w języku Python

Ten przewodnik stanowi wprowadzenie do tworzenia usługi Azure Functions przy użyciu języka Python. W tym artykule założono, że znasz już przewodnik deweloperów usługi Azure Functions.

Ważne

Ten artykuł obsługuje zarówno model programowania w wersji 1, jak i w wersji 2 dla języka Python w usłudze Azure Functions. Model języka Python w wersji 1 używa pliku functions.json do definiowania funkcji, a nowy model w wersji 2 umożliwia zamiast tego użycie podejścia opartego na dekoratorze. To nowe podejście powoduje prostszą strukturę plików i jest bardziej skoncentrowane na kodzie. Wybierz selektor w wersji 2 w górnej części artykułu, aby dowiedzieć się więcej o tym nowym modelu programowania.

Jako deweloper języka Python możesz również zainteresować się jednym z następujących artykułów:

Opcje programowania

Oba modele programowania usługi Python Functions obsługują programowanie lokalne w jednym z następujących środowisk:

Model programowania w języku Python w wersji 2:

Należy pamiętać, że model programowania języka Python w wersji 2 jest obsługiwany tylko w środowisku uruchomieniowym funkcji 4.x. Aby uzyskać więcej informacji, zobacz Omówienie wersji środowiska uruchomieniowego usługi Azure Functions.

Model programowania w języku Python w wersji 1:

Możesz również utworzyć funkcje języka Python w wersji 1 w witrynie Azure Portal.

Napiwek

Mimo że można opracowywać funkcje platformy Azure oparte na języku Python lokalnie w systemie Windows, język Python jest obsługiwany tylko w planie hostingu opartym na systemie Linux, gdy jest uruchomiony na platformie Azure. Aby uzyskać więcej informacji, zobacz listę obsługiwanych kombinacji systemu operacyjnego/środowiska uruchomieniowego.

Model programowania

Usługa Azure Functions oczekuje, że funkcja będzie metodą bezstanową w skrycie języka Python, która przetwarza dane wejściowe i generuje dane wyjściowe. Domyślnie środowisko uruchomieniowe oczekuje, że metoda zostanie zaimplementowana jako metoda globalna wywoływana main() w pliku __init__.py . Można również określić alternatywny punkt wejścia.

Dane są powiązane z funkcją z wyzwalaczy i powiązań za pomocą atrybutów metody, które używają name właściwości zdefiniowanej w pliku function.json . Na przykład poniższy plik function.json opisuje prostą funkcję wyzwalaną przez żądanie HTTP o nazwie req:

{
    "scriptFile": "__init__.py",
    "bindings": [
        {
            "authLevel": "function",
            "type": "httpTrigger",
            "direction": "in",
            "name": "req",
            "methods": [
                "get",
                "post"
            ]
        },
        {
            "type": "http",
            "direction": "out",
            "name": "$return"
        }
    ]
}

Na podstawie tej definicji plik __init__.py zawierający kod funkcji może wyglądać podobnie do następującego przykładu:

def main(req):
    user = req.params.get('user')
    return f'Hello, {user}!'

Można również jawnie zadeklarować typy atrybutów i zwracać typ w funkcji przy użyciu adnotacji typu języka Python. Dzięki temu można korzystać z funkcji IntelliSense i funkcji autouzupełniania, które są udostępniane przez wiele edytorów kodu języka Python.

import azure.functions

def main(req: azure.functions.HttpRequest) -> str:
    user = req.params.get('user')
    return f'Hello, {user}!'

Użyj adnotacji języka Python zawartych w pakiecie azure.functions.* w celu powiązania danych wejściowych i wyjściowych z metodami.

Usługa Azure Functions oczekuje, że funkcja będzie metodą bezstanową w skrycie języka Python, która przetwarza dane wejściowe i generuje dane wyjściowe. Domyślnie środowisko uruchomieniowe oczekuje zaimplementowania metody jako metody globalnej w pliku function_app.py .

Wyzwalacze i powiązania można zadeklarować i używać w funkcji w podejściu opartym na dekoratorze. Są one definiowane w tym samym pliku, function_app.py, co funkcje. Na przykład poniższy plik function_app.py reprezentuje wyzwalacz funkcji przez żądanie HTTP.

@app.function_name(name="HttpTrigger1")
@app.route(route="req")
def main(req):
    user = req.params.get("user")
    return f"Hello, {user}!"

Można również jawnie zadeklarować typy atrybutów i zwracać typ w funkcji przy użyciu adnotacji typu języka Python. Ułatwia to korzystanie z funkcji IntelliSense i funkcji autouzupełniania, które są udostępniane przez wiele edytorów kodu języka Python.

import azure.functions as func

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="req")
def main(req: func.HttpRequest) -> str:
    user = req.params.get("user")
    return f"Hello, {user}!"

Aby dowiedzieć się więcej o znanych ograniczeniach dotyczących modelu w wersji 2 i ich obejściach, zobacz Rozwiązywanie problemów z błędami języka Python w usłudze Azure Functions.

Alternatywny punkt wejścia

Domyślne zachowanie funkcji można zmienić, określając scriptFile opcjonalnie właściwości i entryPoint w pliku function.json . Na przykład poniższy function.json informuje środowisko uruchomieniowe o użyciu customentry() metody w pliku main.py jako punktu wejścia dla funkcji platformy Azure.

{
  "scriptFile": "main.py",
  "entryPoint": "customentry",
  "bindings": [
      ...
  ]
}

Punkt wejścia znajduje się tylko w pliku function_app.py . Można jednak odwoływać się do funkcji w projekcie w function_app.py przy użyciu strategii lub przez zaimportowanie.

Struktura folderów

Zalecana struktura folderów dla projektu funkcji języka Python wygląda następująco:

 <project_root>/
 | - .venv/
 | - .vscode/
 | - my_first_function/
 | | - __init__.py
 | | - function.json
 | | - example.py
 | - my_second_function/
 | | - __init__.py
 | | - function.json
 | - shared_code/
 | | - __init__.py
 | | - my_first_helper_function.py
 | | - my_second_helper_function.py
 | - tests/
 | | - test_my_second_function.py
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - requirements.txt
 | - Dockerfile

Główny folder projektu, <project_root>, może zawierać następujące pliki:

  • local.settings.json: służy do przechowywania ustawień aplikacji i parametry połączenia podczas uruchamiania lokalnego. Ten plik nie jest publikowany na platformie Azure. Aby dowiedzieć się więcej, zobacz local.settings.file.
  • requirements.txt: zawiera listę pakietów języka Python instalowanych przez system podczas publikowania na platformie Azure.
  • host.json: zawiera opcje konfiguracji, które mają wpływ na wszystkie funkcje w wystąpieniu aplikacji funkcji. Ten plik jest publikowany na platformie Azure. Nie wszystkie opcje są obsługiwane w przypadku uruchamiania lokalnego. Aby dowiedzieć się więcej, zobacz host.json.
  • .vscode/: (Opcjonalnie) Zawiera przechowywaną konfigurację programu Visual Studio Code. Aby dowiedzieć się więcej, zobacz Ustawienia programu Visual Studio Code.
  • .venv/: (Opcjonalnie) Zawiera środowisko wirtualne języka Python używane przez programowanie lokalne.
  • Plik Dockerfile: (opcjonalnie) Używany podczas publikowania projektu w kontenerze niestandardowym.
  • tests/: (Opcjonalnie) Zawiera przypadki testowe aplikacji funkcji.
  • .funcignore: (Opcjonalnie) Deklaruje pliki, które nie powinny być publikowane na platformie Azure. Zazwyczaj ten plik zawiera plik vscode/ , aby zignorować ustawienie edytora, plik venv/ w celu zignorowania lokalnego środowiska wirtualnego języka Python, testów/ ignorowania przypadków testowych i local.settings.json , aby zapobiec publikowaniu ustawień aplikacji lokalnych.

Każda funkcja ma własny plik kodu i plik konfiguracji powiązania, function.json.

Zalecana struktura folderów dla projektu funkcji języka Python wygląda następująco:

 <project_root>/
 | - .venv/
 | - .vscode/
 | - function_app.py
 | - additional_functions.py
 | - tests/
 | | - test_my_function.py
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - requirements.txt
 | - Dockerfile

Główny folder projektu, <project_root>, może zawierać następujące pliki:

  • .venv/: (Opcjonalnie) Zawiera środowisko wirtualne języka Python, które jest używane przez programowanie lokalne.
  • .vscode/: (Opcjonalnie) Zawiera przechowywaną konfigurację programu Visual Studio Code. Aby dowiedzieć się więcej, zobacz Ustawienia programu Visual Studio Code.
  • function_app.py: domyślna lokalizacja wszystkich funkcji i powiązanych wyzwalaczy i powiązań.
  • additional_functions.py: (opcjonalnie) Wszystkie inne pliki języka Python zawierające funkcje (zwykle w przypadku grupowania logicznego), do których odwołuje się function_app.py za pośrednictwem strategii.
  • tests/: (Opcjonalnie) Zawiera przypadki testowe aplikacji funkcji.
  • .funcignore: (Opcjonalnie) Deklaruje pliki, które nie powinny być publikowane na platformie Azure. Zazwyczaj ten plik zawiera plik vscode/ do ignorowania ustawienia edytora, .venv/ do ignorowania lokalnego środowiska wirtualnego języka Python, testów/ ignorowania przypadków testowych i local.settings.json , aby zapobiec publikowaniu ustawień aplikacji lokalnych.
  • host.json: zawiera opcje konfiguracji, które mają wpływ na wszystkie funkcje w wystąpieniu aplikacji funkcji. Ten plik jest publikowany na platformie Azure. Nie wszystkie opcje są obsługiwane w przypadku uruchamiania lokalnego. Aby dowiedzieć się więcej, zobacz host.json.
  • local.settings.json: służy do przechowywania ustawień aplikacji i parametry połączenia, gdy są uruchomione lokalnie. Ten plik nie jest publikowany na platformie Azure. Aby dowiedzieć się więcej, zobacz local.settings.file.
  • requirements.txt: zawiera listę pakietów języka Python instalowanych przez system podczas publikowania na platformie Azure.
  • Plik Dockerfile: (opcjonalnie) Używany podczas publikowania projektu w kontenerze niestandardowym.

Podczas wdrażania projektu w aplikacji funkcji na platformie Azure cała zawartość głównego folderu projektu, <project_root>, powinna być uwzględniona w pakiecie, ale nie w samym folderze, co oznacza, że host.json powinien znajdować się w katalogu głównym pakietu. Zalecamy konserwację testów w folderze wraz z innymi funkcjami (w tym przykładzie testy/). Aby uzyskać więcej informacji, zobacz Testowanie jednostkowe.

Połącz z bazą danych

Usługa Azure Functions dobrze integruje się z usługą Azure Cosmos DB dla wielu przypadków użycia, w tym IoT, handlu elektronicznego, gier itp.

Na przykład w przypadku określania źródła zdarzeń dwie usługi są zintegrowane z architekturami opartymi na zdarzeniach zasilania przy użyciu funkcji zestawienia zmian usługi Azure Cosmos DB. Źródło zmian zapewnia mikrousługi podrzędne możliwość niezawodnego i przyrostowego odczytywania wstawień i aktualizacji (na przykład zdarzeń kolejności). Ta funkcja może być służyć do zapewnienia trwałego magazynu zdarzeń jako brokera komunikatów dla zdarzeń zmieniających stan i napędzania przepływu pracy przetwarzania zamówień między wieloma mikrousługami (które można zaimplementować jako bezserwerowe usługi Azure Functions).

Architektura referencyjna potoku porządkowania usługi Azure Cosmos DB

Aby nawiązać połączenie z usługą Cosmos DB, najpierw utwórz konto, bazę danych i kontener. Następnie możesz połączyć usługę Functions z usługą Cosmos DB przy użyciu wyzwalacza i powiązań, takich jak w tym przykładzie.

Aby zaimplementować bardziej złożoną logikę aplikacji, możesz również użyć biblioteki języka Python dla usługi Cosmos DB. Implementacja asynchroniczna we/wy wygląda następująco:

pip install azure-cosmos
pip install aiohttp

from azure.cosmos.aio import CosmosClient
from azure.cosmos import exceptions
from azure.cosmos.partition_key import PartitionKey
import asyncio

# Replace these values with your Cosmos DB connection information
endpoint = "https://azure-cosmos-nosql.documents.azure.com:443/"
key = "master_key"
database_id = "cosmicwerx"
container_id = "cosmicontainer"
partition_key = "/partition_key"

# Set the total throughput (RU/s) for the database and container
database_throughput = 1000

# Singleton CosmosClient instance
client = CosmosClient(endpoint, credential=key)

# Helper function to get or create database and container
async def get_or_create_container(client, database_id, container_id, partition_key):
    database = await client.create_database_if_not_exists(id=database_id)
    print(f'Database "{database_id}" created or retrieved successfully.')

    container = await database.create_container_if_not_exists(id=container_id, partition_key=PartitionKey(path=partition_key))
    print(f'Container with id "{container_id}" created')
 
    return container
 
async def create_products():
    container = await get_or_create_container(client, database_id, container_id, partition_key)
    for i in range(10):
        await container.upsert_item({
            'id': f'item{i}',
            'productName': 'Widget',
            'productModel': f'Model {i}'
        })
 
async def get_products():
    items = []
    container = await get_or_create_container(client, database_id, container_id, partition_key)
    async for item in container.read_all_items():
        items.append(item)
    return items

async def query_products(product_name):
    container = await get_or_create_container(client, database_id, container_id, partition_key)
    query = f"SELECT * FROM c WHERE c.productName = '{product_name}'"
    items = []
    async for item in container.query_items(query=query, enable_cross_partition_query=True):
        items.append(item)
    return items

async def main():
    await create_products()
    all_products = await get_products()
    print('All Products:', all_products)

    queried_products = await query_products('Widget')
    print('Queried Products:', queried_products)

if __name__ == "__main__":
    asyncio.run(main())

Blueprints

Model programowania w języku Python w wersji 2 wprowadza koncepcję strategii. Strategia to nowa klasa, która tworzy wystąpienie funkcji poza podstawową aplikacją funkcji. Funkcje zarejestrowane w wystąpieniach strategii nie są indeksowane bezpośrednio przez środowisko uruchomieniowe funkcji. Aby te funkcje strategii zostały zaindeksowane, aplikacja funkcji musi zarejestrować funkcje z wystąpień strategii.

Korzystanie z strategii zapewnia następujące korzyści:

  • Umożliwia podzielenie aplikacji funkcji na składniki modułowe, co umożliwia definiowanie funkcji w wielu plikach języka Python i dzielenie ich na różne składniki na plik.
  • Udostępnia rozszerzalne interfejsy aplikacji funkcji publicznej do kompilowania i ponownego używania własnych interfejsów API.

W poniższym przykładzie pokazano, jak używać strategii:

Najpierw w pliku http_blueprint.py funkcja wyzwalana przez protokół HTTP jest najpierw definiowana i dodawana do obiektu strategii.

import logging 

import azure.functions as func 

bp = func.Blueprint() 

@bp.route(route="default_template") 
def default_template(req: func.HttpRequest) -> func.HttpResponse: 
    logging.info('Python HTTP trigger function processed a request.') 

    name = req.params.get('name') 
    if not name: 
        try: 
            req_body = req.get_json() 
        except ValueError: 
            pass 
        else: 
            name = req_body.get('name') 

    if name: 
        return func.HttpResponse( 
            f"Hello, {name}. This HTTP-triggered function " 
            f"executed successfully.") 
    else: 
        return func.HttpResponse( 
            "This HTTP-triggered function executed successfully. " 
            "Pass a name in the query string or in the request body for a" 
            " personalized response.", 
            status_code=200 
        ) 

Następnie w pliku function_app.py obiekt strategii jest importowany, a jego funkcje są rejestrowane w aplikacji funkcji.

import azure.functions as func 
from http_blueprint import bp

app = func.FunctionApp() 

app.register_functions(bp) 

Uwaga

Rozszerzenie Durable Functions obsługuje również strategie. Aby utworzyć strategie dla aplikacji durable functions, zarejestruj orkiestrację, działanie i wyzwalacze jednostek oraz powiązania klienta przy użyciu azure-functions-durableBlueprint klasy, jak pokazano tutaj. Wynikowa strategia może być następnie zarejestrowana normalnie. Zobacz nasz przykład .

Zachowanie importowania

Moduły w kodzie funkcji można importować przy użyciu odwołań bezwzględnych i względnych. Na podstawie wcześniej opisanej struktury folderów następujące operacje importowania działają z pliku <funkcji project_root>\my_first_function\__init__.py:

from shared_code import my_first_helper_function #(absolute)
import shared_code.my_second_helper_function #(absolute)
from . import example #(relative)

Uwaga

Jeśli używasz składni importu bezwzględnego, folder shared_code/ musi zawierać plik __init__.py , aby oznaczyć go jako pakiet języka Python.

Następujące __app__ importowania i poza importem względnym najwyższego poziomu są przestarzałe, ponieważ nie są obsługiwane przez moduł sprawdzania typów statycznych i nie są obsługiwane przez struktury testowe języka Python:

from __app__.shared_code import my_first_helper_function #(deprecated __app__ import)
from ..shared_code import my_first_helper_function #(deprecated beyond top-level relative import)

Wyzwalacze i dane wejściowe

Dane wejściowe są podzielone na dwie kategorie w usłudze Azure Functions: wyzwalanie danych wejściowych i innych danych wejściowych. Chociaż różnią się one w pliku function.json , ich użycie jest identyczne w kodzie języka Python. ciągi Połączenie ion lub wpisy tajne dla źródeł wyzwalaczy i wejściowych są mapować na wartości w pliku local.settings.json, gdy są uruchomione lokalnie, i mapują je na ustawienia aplikacji, gdy są uruchomione na platformie Azure.

Na przykład poniższy kod demonstruje różnicę między dwoma danymi wejściowymi:

// function.json
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "route": "items/{id}"
    },
    {
      "name": "obj",
      "direction": "in",
      "type": "blob",
      "path": "samples/{id}",
      "connection": "STORAGE_CONNECTION_STRING"
    }
  ]
}
// local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "STORAGE_CONNECTION_STRING": "<AZURE_STORAGE_CONNECTION_STRING>",
    "AzureWebJobsStorage": "<azure-storage-connection-string>"
  }
}
# __init__.py
import azure.functions as func
import logging

def main(req: func.HttpRequest, obj: func.InputStream):
    logging.info(f'Python HTTP-triggered function processed: {obj.read()}')

Po wywołaniu funkcji żądanie HTTP jest przekazywane do funkcji jako req. Wpis zostanie pobrany z konta usługi Azure Blob Storage na podstawie identyfikatora w adresie URL trasy i udostępniony jako obj w treści funkcji. W tym miejscu określone konto magazynu to parametry połączenia znalezione w ustawieniu CONNECTION_STRING aplikacji.

Dane wejściowe są podzielone na dwie kategorie w usłudze Azure Functions: wyzwalanie danych wejściowych i innych danych wejściowych. Chociaż są one definiowane przy użyciu różnych dekoratorów, ich użycie jest podobne w kodzie języka Python. ciągi Połączenie ion lub wpisy tajne dla źródeł wyzwalaczy i wejściowych są mapować na wartości w pliku local.settings.json, gdy są uruchomione lokalnie, i mapują je na ustawienia aplikacji, gdy są uruchomione na platformie Azure.

Na przykład poniższy kod pokazuje, jak zdefiniować powiązanie wejściowe usługi Blob Storage:

// local.settings.json
{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "python",
    "STORAGE_CONNECTION_STRING": "<AZURE_STORAGE_CONNECTION_STRING>",
    "AzureWebJobsStorage": "<azure-storage-connection-string>",
    "AzureWebJobsFeatureFlags": "EnableWorkerIndexing"
  }
}
# function_app.py
import azure.functions as func
import logging

app = func.FunctionApp()

@app.route(route="req")
@app.read_blob(arg_name="obj", path="samples/{id}", 
               connection="STORAGE_CONNECTION_STRING")
def main(req: func.HttpRequest, obj: func.InputStream):
    logging.info(f'Python HTTP-triggered function processed: {obj.read()}')

Po wywołaniu funkcji żądanie HTTP jest przekazywane do funkcji jako req. Wpis zostanie pobrany z konta usługi Azure Blob Storage na podstawie identyfikatora w adresie URL trasy i udostępniony jako obj w treści funkcji. W tym miejscu określone konto magazynu to parametry połączenia znalezione w ustawieniu STORAGE_CONNECTION_STRING aplikacji.

W przypadku operacji wiązania intensywnie korzystających z danych możesz użyć oddzielnego konta magazynu. Aby uzyskać więcej informacji, zobacz Wskazówki dotyczące konta magazynu.

Dane wyjściowe

Dane wyjściowe można wyrazić zarówno w parametrach zwracanych, jak i wyjściowych. Jeśli istnieje tylko jedno dane wyjściowe, zalecamy użycie wartości zwracanej. W przypadku wielu danych wyjściowych należy użyć parametrów wyjściowych.

Aby użyć wartości zwracanej funkcji jako wartości powiązania wyjściowego, name właściwość powiązania powinna być ustawiona na $return wartość w pliku function.json .

Aby utworzyć wiele danych wyjściowych, użyj set() metody udostępnionej azure.functions.Out przez interfejs, aby przypisać wartość do powiązania. Na przykład następująca funkcja może wypchnąć komunikat do kolejki, a także zwrócić odpowiedź HTTP.

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "req",
      "direction": "in",
      "type": "httpTrigger",
      "authLevel": "anonymous"
    },
    {
      "name": "msg",
      "direction": "out",
      "type": "queue",
      "queueName": "outqueue",
      "connection": "STORAGE_CONNECTION_STRING"
    },
    {
      "name": "$return",
      "direction": "out",
      "type": "http"
    }
  ]
}
import azure.functions as func

def main(req: func.HttpRequest,
         msg: func.Out[func.QueueMessage]) -> str:

    message = req.params.get('body')
    msg.set(message)
    return message

Dane wyjściowe można wyrazić zarówno w parametrach zwracanych, jak i wyjściowych. Jeśli istnieje tylko jedno dane wyjściowe, zalecamy użycie wartości zwracanej. W przypadku wielu danych wyjściowych należy użyć parametrów wyjściowych.

Aby utworzyć wiele danych wyjściowych, użyj set() metody udostępnionej azure.functions.Out przez interfejs, aby przypisać wartość do powiązania. Na przykład następująca funkcja może wypchnąć komunikat do kolejki, a także zwrócić odpowiedź HTTP.

# function_app.py
import azure.functions as func

app = func.FunctionApp()

@app.write_blob(arg_name="msg", path="output-container/{name}",
                connection="CONNECTION_STRING")
def test_function(req: func.HttpRequest,
                  msg: func.Out[str]) -> str:

    message = req.params.get('body')
    msg.set(message)
    return message

Rejestrowanie

Dostęp do rejestratora środowiska uruchomieniowego usługi Azure Functions jest dostępny za pośrednictwem programu obsługi głównej logging w aplikacji funkcji. Ten rejestrator jest powiązany z usługą Application Szczegółowe informacje i umożliwia flagę ostrzeżeń i błędów występujących podczas wykonywania funkcji.

Poniższy przykład rejestruje komunikat informacyjny, gdy funkcja jest wywoływana za pośrednictwem wyzwalacza HTTP.

import logging

def main(req):
    logging.info('Python HTTP trigger function processed a request.')

Dostępnych jest więcej metod rejestrowania, które umożliwiają zapisywanie w konsoli na różnych poziomach śledzenia:

Metoda opis
critical(_message_) Zapisuje komunikat o poziomie KRYTYCZNYm w głównym rejestratorze.
error(_message_) Zapisuje komunikat z poziomem ERROR w głównym rejestratorze.
warning(_message_) Zapisuje komunikat z poziomem OSTRZEŻENIE w głównym rejestratorze.
info(_message_) Zapisuje komunikat z poziomem INFO w głównym rejestratorze.
debug(_message_) Zapisuje komunikat z poziomem DEBUG w głównym rejestratorze.

Aby dowiedzieć się więcej na temat rejestrowania, zobacz Monitorowanie usługi Azure Functions.

Rejestrowanie na podstawie utworzonych wątków

Aby wyświetlić dzienniki pochodzące z utworzonych wątków, uwzględnij context argument w podpisie funkcji. Ten argument zawiera atrybut, który przechowuje lokalny invocation_idelement thread_local_storage . Można to ustawić na bieżącą invocation_id funkcję, aby upewnić się, że kontekst został zmieniony.

import azure.functions as func
import logging
import threading


def main(req, context):
    logging.info('Python HTTP trigger function processed a request.')
    t = threading.Thread(target=log_function, args=(context,))
    t.start()


def log_function(context):
    context.thread_local_storage.invocation_id = context.invocation_id
    logging.info('Logging from thread.')

Rejestrowanie telemetrii niestandardowej

Domyślnie środowisko uruchomieniowe usługi Functions zbiera dzienniki i inne dane telemetryczne generowane przez funkcje. Ta telemetria kończy się jako ślady w aplikacji Szczegółowe informacje. Dane telemetryczne żądań i zależności dla niektórych usług platformy Azure są również zbierane domyślnie przez wyzwalacze i powiązania.

Aby zbierać niestandardowe żądania i niestandardowe dane telemetryczne zależności poza powiązaniami, możesz użyć rozszerzeń języka Python OpenCensus. To rozszerzenie wysyła niestandardowe dane telemetryczne do wystąpienia aplikacji Szczegółowe informacje. Listę obsługiwanych rozszerzeń można znaleźć w repozytorium OpenCensus.

Uwaga

Aby użyć rozszerzeń openCensus języka Python, należy włączyć rozszerzenia procesu roboczego języka Python w aplikacji funkcji, ustawiając wartość PYTHON_ENABLE_WORKER_EXTENSIONS .1 Należy również przełączyć się na używanie Szczegółowe informacje parametry połączenia aplikacji, dodając APPLICATIONINSIGHTS_CONNECTION_STRING ustawienie do ustawień aplikacji, jeśli jeszcze nie istnieje.

// requirements.txt
...
opencensus-extension-azure-functions
opencensus-ext-requests
import json
import logging

import requests
from opencensus.extension.azure.functions import OpenCensusExtension
from opencensus.trace import config_integration

config_integration.trace_integrations(['requests'])

OpenCensusExtension.configure()

def main(req, context):
    logging.info('Executing HttpTrigger with OpenCensus extension')

    # You must use context.tracer to create spans
    with context.tracer.span("parent"):
        response = requests.get(url='http://example.com')

    return json.dumps({
        'method': req.method,
        'response': response.status_code,
        'ctx_func_name': context.function_name,
        'ctx_func_dir': context.function_directory,
        'ctx_invocation_id': context.invocation_id,
        'ctx_trace_context_Traceparent': context.trace_context.Traceparent,
        'ctx_trace_context_Tracestate': context.trace_context.Tracestate,
        'ctx_retry_context_RetryCount': context.retry_context.retry_count,
        'ctx_retry_context_MaxRetryCount': context.retry_context.max_retry_count,
    })

Wyzwalacz HTTP

Wyzwalacz HTTP jest zdefiniowany w pliku function.json . Powiązanie name musi być zgodne z nazwanym parametrem w funkcji. W poprzednich przykładach jest używana nazwa req powiązania. Ten parametr jest obiektem HttpRequest , a zwracany jest obiekt HttpResponse .

Z obiektu HttpRequest można uzyskać nagłówki żądań, parametry zapytania, parametry trasy i treść komunikatu.

Poniższy przykład pochodzi z szablonu wyzwalacza HTTP dla języka Python.

def main(req: func.HttpRequest) -> func.HttpResponse:
    headers = {"my-http-header": "some-value"}

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello {name}!", headers=headers)
    else:
        return func.HttpResponse(
             "Please pass a name on the query string or in the request body",
             headers=headers, status_code=400
        )

W tej funkcji uzyskasz wartość parametru name zapytania z params parametru obiektu HttpRequest . Treść komunikatu zakodowanego w formacie JSON jest odczytywana przy użyciu get_json metody .

Podobnie można ustawić status_code element i headers dla komunikatu odpowiedzi w zwróconym obiekcie HttpResponse .

Wyzwalacz HTTP jest definiowany jako metoda, która przyjmuje nazwany parametr powiązania, który jest obiektem HttpRequest i zwraca obiekt HttpResponse. Dekorator jest stosowany function_name do metody w celu zdefiniowania nazwy funkcji, podczas gdy punkt końcowy HTTP jest ustawiany przez zastosowanie dekoratora route .

Ten przykład pochodzi z szablonu wyzwalacza HTTP dla modelu programowania języka Python w wersji 2, gdzie nazwa parametru powiązania to req. Jest to przykładowy kod udostępniany podczas tworzenia funkcji przy użyciu narzędzi Azure Functions Core Tools lub Visual Studio Code.

@app.function_name(name="HttpTrigger1")
@app.route(route="hello")
def test_function(req: func.HttpRequest) -> func.HttpResponse:
     logging.info('Python HTTP trigger function processed a request.')

     name = req.params.get('name')
     if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

     if name:
        return func.HttpResponse(f"Hello, {name}. This HTTP-triggered function executed successfully.")
     else:
        return func.HttpResponse(
             "This HTTP-triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )

Z obiektu HttpRequest można uzyskać nagłówki żądań, parametry zapytania, parametry trasy i treść komunikatu. W tej funkcji uzyskasz wartość parametru name zapytania z params parametru obiektu HttpRequest . Treść komunikatu zakodowanego w formacie JSON jest odczytywana przy użyciu get_json metody .

Podobnie można ustawić status_code element i headers dla komunikatu odpowiedzi w zwróconym obiekcie HttpResponse .

Aby przekazać nazwę w tym przykładzie, wklej adres URL podany podczas uruchamiania funkcji, a następnie dołącz go za pomocą "?name={name}"polecenia .

Struktury sieci Web

Można użyć platform zgodnych z interfejsem bramy serwera sieci Web (WSGI) i asynchronicznych platform zgodnych z bramą serwera (ASGI), takich jak Flask i FastAPI, z funkcjami języka Python wyzwalanymi przez protokół HTTP. W tej sekcji pokazano, jak zmodyfikować funkcje tak, aby obsługiwały te struktury.

Najpierw należy zaktualizować plik function.json, aby uwzględnić element route w wyzwalaczu HTTP, jak pokazano w poniższym przykładzie:

{
  "scriptFile": "__init__.py",
  "bindings": [
    {
       "authLevel": "anonymous",
       "type": "httpTrigger",
       "direction": "in",
       "name": "req",
       "methods": [
           "get",
           "post"
       ],
       "route": "{*route}"
    },
    {
       "type": "http",
       "direction": "out",
       "name": "$return"
    }
  ]
}

Plik host.json musi również zostać zaktualizowany w celu uwzględnienia protokołu HTTP routePrefix, jak pokazano w poniższym przykładzie:

{
  "version": "2.0",
  "logging": 
  {
    "applicationInsights": 
    {
      "samplingSettings": 
      {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": 
  {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  },
  "extensions": 
  {
    "http": 
    {
        "routePrefix": ""
    }
  }
}

Zaktualizuj plik kodu języka Python init.py w zależności od interfejsu używanego przez platformę. W poniższym przykładzie przedstawiono podejście obsługi ASGI lub podejście otoki WSGI dla platformy Flask:

app = fastapi.FastAPI()

@app.get("hello/{name}")
async def get_name(name: str):
  return {"name": name}

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    return func.AsgiMiddleware(app).handle(req, context)

Można użyć asynchronicznych struktur zgodnych z interfejsem bramy serwera (ASGI) i interfejsu bramy serwera sieci Web (WSGI), takich jak Flask i FastAPI, z funkcjami języka Python wyzwalanymi przez protokół HTTP. Najpierw należy zaktualizować plik host.json w celu uwzględnienia protokołu HTTP routePrefix, jak pokazano w poniższym przykładzie:

{
  "version": "2.0",
  "logging": 
  {
    "applicationInsights": 
    {
      "samplingSettings": 
      {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": 
  {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[2.*, 3.0.0)"
  },
  "extensions": 
  {
    "http": 
    {
        "routePrefix": ""
    }
  }
}

Kod struktury wygląda podobnie do następującego przykładu:

AsgiFunctionApp to klasa aplikacji funkcji najwyższego poziomu do konstruowania funkcji HTTP ASGI.

# function_app.py

import azure.functions as func 
from fastapi import FastAPI, Request, Response 

fast_app = FastAPI() 

@fast_app.get("/return_http_no_body") 
async def return_http_no_body(): 
    return Response(content="", media_type="text/plain") 

app = func.AsgiFunctionApp(app=fast_app, 
                           http_auth_level=func.AuthLevel.ANONYMOUS) 

Skalowanie i wydajność

Aby uzyskać najlepsze rozwiązania dotyczące skalowania i wydajności aplikacji funkcji języka Python, zobacz artykuł Skalowanie i wydajność języka Python.

Kontekst

Aby uzyskać kontekst wywołania funkcji po jej uruchomieniu, dołącz context argument w podpisie.

Na przykład:

import azure.functions


def main(req: azure.functions.HttpRequest,
         context: azure.functions.Context) -> str:
    return f'{context.invocation_id}'

Klasa Context ma następujące atrybuty ciągu:

Atrybut opis
function_directory Katalog, w którym jest uruchomiona funkcja.
function_name Nazwa funkcji.
invocation_id Identyfikator wywołania bieżącej funkcji.
thread_local_storage Lokalny magazyn wątku funkcji. Zawiera lokalną invocation_id lokalizację rejestrowania z utworzonych wątków.
trace_context Kontekst śledzenia rozproszonego. Aby uzyskać więcej informacji, zobacz Trace Context.
retry_context Kontekst ponownych prób dla funkcji. Aby uzyskać więcej informacji, zobacz retry-policies.

Zmienne globalne

Nie ma gwarancji, że stan aplikacji zostanie zachowany na potrzeby przyszłych wykonań. Jednak środowisko uruchomieniowe usługi Azure Functions często używa tego samego procesu w przypadku wielu wykonań tej samej aplikacji. Aby buforować wyniki kosztownych obliczeń, zadeklaruj ją jako zmienną globalną.

CACHED_DATA = None


def main(req):
    global CACHED_DATA
    if CACHED_DATA is None:
        CACHED_DATA = load_json()

    # ... use CACHED_DATA in code

Zmienne środowiskowe

W usłudze Azure Functions ustawienia aplikacji, takie jak parametry połączenia usług, są widoczne jako zmienne środowiskowe podczas ich uruchamiania. Istnieją dwa główne sposoby uzyskiwania dostępu do tych ustawień w kodzie.

Metoda opis
os.environ["myAppSetting"] Próbuje pobrać ustawienie aplikacji według nazwy klucza i zgłasza błąd, gdy nie powiedzie się.
os.getenv("myAppSetting") Próbuje pobrać ustawienie aplikacji według nazwy klucza i zwraca null wartość, gdy nie powiedzie się.

Oba te sposoby wymagają zadeklarowania wartości import os.

W poniższym przykładzie użyto os.environ["myAppSetting"] polecenia w celu pobrania ustawienia aplikacji z kluczem o nazwie myAppSetting:

import logging
import os

import azure.functions as func

def main(req: func.HttpRequest) -> func.HttpResponse:
  # Get the setting named 'myAppSetting'
  my_app_setting_value = os.environ["myAppSetting"]
  logging.info(f'My app setting value:{my_app_setting_value}')

W przypadku programowania lokalnego ustawienia aplikacji są przechowywane w pliku local.settings.json.

W usłudze Azure Functions ustawienia aplikacji, takie jak parametry połączenia usług, są widoczne jako zmienne środowiskowe podczas ich uruchamiania. Istnieją dwa główne sposoby uzyskiwania dostępu do tych ustawień w kodzie.

Metoda opis
os.environ["myAppSetting"] Próbuje pobrać ustawienie aplikacji według nazwy klucza i zgłasza błąd, gdy nie powiedzie się.
os.getenv("myAppSetting") Próbuje pobrać ustawienie aplikacji według nazwy klucza i zwraca null wartość, gdy nie powiedzie się.

Oba te sposoby wymagają zadeklarowania wartości import os.

W poniższym przykładzie użyto os.environ["myAppSetting"] polecenia w celu pobrania ustawienia aplikacji z kluczem o nazwie myAppSetting:

import logging
import os

import azure.functions as func

app = func.FunctionApp()

@app.function_name(name="HttpTrigger1")
@app.route(route="req")
def main(req: func.HttpRequest) -> func.HttpResponse:
  # Get the setting named 'myAppSetting'
  my_app_setting_value = os.environ["myAppSetting"]
  logging.info(f'My app setting value:{my_app_setting_value}')

W przypadku programowania lokalnego ustawienia aplikacji są przechowywane w pliku local.settings.json.

W przypadku korzystania z nowego modelu programowania włącz następujące ustawienie aplikacji w pliku local.settings.json , jak pokazano poniżej:

"AzureWebJobsFeatureFlags": "EnableWorkerIndexing"

Podczas wdrażania funkcji to ustawienie nie jest tworzone automatycznie. To ustawienie należy jawnie utworzyć w aplikacji funkcji na platformie Azure, aby było uruchamiane przy użyciu modelu w wersji 2.

Wersja języka Python

Usługa Azure Functions obsługuje następujące wersje języka Python:

Wersja usługi Functions Wersje języka Python*
4.x 3.11
3,10
3.9
3,8
3.7
3.x 3.9
3,8
3.7

* Oficjalne dystrybucje języka Python

Aby zażądać określonej wersji języka Python podczas tworzenia aplikacji funkcji na platformie Azure, użyj --runtime-version opcji az functionapp create polecenia . Wersja środowiska uruchomieniowego usługi Functions jest ustawiana --functions-version przez opcję . Wersja języka Python jest ustawiana podczas tworzenia aplikacji funkcji i nie można jej zmienić dla aplikacji uruchomionych w planie Zużycie.

Środowisko uruchomieniowe używa dostępnej wersji języka Python podczas lokalnego uruchamiania.

Zmiana wersji języka Python

Aby ustawić aplikację funkcji języka Python na określoną wersję języka, należy określić język i wersję języka w LinuxFxVersion polu w konfiguracji lokacji. Aby na przykład zmienić aplikację języka Python na użycie języka Python w wersji 3.8, ustaw wartość linuxFxVersionpython|3.8.

Aby dowiedzieć się, jak wyświetlać i zmieniać linuxFxVersion ustawienie witryny, zobacz Jak kierować do wersji środowiska uruchomieniowego usługi Azure Functions.

Aby uzyskać więcej ogólnych informacji, zobacz zasady obsługi środowiska uruchomieniowego usługi Azure Functions i Obsługiwane języki w usłudze Azure Functions.

Zarządzanie pakietami

Podczas tworzenia aplikacji lokalnie przy użyciu narzędzi Core Tools lub Visual Studio Code dodaj nazwy i wersje wymaganych pakietów do pliku requirements.txt , a następnie zainstaluj je przy użyciu polecenia pip.

Na przykład możesz użyć następującego pliku requirements.txt i pip polecenia, aby zainstalować requests pakiet z PyPI.

requests==2.19.1
pip install -r requirements.txt

Podczas uruchamiania funkcji w planie usługi App Service zależności zdefiniowane w requirements.txt mają pierwszeństwo przed wbudowanymi modułami języka Python, takimi jak logging. Pierwszeństwo to może powodować konflikty, gdy wbudowane moduły mają takie same nazwy jak katalogi w kodzie. W przypadku uruchamiania w planie Zużycie lub w planie Elastic Premium konflikty są mniej prawdopodobne, ponieważ zależności nie są domyślnie priorytetowe.

Aby zapobiec problemom uruchomionym w planie usługi App Service, nie nazwij katalogów tak samo jak w przypadku modułów natywnych języka Python i nie dołączaj bibliotek natywnych języka Python do pliku requirements.txt projektu.

Publikowanie na platformie Azure

Gdy wszystko będzie gotowe do opublikowania, upewnij się, że wszystkie publicznie dostępne zależności są wymienione w pliku requirements.txt . Ten plik można zlokalizować w katalogu głównym katalogu projektu.

Pliki i foldery projektu, które są wykluczone z publikowania, w tym folder środowiska wirtualnego, można znaleźć w katalogu głównym projektu.

Istnieją trzy akcje kompilacji obsługiwane do publikowania projektu w języku Python na platformie Azure: kompilacja zdalna, kompilacja lokalna i kompilacje przy użyciu niestandardowych zależności.

Za pomocą usługi Azure Pipelines można również tworzyć zależności i publikować przy użyciu ciągłego dostarczania (CD). Aby dowiedzieć się więcej, zobacz Ciągłe dostarczanie za pomocą usługi Azure Pipelines.

Kompilacja zdalna

W przypadku korzystania z kompilacji zdalnej zależności przywracane na serwerze i zależności natywne są zgodne ze środowiskiem produkcyjnym. Spowoduje to utworzenie mniejszego pakietu wdrożeniowego do przekazania. Użyj kompilacji zdalnej podczas tworzenia aplikacji języka Python w systemie Windows. Jeśli projekt ma niestandardowe zależności, możesz użyć kompilacji zdalnej z dodatkowym adresem URL indeksu.

Zależności są uzyskiwane zdalnie na podstawie zawartości pliku requirements.txt . Kompilacja zdalna to zalecana metoda kompilacji. Domyślnie narzędzia Core Tools żądają kompilacji zdalnej, gdy użyjesz następującego func azure functionapp publish polecenia, aby opublikować projekt w języku Python na platformie Azure.

func azure functionapp publish <APP_NAME>

Pamiętaj, aby zastąpić <APP_NAME> ciąg nazwą aplikacji funkcji na platformie Azure.

Rozszerzenie usługi Azure Functions dla programu Visual Studio Code domyślnie żąda również kompilacji zdalnej.

Kompilacja lokalna

Zależności są uzyskiwane lokalnie na podstawie zawartości pliku requirements.txt . Możesz uniemożliwić wykonywanie kompilacji zdalnej przy użyciu następującego func azure functionapp publish polecenia w celu opublikowania przy użyciu kompilacji lokalnej:

func azure functionapp publish <APP_NAME> --build local

Pamiętaj, aby zastąpić <APP_NAME> ciąg nazwą aplikacji funkcji na platformie Azure.

W przypadku korzystania z --build local tej opcji zależności projektu są odczytywane z pliku requirements.txt , a te pakiety zależne są pobierane i instalowane lokalnie. Pliki i zależności projektu są wdrażane z komputera lokalnego na platformie Azure. Spowoduje to przekazanie większego pakietu wdrożeniowego na platformę Azure. Jeśli z jakiegoś powodu nie można pobrać pliku requirements.txt przy użyciu narzędzi Core Tools, musisz użyć opcji zależności niestandardowych do publikowania.

Nie zalecamy używania kompilacji lokalnych podczas tworzenia lokalnie w systemie Windows.

Zależności niestandardowe

Jeśli projekt ma zależności, które nie znajdują się w indeksie pakietów języka Python, istnieją dwa sposoby kompilowania projektu. Pierwszy sposób, metoda kompilacji , zależy od sposobu kompilowania projektu.

Zdalna kompilacja z dodatkowym adresem URL indeksu

Jeśli pakiety są dostępne z dostępnego niestandardowego indeksu pakietów, użyj kompilacji zdalnej. Przed opublikowaniem pamiętaj o utworzeniu ustawienia aplikacji o nazwie PIP_EXTRA_INDEX_URL. Wartość tego ustawienia to adres URL niestandardowego indeksu pakietu. Użycie tego ustawienia powoduje, że kompilacja zdalna zostanie uruchomiona pip install--extra-index-url przy użyciu opcji . Aby dowiedzieć się więcej, zobacz dokumentację języka Pythonpip install.

Możesz również użyć podstawowych poświadczeń uwierzytelniania z dodatkowymi adresami URL indeksu pakietu. Aby dowiedzieć się więcej, zobacz Podstawowe poświadczenia uwierzytelniania w dokumentacji języka Python.

Instalowanie pakietów lokalnych

Jeśli projekt używa pakietów, które nie są publicznie dostępne dla naszych narzędzi, możesz udostępnić je aplikacji, umieszczając je w katalogu __app__/.python_packages . Przed opublikowaniem uruchom następujące polecenie, aby zainstalować zależności lokalnie:

pip install  --target="<PROJECT_DIR>/.python_packages/lib/site-packages"  -r requirements.txt

W przypadku używania zależności niestandardowych należy użyć --no-build opcji publikowania, ponieważ zależności zostały już zainstalowane w folderze projektu.

func azure functionapp publish <APP_NAME> --no-build

Pamiętaj, aby zastąpić <APP_NAME> ciąg nazwą aplikacji funkcji na platformie Azure.

Testowanie jednostek

Funkcje napisane w języku Python mogą być testowane jak inny kod języka Python przy użyciu standardowych struktur testowania. W przypadku większości powiązań można utworzyć pozorny obiekt wejściowy, tworząc wystąpienie odpowiedniej klasy z azure.functions pakietu. azure.functions Ponieważ pakiet nie jest natychmiast dostępny, pamiętaj, aby zainstalować go za pośrednictwem pliku requirements.txt zgodnie z opisem w powyższej sekcji zarządzania pakietami.

W przypadku my_second_function na przykład poniżej przedstawiono pozorny test funkcji wyzwalanej przez protokół HTTP:

Najpierw utwórz <plik project_root>/my_second_function/function.json , a następnie zdefiniuj tę funkcję jako wyzwalacz HTTP.

{
  "scriptFile": "__init__.py",
  "entryPoint": "main",
  "bindings": [
    {
      "authLevel": "function",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
  ]
}

Następnie możesz zaimplementować elementy my_second_function i shared_code.my_second_helper_function.

# <project_root>/my_second_function/__init__.py
import azure.functions as func
import logging

# Use absolute import to resolve shared_code modules
from shared_code import my_second_helper_function

# Define an HTTP trigger that accepts the ?value=<int> query parameter
# Double the value and return the result in HttpResponse
def main(req: func.HttpRequest) -> func.HttpResponse:
  logging.info('Executing my_second_function.')

  initial_value: int = int(req.params.get('value'))
  doubled_value: int = my_second_helper_function.double(initial_value)

  return func.HttpResponse(
    body=f"{initial_value} * 2 = {doubled_value}",
    status_code=200
    )
# <project_root>/shared_code/__init__.py
# Empty __init__.py file marks shared_code folder as a Python package
# <project_root>/shared_code/my_second_helper_function.py

def double(value: int) -> int:
  return value * 2

Możesz rozpocząć pisanie przypadków testowych dla wyzwalacza HTTP.

# <project_root>/tests/test_my_second_function.py
import unittest

import azure.functions as func
from my_second_function import main

class TestFunction(unittest.TestCase):
  def test_my_second_function(self):
    # Construct a mock HTTP request.
    req = func.HttpRequest(method='GET',
                           body=None,
                           url='/api/my_second_function',
                           params={'value': '21'})
    # Call the function.
    resp = main(req)

    # Check the output.
    self.assertEqual(resp.get_body(), b'21 * 2 = 42',)

W folderze środowiska wirtualnego .venv Python zainstaluj ulubioną strukturę testową języka Python, taką jak pip install pytest. Następnie uruchom polecenie pytest tests , aby sprawdzić wynik testu.

Najpierw utwórz <plik project_root>/function_app.py i zaimplementuj my_second_function funkcję jako wyzwalacz HTTP i shared_code.my_second_helper_function.

# <project_root>/function_app.py
import azure.functions as func
import logging

# Use absolute import to resolve shared_code modules
from shared_code import my_second_helper_function

app = func.FunctionApp()

# Define the HTTP trigger that accepts the ?value=<int> query parameter
# Double the value and return the result in HttpResponse
@app.function_name(name="my_second_function")
@app.route(route="hello")
def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Executing my_second_function.')

    initial_value: int = int(req.params.get('value'))
    doubled_value: int = my_second_helper_function.double(initial_value)

    return func.HttpResponse(
        body=f"{initial_value} * 2 = {doubled_value}",
        status_code=200
    )
# <project_root>/shared_code/__init__.py
# Empty __init__.py file marks shared_code folder as a Python package
# <project_root>/shared_code/my_second_helper_function.py

def double(value: int) -> int:
  return value * 2

Możesz rozpocząć pisanie przypadków testowych dla wyzwalacza HTTP.

# <project_root>/tests/test_my_second_function.py
import unittest
import azure.functions as func

from function_app import main

class TestFunction(unittest.TestCase):
  def test_my_second_function(self):
    # Construct a mock HTTP request.
    req = func.HttpRequest(method='GET',
                           body=None,
                           url='/api/my_second_function',
                           params={'value': '21'})
    # Call the function.
    func_call = main.build().get_user_function()
    resp = func_call(req)
    # Check the output.
    self.assertEqual(
        resp.get_body(),
        b'21 * 2 = 42',
    )

W folderze środowiska wirtualnego .venv Python zainstaluj ulubioną strukturę testową języka Python, taką jak pip install pytest. Następnie uruchom polecenie pytest tests , aby sprawdzić wynik testu.

Pliki tymczasowe

Metoda tempfile.gettempdir() zwraca folder tymczasowy, który w systemie Linux to /tmp. Aplikacja może używać tego katalogu do przechowywania plików tymczasowych, które są generowane i używane przez funkcje podczas ich uruchamiania.

Ważne

Pliki zapisane w katalogu tymczasowym nie mają gwarancji, że będą utrwalane w wywołaniach. Podczas skalowania w poziomie pliki tymczasowe nie są udostępniane między wystąpieniami.

Poniższy przykład tworzy nazwany plik tymczasowy w katalogu tymczasowym (/tmp):

import logging
import azure.functions as func
import tempfile

from os import listdir

#---
   tempFilePath = tempfile.gettempdir()
   fp = tempfile.NamedTemporaryFile()
   fp.write(b'Hello world!')
   filesDirListInTemp = listdir(tempFilePath)

Zalecamy zachowanie testów w folderze, który jest oddzielony od folderu projektu. Ta akcja uniemożliwia wdrażanie kodu testowego za pomocą aplikacji.

Wstępnie zainstalowane biblioteki

Kilka bibliotek jest dostarczanych ze środowiskiem uruchomieniowym funkcji języka Python.

Standardowa biblioteka języka Python

Biblioteka standardowa języka Python zawiera listę wbudowanych modułów języka Python dostarczanych z każdą dystrybucją języka Python. Większość tych bibliotek ułatwia dostęp do funkcji systemu, takich jak dane wejściowe/wyjściowe pliku (We/Wy). W systemach Windows te biblioteki są instalowane z językiem Python. W systemach opartych na systemie Unix są one dostarczane przez kolekcje pakietów.

Aby wyświetlić bibliotekę dla używanej wersji języka Python, przejdź do:

Zależności procesu roboczego języka Python usługi Azure Functions

Proces roboczy języka Python usługi Azure Functions wymaga określonego zestawu bibliotek. Możesz również używać tych bibliotek w funkcjach, ale nie są one częścią standardu języka Python. Jeśli funkcje korzystają z dowolnej z tych bibliotek, mogą być niedostępne dla kodu, gdy działa poza usługą Azure Functions. Szczegółową listę zależności znajdziesz w sekcji "install_requires" pliku setup.py .

Uwaga

Jeśli plik requirements.txt aplikacji funkcji zawiera azure-functions-worker wpis, usuń go. Proces roboczy funkcji jest automatycznie zarządzany przez platformę Azure Functions i regularnie aktualizujemy go przy użyciu nowych funkcji i poprawek błędów. Ręczne zainstalowanie starej wersji procesu roboczego w pliku requirements.txt może spowodować nieoczekiwane problemy.

Uwaga

Jeśli pakiet zawiera pewne biblioteki, które mogą zderzać się z zależnościami procesu roboczego (na przykład protobuf, tensorflow lub grpcio), skonfiguruj element PYTHON_ISOLATE_WORKER_DEPENDENCIES w 1 ustawieniach aplikacji, aby uniemożliwić aplikacji odwoływania się do zależności procesu roboczego.

Biblioteka języka Python usługi Azure Functions

Każda aktualizacja procesu roboczego języka Python obejmuje nową wersję biblioteki języka Python usługi Azure Functions (azure.functions). Takie podejście ułatwia ciągłe aktualizowanie aplikacji funkcji języka Python, ponieważ każda aktualizacja jest zgodna z poprzednimi wersjami. Aby uzyskać listę wydań tej biblioteki, przejdź do witryny azure-functions PyPi.

Wersja biblioteki środowiska uruchomieniowego jest naprawiona przez platformę Azure i nie można jej zastąpić przez requirements.txt. azure-functions Wpis w requirements.txt dotyczy tylko podszewki i świadomości klientów.

Użyj następującego kodu, aby śledzić rzeczywistą wersję biblioteki funkcji języka Python w środowisku uruchomieniowym:

getattr(azure.functions, '__version__', '< 1.2.1')

Biblioteki systemowe środowiska uruchomieniowego

Aby uzyskać listę wstępnie zainstalowanych bibliotek systemowych w obrazach platformy Docker procesu roboczego języka Python, zobacz następujące elementy:

Środowisko uruchomieniowe usługi Functions Wersja debiana Wersje języka Python
Wersja 3.x Buster Środowisko Python w wersji 3.7
Python 3.8
Python 3.9

Rozszerzenia procesu roboczego języka Python

Proces roboczy języka Python uruchamiany w usłudze Azure Functions umożliwia integrację bibliotek innych firm z aplikacją funkcji. Te biblioteki rozszerzeń działają jako oprogramowanie pośredniczące, które może wprowadzać określone operacje podczas cyklu życia wykonywania funkcji.

Rozszerzenia są importowane w kodzie funkcji podobnie jak standardowy moduł biblioteki języka Python. Rozszerzenia są uruchamiane w oparciu o następujące zakresy:

Scope opis
Poziom aplikacji Po zaimportowaniu do dowolnego wyzwalacza funkcji rozszerzenie ma zastosowanie do każdego wykonania funkcji w aplikacji.
Poziom funkcji Wykonanie jest ograniczone tylko do określonego wyzwalacza funkcji, do którego jest importowany.

Przejrzyj informacje dotyczące każdego rozszerzenia, aby dowiedzieć się więcej o zakresie, w którym działa rozszerzenie.

Rozszerzenia implementują interfejs rozszerzenia procesu roboczego języka Python. Ta akcja umożliwia procesowi roboczego języka Python wywołanie kodu rozszerzenia podczas cyklu życia wykonywania funkcji. Aby dowiedzieć się więcej, zobacz Tworzenie rozszerzeń.

Korzystanie z rozszerzeń

Bibliotekę rozszerzenia procesu roboczego języka Python można używać w funkcjach języka Python, wykonując następujące czynności:

  1. Dodaj pakiet rozszerzenia w pliku requirements.txt dla projektu.
  2. Zainstaluj bibliotekę w aplikacji.
  3. Dodaj następujące ustawienia aplikacji:
    • Lokalnie: wprowadź "PYTHON_ENABLE_WORKER_EXTENSIONS": "1" w Values sekcji pliku local.settings.json.
    • Azure: wprowadź PYTHON_ENABLE_WORKER_EXTENSIONS=1 w ustawieniach aplikacji.
  4. Zaimportuj moduł rozszerzenia do wyzwalacza funkcji.
  5. W razie potrzeby skonfiguruj wystąpienie rozszerzenia. Wymagania dotyczące konfiguracji należy określić w dokumentacji rozszerzenia.

Ważne

Biblioteki rozszerzeń roboczych języka Python innych firm nie są obsługiwane ani nie są objęte gwarancją firmy Microsoft. Upewnij się, że wszystkie rozszerzenia używane w aplikacji funkcji są wiarygodne i ponosisz pełne ryzyko użycia złośliwego lub źle napisanego rozszerzenia.

Inne firmy powinny podać konkretną dokumentację dotyczącą sposobu instalowania i korzystania z rozszerzeń w aplikacji funkcji. Aby zapoznać się z podstawowym przykładem korzystania z rozszerzenia, zobacz Korzystanie z rozszerzenia.

Oto przykłady używania rozszerzeń w aplikacji funkcji według zakresu:

# <project_root>/requirements.txt
application-level-extension==1.0.0
# <project_root>/Trigger/__init__.py

from application_level_extension import AppExtension
AppExtension.configure(key=value)

def main(req, context):
  # Use context.app_ext_attributes here

Tworzenie rozszerzeń

Rozszerzenia są tworzone przez deweloperów bibliotek innych firm, którzy utworzyli funkcje, które można zintegrować z usługą Azure Functions. Rozszerzenie projektuje, implementuje i zwalnia pakiety języka Python zawierające logikę niestandardową zaprojektowaną specjalnie do uruchamiania w kontekście wykonywania funkcji. Te rozszerzenia można publikować w rejestrze PyPI lub w repozytoriach GitHub.

Aby dowiedzieć się, jak tworzyć, pakować, publikować i korzystać z pakietu rozszerzenia procesu roboczego języka Python, zobacz Tworzenie rozszerzeń procesów roboczych języka Python dla usługi Azure Functions.

Rozszerzenia na poziomie aplikacji

Rozszerzenie dziedziczone z AppExtensionBase przebiegów w zakresie aplikacji .

AppExtensionBase Uwidacznia następujące metody klasy abstrakcyjnej do zaimplementowania:

Metoda opis
init Wywoływana po zaimportowaniu rozszerzenia.
configure Wywoływane z kodu funkcji, gdy jest wymagane do skonfigurowania rozszerzenia.
post_function_load_app_level Wywoływana bezpośrednio po załadowaniu funkcji. Nazwa funkcji i katalog funkcji są przekazywane do rozszerzenia. Należy pamiętać, że katalog funkcji jest tylko do odczytu, a każda próba zapisu w lokalnym pliku w tym katalogu kończy się niepowodzeniem.
pre_invocation_app_level Wywoływana bezpośrednio przed wyzwoleniem funkcji. Argumenty wywołania funkcji i kontekstu funkcji są przekazywane do rozszerzenia. Zazwyczaj można przekazać inne atrybuty w obiekcie kontekstu, aby kod funkcji był używany.
post_invocation_app_level Wywołana bezpośrednio po zakończeniu wykonywania funkcji. Kontekst funkcji, argumenty wywołania funkcji i obiekt zwracany wywołania są przekazywane do rozszerzenia. Ta implementacja jest dobrym miejscem do sprawdzenia, czy wykonanie punktów zaczepienia cyklu życia zakończyło się pomyślnie.

Rozszerzenia na poziomie funkcji

Rozszerzenie dziedziczone z bazy danych FuncExtensionBase jest uruchamiane w określonym wyzwalaczu funkcji.

FuncExtensionBase Uwidacznia następujące metody klasy abstrakcyjnej dla implementacji:

Metoda opis
__init__ Konstruktor rozszerzenia. Jest wywoływana, gdy wystąpienie rozszerzenia jest inicjowane w określonej funkcji. Podczas implementowania tej metody abstrakcyjnej możesz zaakceptować filename parametr i przekazać go do metody super().__init__(filename) nadrzędnej w celu odpowiedniej rejestracji rozszerzenia.
post_function_load Wywoływana bezpośrednio po załadowaniu funkcji. Nazwa funkcji i katalog funkcji są przekazywane do rozszerzenia. Należy pamiętać, że katalog funkcji jest tylko do odczytu, a każda próba zapisu w lokalnym pliku w tym katalogu kończy się niepowodzeniem.
pre_invocation Wywoływana bezpośrednio przed wyzwoleniem funkcji. Argumenty wywołania funkcji i kontekstu funkcji są przekazywane do rozszerzenia. Zazwyczaj można przekazać inne atrybuty w obiekcie kontekstu, aby kod funkcji był używany.
post_invocation Wywołana bezpośrednio po zakończeniu wykonywania funkcji. Kontekst funkcji, argumenty wywołania funkcji i obiekt zwracany wywołania są przekazywane do rozszerzenia. Ta implementacja jest dobrym miejscem do sprawdzenia, czy wykonanie punktów zaczepienia cyklu życia zakończyło się pomyślnie.

Współużytkowanie zasobów między źródłami

Usługa Azure Functions obsługuje współużytkowanie zasobów między źródłami (CORS). Mechanizm CORS jest skonfigurowany w portalu i za pośrednictwem interfejsu wiersza polecenia platformy Azure. Lista dozwolonych źródeł mechanizmu CORS ma zastosowanie na poziomie aplikacji funkcji. Po włączeniu Access-Control-Allow-Origin mechanizmu CORS odpowiedzi zawierają nagłówek. Aby uzyskać więcej informacji, zobacz temat Współużytkowanie zasobów między źródłami.

Współużytkowanie zasobów między źródłami (CORS) jest w pełni obsługiwane w przypadku aplikacji funkcji języka Python.

Async

Domyślnie wystąpienie hosta języka Python może przetwarzać tylko jedną wywołanie funkcji jednocześnie. Dzieje się tak, ponieważ język Python jest środowiskiem uruchomieniowym jednowątkowym. W przypadku aplikacji funkcji, która przetwarza dużą liczbę zdarzeń we/wy lub jest powiązana we/wy, można znacznie zwiększyć wydajność, uruchamiając funkcje asynchronicznie. Aby uzyskać więcej informacji, zobacz Zwiększanie wydajności aplikacji języka Python w usłudze Azure Functions.

Pamięć udostępniona (wersja zapoznawcza)

Aby zwiększyć przepływność, usługa Azure Functions umożliwia procesowi roboczemu języka Python poza procesem przetwarzania języka Python udostępnianie pamięci procesowi hosta usługi Functions. Gdy aplikacja funkcji osiąga wąskie gardła, możesz włączyć pamięć udostępnioną, dodając ustawienie aplikacji o nazwie FUNCTIONS_WORKER_SHARED_MEMORY_DATA_TRANSFER_ENABLED z wartością 1. Po włączeniu pamięci udostępnionej można użyć ustawienia DOCKER_SHM_SIZE , aby ustawić pamięć udostępnioną na wartość podobną 268435456do , która jest równoważna 256 MB.

Na przykład możesz włączyć pamięć udostępnioną, aby zmniejszyć wąskie gardła, gdy używasz powiązań usługi Blob Storage do przenoszenia ładunków większych niż 1 MB.

Ta funkcja jest dostępna tylko w przypadku aplikacji funkcji działających w planach Premium i Dedicated (aplikacja systemu Azure Service). Aby dowiedzieć się więcej, zobacz Pamięć udostępniona.

Znane problemy i często zadawane pytania

Poniżej przedstawiono dwa przewodniki rozwiązywania problemów dotyczące typowych problemów:

Poniżej przedstawiono dwa przewodniki rozwiązywania problemów dotyczące znanych problemów z modelem programowania w wersji 2:

Wszystkie znane problemy i żądania funkcji są śledzone na liście problemów z usługą GitHub. Jeśli napotkasz problem i nie możesz znaleźć problemu w usłudze GitHub, otwórz nowy problem i dołącz szczegółowy opis problemu.

Następne kroki

Aby uzyskać więcej informacji, zobacz następujące zasoby:

Masz problemy z używaniem języka Python? Powiedz nam, co się dzieje.