Dela via


Anpassade hanterare för Azure Functions

Varje Functions-app körs av en språkspecifik hanterare. Även om Azure Functions har många språkhanterare som standard, finns det fall där du kanske vill använda andra språk eller körningsmiljöer.

Anpassade hanterare är enkla webbservrar som tar emot händelser från Functions-värden. Alla språk som stöder HTTP-primitiver kan implementera en anpassad hanterare.

Anpassade hanterare passar bäst för situationer där du vill:

  • Implementera en funktionsapp på ett språk som för närvarande inte erbjuds direkt, till exempel Go eller Rust.
  • Implementera en funktionsapp i en körning som för närvarande inte finns med som standard, till exempel Deno.

Med anpassade hanterare kan du använda utlösare och indata- och utdatabindningar via tilläggspaket.

Kom igång med Azure Functions anpassade hanterare med snabbstarter i Go och Rust.

Översikt

Följande diagram visar relationen mellan Functions-värden och en webbserver som implementeras som en anpassad hanterare.

översikt över Azure Functions anpassad hanterare

  1. Varje händelse utlöser en begäran som skickas till Functions-värden. En händelse är en utlösare som stöds av Azure Functions.
  2. Functions-värden skickar sedan en nyttolast för begäran till webbservern. Nyttolasten innehåller utlösar- och indatabindningsdata och andra metadata för funktionen.
  3. Webbservern kör den enskilda funktionen och returnerar en svarsnyttolast till Functions-värden.
  4. Functions-värden skickar data från svaret till funktionens utdatabindningar för bearbetning.

En Azure Functions-app som implementeras som en anpassad hanterare måste konfigurera filerna host.json, local.settings.json och function.json enligt några konventioner.

Programstruktur

Om du vill implementera en anpassad hanterare behöver du följande aspekter av ditt program:

  • En host.json-fil i appens rot
  • En local.settings.json-fil i appens rot
  • En function.json-fil för varje funktion (i en mapp som matchar funktionsnamnet)
  • Ett kommando, skript eller körbar fil som kör en webbserver

Följande diagram visar hur de här filerna ser ut i filsystemet för en funktion med namnet "MyQueueFunction" och en anpassad körbar filhanterare med namnet handler.exe.

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

Konfiguration

Programmet konfigureras via filerna host.json och local.settings.json .

host.json

host.json talar om för Functions-värden var begäranden ska skickas genom att peka på en webbserver som kan bearbeta HTTP-händelser.

En anpassad hanterare definieras genom att konfigurera filen host.json med information om hur du kör webbservern via customHandler avsnittet .

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

Avsnittet customHandler pekar på ett mål som definieras av defaultExecutablePath. Körningsmålet kan antingen vara ett kommando, en körbar fil eller en fil där webbservern implementeras.

Använd matrisen arguments för att skicka eventuella argument till den körbara filen. Argument stöder expansion av miljövariabler (programinställningar) med hjälp av %% notation.

Du kan också ändra arbetskatalogen som används av den körbara filen med workingDirectory.

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "app/handler.exe",
      "arguments": [
        "--database-connection-string",
        "%DATABASE_CONNECTION_STRING%"
      ],
      "workingDirectory": "app"
    }
  }
}
Stöd för bindningar

Standardutlösare tillsammans med indata- och utdatabindningar är tillgängliga genom att referera till tilläggspaket i host.json-filen .

local.settings.json

local.settings.json definierar programinställningar som används när funktionsappen körs lokalt. Eftersom den kan innehålla hemligheter bör local.settings.json undantas från källkontrollen. I Azure använder du programinställningar i stället.

För anpassade hanterare anger du FUNCTIONS_WORKER_RUNTIME till Custom i local.settings.json.

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

Funktionsmetadata

När det används med en anpassad hanterare skiljer sig innehållet i function.json inte från hur du definierar en funktion i någon annan kontext. Det enda kravet är att function.json-filer måste finnas i en mapp med namnet för att matcha funktionsnamnet.

Följande function.json konfigurerar en funktion som har en köutlösare och en köutdatabindning. Eftersom den finns i en mapp med namnet MyQueueFunction definierar den en funktion med namnet 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"
    }
  ]
}

Begär nyttolast

När ett kömeddelande tas emot skickar Functions-värden en HTTP-postbegäran till den anpassade hanteraren med en nyttolast i brödtexten.

Följande kod representerar en nyttolast för exempelbegäran. Nyttolasten innehåller en JSON-struktur med två medlemmar: Data och Metadata.

Medlemmen Data innehåller nycklar som matchar indata- och utlösarnamn enligt definitionen i bindningsmatrisen i filen function.json .

Medlemmen Metadata innehåller metadata som genererats från händelsekällan.

{
  "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"
    }
  }
}

Svarsnyttolast

Enligt konventionen formateras funktionssvar som nyckel/värde-par. Nycklar som stöds är:

