Delen via


Aangepaste handlers voor Azure Functions

Elke Functions-app wordt uitgevoerd door een taalspecifieke handler. Hoewel Azure Functions standaard veel taalhandlers bevat, zijn er gevallen waarin u mogelijk andere talen of runtimes wilt gebruiken.

Aangepaste handlers zijn lichtgewicht webservers die gebeurtenissen ontvangen van de Functions-host. Elke taal die HTTP-primitieven ondersteunt, kan een aangepaste handler implementeren.

Aangepaste handlers zijn het meest geschikt voor situaties waarin u het volgende wilt doen:

  • Implementeer een functie-app in een taal die momenteel niet standaard wordt aangeboden, zoals Go of Rust.
  • Implementeer een functie-app in een runtime die momenteel niet standaard wordt aanbevolen, zoals Deno.

Met aangepaste handlers kunt u triggers en invoer- en uitvoerbindingen gebruiken via extensiebundels.

Aan de slag met aangepaste handlers van Azure Functions met quickstarts in Go en Rust.

Overzicht

In het volgende diagram ziet u de relatie tussen de Functions-host en een webserver die is geïmplementeerd als een aangepaste handler.

Overzicht van aangepaste handler van Azure Functions

  1. Elke gebeurtenis activeert een aanvraag die naar de Functions-host wordt verzonden. Een gebeurtenis is een trigger die wordt ondersteund door Azure Functions.
  2. De Functions-host geeft vervolgens een nettolading van een aanvraag uit aan de webserver. De nettolading bevat trigger- en invoerbindingsgegevens en andere metagegevens voor de functie.
  3. De webserver voert de afzonderlijke functie uit en retourneert een nettolading van het antwoord op de Functions-host.
  4. De Functions-host geeft gegevens van het antwoord door aan de uitvoerbindingen van de functie voor verwerking.

Een Azure Functions-app die is geïmplementeerd als een aangepaste handler, moet de bestanden host.json, local.settings.json en function.json volgens een paar conventies configureren.

Toepassingsstructuur

Als u een aangepaste handler wilt implementeren, hebt u de volgende aspecten voor uw toepassing nodig:

  • Een host.json-bestand in de hoofdmap van uw app
  • Een local.settings.json-bestand in de hoofdmap van uw app
  • Een function.json-bestand voor elke functie (in een map die overeenkomt met de functienaam)
  • Een opdracht, script of uitvoerbaar bestand, waarop een webserver wordt uitgevoerd

In het volgende diagram ziet u hoe deze bestanden eruitzien in het bestandssysteem voor een functie met de naam 'MyQueueFunction' en een aangepast handler-uitvoerbare bestand met de naam handler.exe.

| /MyQueueFunction
|   function.json
|
| host.json
| local.settings.json
| handler.exe

Configuratie

De toepassing wordt geconfigureerd via de host.json- en local.settings.json-bestanden.

host.json

host.json vertelt de Functions-host waar aanvragen moeten worden verzonden door te verwijzen naar een webserver die HTTP-gebeurtenissen kan verwerken.

Een aangepaste handler wordt gedefinieerd door het host.json-bestand te configureren met details over het uitvoeren van de webserver via de customHandler sectie.

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  }
}

De customHandler sectie verwijst naar een doel zoals gedefinieerd door de defaultExecutablePath. Het uitvoeringsdoel kan een opdracht, uitvoerbaar bestand of bestand zijn waarop de webserver is geïmplementeerd.

Gebruik de arguments matrix om argumenten door te geven aan het uitvoerbare bestand. Argumenten ondersteunen uitbreiding van omgevingsvariabelen (toepassingsinstellingen) met behulp van %% notatie.

U kunt ook de werkmap wijzigen die door het uitvoerbare bestand wordt gebruikt.workingDirectory

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "app/handler.exe",
      "arguments": [
        "--database-connection-string",
        "%DATABASE_CONNECTION_STRING%"
      ],
      "workingDirectory": "app"
    }
  }
}
Ondersteuning voor bindingen

Standaardtriggers samen met invoer- en uitvoerbindingen zijn beschikbaar door te verwijzen naar extensiebundels in uw host.json-bestand .

local.settings.json

local.settings.json definieert toepassingsinstellingen die worden gebruikt bij het lokaal uitvoeren van de functie-app. Omdat het geheimen kan bevatten, moet local.settings.json worden uitgesloten van broncodebeheer. In Azure gebruikt u in plaats daarvan toepassingsinstellingen.

Stel voor aangepaste handlers FUNCTIONS_WORKER_RUNTIME Custom in local.settings.json.

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "Custom"
  }
}

Functiemetagegevens

Wanneer u met een aangepaste handler werkt, verschilt de function.json inhoud niet van de manier waarop u een functie definieert in een andere context. De enige vereiste is dat function.json bestanden zich in een map met de naam van de functie moeten bevinden.

Met de volgende function.json configureert u een functie met een wachtrijtrigger en een wachtrijuitvoerbinding. Omdat deze zich in een map met de naam MyQueueFunction bevindt, definieert deze een functie met de naam MyQueueFunction.

MyQueueFunction/function.json

{
  "bindings": [
    {
      "name": "myQueueItem",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "messages-incoming",
      "connection": "AzureWebJobsStorage"
    },
    {
      "name": "$return",
      "type": "queue",
      "direction": "out",
      "queueName": "messages-outgoing",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

Nettolading aanvragen

Wanneer een wachtrijbericht wordt ontvangen, verzendt de Functions-host een HTTP-postaanvraag naar de aangepaste handler met een nettolading in de hoofdtekst.

De volgende code vertegenwoordigt een nettolading van een voorbeeldaanvraag. De nettolading bevat een JSON-structuur met twee leden: Data en Metadata.

Het Data lid bevat sleutels die overeenkomen met invoer- en triggernamen zoals gedefinieerd in de bindingsmatrix in het function.json-bestand .

Het Metadata lid bevat metagegevens die zijn gegenereerd op basis van de gebeurtenisbron.

{
  "Data": {
    "myQueueItem": "{ message: \"Message sent\" }"
  },
  "Metadata": {
    "DequeueCount": 1,
    "ExpirationTime": "2019-10-16T17:58:31+00:00",
    "Id": "800ae4b3-bdd2-4c08-badd-f08e5a34b865",
    "InsertionTime": "2019-10-09T17:58:31+00:00",
    "NextVisibleTime": "2019-10-09T18:08:32+00:00",
    "PopReceipt": "AgAAAAMAAAAAAAAAAgtnj8x+1QE=",
    "sys": {
      "MethodName": "QueueTrigger",
      "UtcNow": "2019-10-09T17:58:32.2205399Z",
      "RandGuid": "24ad4c06-24ad-4e5b-8294-3da9714877e9"
    }
  }
}

Nettolading van antwoord

Volgens de conventie worden functiereacties opgemaakt als sleutel-/waardeparen. Ondersteunde sleutels zijn onder andere:

Nettoladingsleutel Gegevenstype Opmerkingen
Outputs object Bevat antwoordwaarden zoals gedefinieerd door de bindings matrix in function.json.

Als een functie bijvoorbeeld is geconfigureerd met een wachtrijuitvoerbinding met de naam 'myQueueOutput', bevat deze Outputs een sleutel met de naam myQueueOutput, die door de aangepaste handler wordt ingesteld op de berichten die naar de wachtrij worden verzonden.
Logs matrix Berichten worden weergegeven in de aanroeplogboeken van Functions.

Wanneer berichten worden uitgevoerd in Azure, worden berichten weergegeven in Application Insights.
ReturnValue tekenreeks Wordt gebruikt om een antwoord te geven wanneer een uitvoer is geconfigureerd zoals $return in het function.json-bestand .

Dit is een voorbeeld van een nettolading van een antwoord.

{
  "Outputs": {
    "res": {
      "body": "Message enqueued"
    },
    "myQueueOutput": [
      "queue message 1",
      "queue message 2"
    ]
  },
  "Logs": [
    "Log message 1",
    "Log message 2"
  ],
  "ReturnValue": "{\"hello\":\"world\"}"
}

Voorbeelden

Aangepaste handlers kunnen worden geïmplementeerd in elke taal die ondersteuning biedt voor het ontvangen van HTTP-gebeurtenissen. In de volgende voorbeelden ziet u hoe u een aangepaste handler implementeert met behulp van de programmeertaal Go.

Functie met bindingen

Het scenario dat in dit voorbeeld wordt geïmplementeerd, bevat een functie die order een met een POST nettolading accepteert die een productorder vertegenwoordigt. Als er een order naar de functie wordt geplaatst, wordt er een Queue Storage-bericht gemaakt en wordt er een HTTP-antwoord geretourneerd.

Implementatie

In een map met de naamvolgorde configureert het bestand function.json de door HTTP geactiveerde functie.

order/function.json

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": ["post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    },
    {
      "type": "queue",
      "name": "message",
      "direction": "out",
      "queueName": "orders",
      "connection": "AzureWebJobsStorage"
    }
  ]
}