Nyttolastnyckel Datatyp Kommentarer
Outputs objekt Innehåller svarsvärden som definieras av matrisen bindings i function.json.

Om en funktion till exempel har konfigurerats med en köutdatabindning med namnet "myQueueOutput" innehåller den Outputs en nyckel med namnet myQueueOutput, som anges av den anpassade hanteraren för de meddelanden som skickas till kön.
Logs matris Meddelanden visas i Functions-anropsloggarna.

När du kör i Azure visas meddelanden i Application Insights.
ReturnValue sträng Används för att ge ett svar när utdata konfigureras som $return i filen function.json .

Det här är ett exempel på en svarsnyttolast.

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

Exempel

Anpassade hanterare kan implementeras på alla språk som stöder mottagning av HTTP-händelser. I följande exempel visas hur du implementerar en anpassad hanterare med programmeringsspråket Go.

Funktion med bindningar

Scenariot som implementeras i det här exemplet har en funktion med namnet order som accepterar en POST med en nyttolast som representerar en produktbeställning. När en order publiceras i funktionen skapas ett Queue Storage-meddelande och ett HTTP-svar returneras.

Implementering

I en mapp med namnet order konfigurerar filen function.json den HTTP-utlösta funktionen.

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"
    }
  ]
}

Den här funktionen definieras som en HTTP-utlöst funktion som returnerar ett HTTP-svar och matar ut ett Queue Storage-meddelande .

I appens rot är filen host.json konfigurerad för att köra en körbar fil med namnet handler.exe (handler i Linux eller macOS).

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

Det här är HTTP-begäran som skickas till Functions-körningen.

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

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

Functions-körningen skickar sedan följande HTTP-begäran till den anpassade hanteraren:

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": {
  }
}

Anteckning

Vissa delar av nyttolasten har tagits bort av utrymmesskäl.

handler.exe är det kompilerade anpassade Go-hanterarprogrammet som kör en webbserver och svarar på funktionsanropsbegäranden från Functions-värden.

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))
}

I det här exemplet kör den anpassade hanteraren en webbserver för att hantera HTTP-händelser och är inställd på att lyssna efter begäranden via FUNCTIONS_CUSTOMHANDLER_PORT.

Även om Functions-värden tog emot den ursprungliga HTTP-begäran på /api/orderanropar den den anpassade hanteraren med hjälp av funktionsnamnet (dess mappnamn). I det här exemplet definieras funktionen i sökvägen /ordertill . Värden skickar den anpassade hanteraren en HTTP-begäran på sökvägen /ordertill .

När POST begäranden skickas till den här funktionen är utlösardata och funktionsmetadata tillgängliga via HTTP-begärandetexten. Den ursprungliga HTTP-begärandetexten kan nås i nyttolastens Data.req.Body.

Funktionens svar formateras till nyckel/värde-par där Outputs medlemmen har ett JSON-värde där nycklarna matchar utdata enligt definitionen i filen function.json .

Det här är ett exempel på en nyttolast som den här hanteraren returnerar till Functions-värden.

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

Genom att ange message utdata som är lika med orderdata som kom in från begäran, matar funktionen ut som beställer data till den konfigurerade kön. Functions-värden returnerar också DET HTTP-svar som konfigurerats i res till anroparen.

Funktionen endast HTTP

För HTTP-utlösta funktioner utan ytterligare bindningar eller utdata kanske du vill att hanteraren ska arbeta direkt med HTTP-begäran och -svaret i stället för nyttolasten för anpassad hanterarbegäran och svar . Det här beteendet kan konfigureras i host.json med hjälp av inställningen enableForwardingHttpRequest .

Viktigt

Det primära syftet med funktionen för anpassade hanterare är att aktivera språk och körningsmiljöer som för närvarande inte har förstklassigt stöd på Azure Functions. Det kan vara möjligt att köra webbprogram med anpassade hanterare, men Azure Functions är inte en omvänd standardproxy. Vissa funktioner som svarsströmning, HTTP/2 och WebSockets är inte tillgängliga. Vissa komponenter i HTTP-begäran, till exempel vissa huvuden och vägar, kan vara begränsade. Programmet kan också uppleva överdriven kallstart.

Överväg att köra dina webbappar på Azure App Service för att åtgärda dessa omständigheter.

I följande exempel visas hur du konfigurerar en HTTP-utlöst funktion utan ytterligare bindningar eller utdata. Scenariot som implementeras i det här exemplet har en funktion med namnet hello som accepterar en GET eller POST .

Implementering

I en mapp med namnet hello konfigurerar filen function.json den HTTP-utlösta funktionen.

hello/function.json

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

Funktionen är konfigurerad för att acceptera både GET - och POST -begäranden och resultatvärdet anges via ett argument med namnet res.

I appens rot är filen host.json konfigurerad att köras handler.exe och enableForwardingHttpRequest är inställd på true.

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