Deze functie wordt gedefinieerd als een door HTTP geactiveerde functie die een HTTP-antwoord retourneert en een Queue Storage-bericht uitvoert.

In de hoofdmap van de app is het host.json-bestand geconfigureerd voor het uitvoeren van een uitvoerbaar bestand met de naam handler.exe (handlerin Linux of macOS).

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  }
}

Dit is de HTTP-aanvraag die naar de Functions-runtime wordt verzonden.

POST http://127.0.0.1:7071/api/order HTTP/1.1
Content-Type: application/json

{
  "id": 1005,
  "quantity": 2,
  "color": "black"
}

De Functions-runtime verzendt vervolgens de volgende HTTP-aanvraag naar de aangepaste handler:

POST http://127.0.0.1:<FUNCTIONS_CUSTOMHANDLER_PORT>/order HTTP/1.1
Content-Type: application/json

{
  "Data": {
    "req": {
      "Url": "http://localhost:7071/api/order",
      "Method": "POST",
      "Query": "{}",
      "Headers": {
        "Content-Type": [
          "application/json"
        ]
      },
      "Params": {},
      "Body": "{\"id\":1005,\"quantity\":2,\"color\":\"black\"}"
    }
  },
  "Metadata": {
  }
}

Notitie

Sommige gedeelten van de nettolading zijn verwijderd om kortheid te hebben.

handler.exe is het gecompileerde aangepaste Go-handlerprogramma dat een webserver uitvoert en reageert op aanvragen voor functie-aanroep van de Functions-host.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

type InvokeRequest struct {
	Data     map[string]json.RawMessage
	Metadata map[string]interface{}
}

type InvokeResponse struct {
	Outputs     map[string]interface{}
	Logs        []string
	ReturnValue interface{}
}

func orderHandler(w http.ResponseWriter, r *http.Request) {
	var invokeRequest InvokeRequest

	d := json.NewDecoder(r.Body)
	d.Decode(&invokeRequest)

	var reqData map[string]interface{}
	json.Unmarshal(invokeRequest.Data["req"], &reqData)

	outputs := make(map[string]interface{})
	outputs["message"] = reqData["Body"]

	resData := make(map[string]interface{})
	resData["body"] = "Order enqueued"
	outputs["res"] = resData
	invokeResponse := InvokeResponse{outputs, nil, nil}

	responseJson, _ := json.Marshal(invokeResponse)

	w.Header().Set("Content-Type", "application/json")
	w.Write(responseJson)
}