När enableForwardingHttpRequest är trueskiljer sig beteendet för http-only-funktioner från standardbeteendet för anpassade hanterare på följande sätt:

  • HTTP-begäran innehåller inte nyttolasten för anpassade hanterares begäran . I stället anropar Functions-värden hanteraren med en kopia av den ursprungliga HTTP-begäran.
  • Functions-värden anropar hanteraren med samma sökväg som den ursprungliga begäran, inklusive eventuella frågesträngsparametrar.
  • Functions-värden returnerar en kopia av hanterarens HTTP-svar som svar på den ursprungliga begäran.

Följande är en POST-begäran till Functions-värden. Functions-värden skickar sedan en kopia av begäran till den anpassade hanteraren på samma sökväg.

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

{
  "message": "Hello World!"
}

Filen handler.go implementerar en webbserver och HTTP-funktion.

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))
}

I det här exemplet skapar den anpassade hanteraren en webbserver för att hantera HTTP-händelser och är inställd på att lyssna efter begäranden via FUNCTIONS_CUSTOMHANDLER_PORT.

GET begäranden hanteras genom att en sträng returneras och POST begäranden har åtkomst till begärandetexten.

Vägen för orderfunktionen här är /api/hello, samma som den ursprungliga begäran.

Anteckning

FUNCTIONS_CUSTOMHANDLER_PORT är inte den offentliga porten som används för att anropa funktionen. Den här porten används av Functions-värden för att anropa den anpassade hanteraren.

Distribuera

En anpassad hanterare kan distribueras till varje Azure Functions värdalternativ. Om hanteraren kräver operativsystem- eller plattformsberoenden (till exempel en språkkörning) kan du behöva använda en anpassad container.

När du skapar en funktionsapp i Azure för anpassade hanterare rekommenderar vi att du väljer .NET Core som stack.

Om du vill distribuera en anpassad hanteringsapp med hjälp av Azure Functions Core Tools kör du följande kommando.

func azure functionapp publish $functionAppName

Anteckning

Se till att alla filer som krävs för att köra din anpassade hanterare finns i mappen och ingår i distributionen. Om din anpassade hanterare är binär körbar eller har plattformsspecifika beroenden kontrollerar du att dessa filer matchar måldistributionsplattformen.

Begränsningar

  • Webbservern för den anpassade hanteraren måste starta inom 60 sekunder.

Exempel

Se GitHub-lagringsplatsen med anpassade hanterarexempel för exempel på hur du implementerar funktioner på olika språk.

Felsökning och support

Spårningsloggning

Om din anpassade hanteringsprocess inte kan startas eller om den har problem med att kommunicera med Functions-värden kan du öka funktionsappens loggnivå till Trace att se fler diagnostiska meddelanden från värden.

Om du vill ändra funktionsappens standardloggnivå konfigurerar du logLevel inställningen i logging avsnittet i host.json.

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

Functions-värden matar ut extra loggmeddelanden, inklusive information som rör den anpassade hanteringsprocessen. Använd loggarna för att undersöka problem med att starta din anpassade hanteringsprocess eller anropa funktioner i din anpassade hanterare.

Lokalt skrivs loggar ut till konsolen.

I Azure kör du frågor mot Application Insights-spårningar för att visa loggmeddelandena. Om din app skapar en stor mängd loggar skickas endast en delmängd av loggmeddelandena till Application Insights. Inaktivera sampling för att säkerställa att alla meddelanden loggas.

Testa anpassad hanterare isolerat

Anpassade hanteringsappar är en webbserverprocess, så det kan vara bra att starta den på egen hand och testa funktionsanrop genom att skicka simulerade HTTP-begäranden med hjälp av ett verktyg som cURL eller Postman.

Du kan också använda den här strategin i dina CI/CD-pipelines för att köra automatiserade tester på din anpassade hanterare.

Körningsmiljö

Anpassade hanterare körs i samma miljö som en typisk Azure Functions app. Testa hanteraren för att säkerställa att miljön innehåller alla beroenden som behövs för att köras. För appar som kräver ytterligare beroenden kan du behöva köra dem med en anpassad containeravbildning som finns i Azure Functions Premium-plan.

Få support

Om du behöver hjälp med en funktionsapp med anpassade hanterare kan du skicka en begäran via vanliga supportkanaler. Men på grund av de många olika språk som används för att skapa anpassade hanterarappar är stödet inte obegränsat.

Stöd är tillgängligt om Functions-värden har problem med att starta eller kommunicera med den anpassade hanteringsprocessen. För problem som är specifika för det inre arbetet i din anpassade hanteringsprocess, till exempel problem med det valda språket eller ramverket, kan vårt supportteam inte ge hjälp i den här kontexten.

Nästa steg

Kom igång med att skapa en Azure Functions-app i Go eller Rust med snabbstarten för anpassade hanterare.