func main() {
	customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
	if !exists {
		customHandlerPort = "8080"
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/order", orderHandler)
	fmt.Println("Go server Listening on: ", customHandlerPort)
	log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
}

In dit voorbeeld voert de aangepaste handler een webserver uit om HTTP-gebeurtenissen te verwerken en is ingesteld om te luisteren naar aanvragen via de FUNCTIONS_CUSTOMHANDLER_PORT.

Hoewel de Functions-host de oorspronkelijke HTTP-aanvraag heeft /api/orderontvangen, wordt de aangepaste handler aangeroepen met behulp van de functienaam (de mapnaam). In dit voorbeeld wordt de functie gedefinieerd op het pad van /order. De host verzendt de aangepaste handler een HTTP-aanvraag op het pad van /order.

Wanneer POST aanvragen naar deze functie worden verzonden, zijn de triggergegevens en functiemetagegevens beschikbaar via de hoofdtekst van de HTTP-aanvraag. De oorspronkelijke HTTP-aanvraagbody kan worden geopend in de payload Data.req.Body.

Het antwoord van de functie wordt opgemaakt in sleutel-/waardeparen waarbij het Outputs lid een JSON-waarde bevat waarin de sleutels overeenkomen met de uitvoer zoals gedefinieerd in het function.json-bestand .

Dit is een voorbeeld van een nettolading die door deze handler wordt geretourneerd naar de Functions-host.

{
  "Outputs": {
    "message": "{\"id\":1005,\"quantity\":2,\"color\":\"black\"}",
    "res": {
      "body": "Order enqueued"
    }
  },
  "Logs": null,
  "ReturnValue": null
}

Door de message uitvoer in te stellen die gelijk is aan de ordergegevens die afkomstig zijn van de aanvraag, voert de functie gegevens uit naar de geconfigureerde wachtrij. De Functions-host retourneert ook het HTTP-antwoord dat is geconfigureerd voor res de aanroeper.

Http-functie

Voor door HTTP geactiveerde functies zonder extra bindingen of uitvoer wilt u mogelijk dat uw handler rechtstreeks met de HTTP-aanvraag en -reactie werkt in plaats van de aangepaste handleraanvraag en nettoladingen van reacties. Dit gedrag kan worden geconfigureerd in host.json met behulp van de enableForwardingHttpRequest instelling.

Belangrijk

Het primaire doel van de functie voor aangepaste handlers is het inschakelen van talen en runtimes die momenteel geen eersteklas ondersteuning hebben voor Azure Functions. Hoewel het mogelijk is om webtoepassingen uit te voeren met behulp van aangepaste handlers, is Azure Functions geen standaard omgekeerde proxy. Sommige functies, zoals antwoordstreaming, HTTP/2 en WebSockets, zijn niet beschikbaar. Sommige onderdelen van de HTTP-aanvraag, zoals bepaalde headers en routes, kunnen worden beperkt. Uw toepassing kan ook te veel koude start ervaren.

U kunt deze omstandigheden oplossen door uw web-apps uit te voeren op Azure-app Service.

In het volgende voorbeeld ziet u hoe u een door HTTP geactiveerde functie configureert zonder extra bindingen of uitvoer. Het scenario dat in dit voorbeeld wordt geïmplementeerd, bevat een functie met de naam hello die een GET of POST .

Implementatie

In een map met de naam Hello configureert het function.json-bestand de door HTTP geactiveerde functie.

hello/function.json

{
  "bindings": [
    {
      "type": "httpTrigger",
      "authLevel": "anonymous",
      "direction": "in",
      "name": "req",
      "methods": ["get", "post"]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

De functie is geconfigureerd om zowel aanvragen GET POST als aanvragen te accepteren en de resultaatwaarde wordt opgegeven via een argument met de naam res.

In de hoofdmap van de app is het host.json-bestand geconfigureerd om uit te voeren handler.exe en enableForwardingHttpRequest is ingesteld op true.

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    },
    "enableForwardingHttpRequest": true
  }
}

Wanneer enableForwardingHttpRequest is true, verschilt het gedrag van alleen HTTP-functies op de volgende manieren van het standaardgedrag van aangepaste handlers:

  • De HTTP-aanvraag bevat niet de nettolading van de aanvraag voor aangepaste handlers. In plaats daarvan roept de Functions-host de handler aan met een kopie van de oorspronkelijke HTTP-aanvraag.
  • De Functions-host roept de handler aan met hetzelfde pad als de oorspronkelijke aanvraag, inclusief eventuele queryreeksparameters.
  • De Functions-host retourneert een kopie van het HTTP-antwoord van de handler als het antwoord op de oorspronkelijke aanvraag.

Hier volgt een POST-aanvraag voor de Functions-host. De Functions-host verzendt vervolgens een kopie van de aanvraag naar de aangepaste handler op hetzelfde pad.

POST http://127.0.0.1:7071/api/hello HTTP/1.1
Content-Type: application/json

{
  "message": "Hello World!"
}

Het bestand handler.go implementeert een webserver en een HTTP-functie.

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	if r.Method == "GET" {
		w.Write([]byte("hello world"))
	} else {
		body, _ := ioutil.ReadAll(r.Body)
		w.Write(body)
	}
}

func main() {
	customHandlerPort, exists := os.LookupEnv("FUNCTIONS_CUSTOMHANDLER_PORT")
	if !exists {
		customHandlerPort = "8080"
	}
	mux := http.NewServeMux()
	mux.HandleFunc("/api/hello", helloHandler)
	fmt.Println("Go server Listening on: ", customHandlerPort)
	log.Fatal(http.ListenAndServe(":"+customHandlerPort, mux))
}

In dit voorbeeld maakt de aangepaste handler een webserver voor het afhandelen van HTTP-gebeurtenissen en wordt ingesteld om te luisteren naar aanvragen via de FUNCTIONS_CUSTOMHANDLER_PORT.

GET aanvragen worden verwerkt door een tekenreeks te retourneren en POST aanvragen hebben toegang tot de hoofdtekst van de aanvraag.

De route voor de orderfunctie is /api/hellohier hetzelfde als de oorspronkelijke aanvraag.

Notitie

Dit FUNCTIONS_CUSTOMHANDLER_PORT is niet de openbare poort die wordt gebruikt om de functie aan te roepen. Deze poort wordt door de Functions-host gebruikt om de aangepaste handler aan te roepen.

Implementeren

Een aangepaste handler kan worden geïmplementeerd op elke Azure Functions-hostingoptie. Als voor uw handler besturingssysteem- of platformafhankelijkheden (zoals een taalruntime) zijn vereist, moet u mogelijk een aangepaste container gebruiken.

Wanneer u een functie-app maakt in Azure voor aangepaste handlers, raden we u aan .NET Core als de stack te selecteren.

Voer de volgende opdracht uit om een aangepaste handler-app te implementeren met behulp van Azure Functions Core Tools.

func azure functionapp publish $functionAppName

Notitie

Zorg ervoor dat alle bestanden die nodig zijn om uw aangepaste handler uit te voeren, zich in de map bevinden en zijn opgenomen in de implementatie. Als uw aangepaste handler een binair uitvoerbaar bestand is of platformspecifieke afhankelijkheden heeft, moet u ervoor zorgen dat deze bestanden overeenkomen met het doelimplementatieplatform.

Beperkingen

  • De aangepaste handlerwebserver moet binnen 60 seconden starten.

Voorbeelden

Raadpleeg de GitHub-opslagplaats voor aangepaste handlervoorbeelden voor voorbeelden van het implementeren van functies in verschillende talen.

Probleemoplossing en ondersteuning

Traceerlogboekregistratie

Als het aangepaste handlerproces niet kan worden opgestart of als er problemen zijn met de communicatie met de Functions-host, kunt u het logboekniveau van de functie-app verhogen om Trace meer diagnostische berichten van de host te zien.

Als u het standaardlogboekniveau van de functie-app wilt wijzigen, configureert u de logLevel instelling in de logging sectie van host.json.

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "handler.exe"
    }
  },
  "logging": {
    "logLevel": {
      "default": "Trace"
    }
  }
}

De Functions-host voert extra logboekberichten uit, inclusief informatie met betrekking tot het aangepaste handlerproces. Gebruik de logboeken om problemen te onderzoeken bij het starten van uw aangepaste handlerproces of het aanroepen van functies in uw aangepaste handler.

Lokaal worden logboeken afgedrukt naar de console.

Voer in Azure query's uit op Application Insights-traceringen om de logboekberichten weer te geven. Als uw app een groot aantal logboeken produceert, worden alleen een subset logboekberichten verzonden naar Application Insights. Schakel steekproeven uit om ervoor te zorgen dat alle berichten worden geregistreerd.

Aangepaste handler in isolatie testen

Aangepaste handler-apps zijn een webserverproces, dus het kan handig zijn om deze zelf te starten en functie-aanroepen te testen door mock-HTTP-aanvragen te verzenden. Voor het verzenden van HTTP-aanvragen met nettoladingen moet u een hulpprogramma kiezen waarmee uw gegevens veilig blijven. Zie HTTP-testhulpprogramma's voor meer informatie.

U kunt deze strategie ook gebruiken in uw CI/CD-pijplijnen om geautomatiseerde tests uit te voeren op uw aangepaste handler.

Uitvoeringsomgeving

Aangepaste handlers worden uitgevoerd in dezelfde omgeving als een typische Azure Functions-app. Test uw handler om ervoor te zorgen dat de omgeving alle afhankelijkheden bevat die moeten worden uitgevoerd. Voor apps waarvoor extra afhankelijkheden zijn vereist, moet u deze mogelijk uitvoeren met behulp van een aangepaste containerinstallatiekopie die wordt gehost in een Azure Functions Premium-abonnement.

Ondersteuning krijgen

Als u hulp nodig hebt bij een functie-app met aangepaste handlers, kunt u een aanvraag indienen via reguliere ondersteuningskanalen. Vanwege de grote verscheidenheid aan mogelijke talen die worden gebruikt voor het bouwen van aangepaste handlers-apps, is ondersteuning echter niet onbeperkt.

Ondersteuning is beschikbaar als de Functions-host problemen ondervindt bij het starten of communiceren met het aangepaste handlerproces. Voor problemen die specifiek zijn voor de interne werking van uw aangepaste handlerproces, zoals problemen met de gekozen taal of het gekozen framework, kan ons ondersteuningsteam in deze context geen hulp bieden.

Volgende stappen

Ga aan de slag met het bouwen van een Azure Functions-app in Go of Rust met de quickstart voor aangepaste handlers.