Vlastní obslužné rutiny Azure Functions

Každou aplikaci Functions spouští obslužná rutina specifická pro konkrétní jazyk. I když Azure Functions ve výchozím nastavení obsahuje mnoho jazykových obslužných rutin, existují případy, kdy můžete chtít použít jiné jazyky nebo moduly runtime.

Vlastní obslužné rutiny jsou jednoduché webové servery, které přijímají události z hostitele služby Functions. Každý jazyk, který podporuje primitiva HTTP, může implementovat vlastní obslužnou rutinu.

Vlastní obslužné rutiny jsou nejvhodnější pro situace, kdy chcete:

  • Implementujte aplikaci funkcí v jazyce, který se momentálně nenabízí, například Go nebo Rust.
  • Implementujte aplikaci funkcí v modulu runtime, který není aktuálně ve výchozím nastavení, například Deno.

S vlastními obslužnými rutinami můžete používat triggery a vstupní a výstupní vazby prostřednictvím sad rozšíření.

Začínáme s Azure Functions vlastními obslužnými rutinami pomocí rychlých startů v Go a Rustu.

Přehled

Následující diagram znázorňuje vztah mezi hostitelem služby Functions a webovým serverem implementovaným jako vlastní obslužná rutina.

Azure Functions přehled vlastní obslužné rutiny

  1. Každá událost aktivuje požadavek odeslaný hostiteli Functions. Událost je libovolný trigger, který Azure Functions podporuje.
  2. Hostitel služby Functions pak webovému serveru vydá datovou část požadavku . Datová část obsahuje data aktivačních a vstupních vazeb a další metadata pro funkci.
  3. Webový server spustí jednotlivé funkce a vrátí do hostitele functions datovou část odpovědi .
  4. Hostitel functions předává data z odpovědi do výstupních vazeb funkce ke zpracování.

Aplikace Azure Functions implementovaná jako vlastní obslužná rutina musí nakonfigurovat soubory host.json, local.settings.json a function.json podle několika konvencí.

Struktura aplikace

K implementaci vlastní obslužné rutiny potřebujete pro vaši aplikaci následující aspekty:

  • Soubor host.json v kořenovém adresáři vaší aplikace
  • Soubor local.settings.json v kořenovém adresáři aplikace
  • Soubor function.json pro každou funkci (uvnitř složky, která odpovídá názvu funkce)
  • Příkaz, skript nebo spustitelný soubor, na kterém běží webový server

Následující diagram znázorňuje, jak tyto soubory vypadají v systému souborů pro funkci s názvem "MyQueueFunction" a spustitelný soubor vlastní obslužné rutiny s názvemhandler.exe.

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

Konfigurace

Aplikace se konfiguruje prostřednictvím souborů host.json a local.settings.json .

host.json

soubor host.json říká hostiteli functions, kam má odesílat požadavky, a to tak, že nasměruje na webový server schopný zpracovávat události HTTP.

Vlastní obslužná rutina je definována konfigurací souboru host.json s podrobnostmi o tom, jak spustit webový server prostřednictvím oddílu customHandler .

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

Oddíl customHandler odkazuje na cíl definovaný objektem defaultExecutablePath. Cílem spuštění může být příkaz, spustitelný soubor nebo soubor, ve kterém je implementován webový server.

arguments Pomocí pole předejte všechny argumenty spustitelnému souboru. Argumenty podporují rozšíření proměnných prostředí (nastavení aplikace) pomocí %% zápisu.

Pracovní adresář používaný spustitelným souborem můžete také změnit pomocí workingDirectorypříkazu .

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

Standardní triggery spolu se vstupními a výstupními vazbami jsou k dispozici odkazem na balíčky rozšíření v souboru host.json .

local.settings.json

local.settings.json definuje nastavení aplikace používané při místním spuštění aplikace funkcí. Protože může obsahovat tajné kódy, local.settings.json by měl být vyloučen ze správy zdrojového kódu. V Azure použijte místo toho nastavení aplikace.

Pro vlastní obslužné rutiny nastavte FUNCTIONS_WORKER_RUNTIME na Custom v souboru local.settings.json.

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

Metadata funkce

Při použití s vlastní obslužnou rutinou se obsah function.json neliší od toho, jak byste definovali funkci v jakémkoli jiném kontextu. Jediným požadavkem je, aby soubory function.json byly ve složce s názvem, aby odpovídaly názvu funkce.

Následující function.json konfiguruje funkci, která má aktivační událost fronty a výstupní vazbu fronty. Protože je ve složce s názvem MyQueueFunction, definuje funkci s názvem 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"
    }
  ]
}

Datová část žádosti

Při přijetí zprávy fronty odešle hostitel služby Functions vlastní obslužné rutině požadavek HTTP post s datovou částí v těle.

Následující kód představuje ukázkovou datovou část požadavku. Datová část obsahuje strukturu JSON se dvěma členy: Data a Metadata.

Člen Data obsahuje klíče, které odpovídají názvům vstupních a aktivačních událostí definovaných v poli vazeb v souboru function.json .

Člen Metadata zahrnuje metadata vygenerovaná ze zdroje událostí.

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

Datová část odpovědi

Podle konvence jsou odpovědi funkcí formátovány jako páry klíč/hodnota. Mezi podporované klíče patří:

Klíč datové části Datový typ Poznámky
Outputs object Obsahuje hodnoty odpovědi definované polem bindings v souboru function.json.

Pokud je například funkce nakonfigurovaná s výstupní vazbou fronty myQueueOutput, Outputs obsahuje klíč s názvem myQueueOutput, který je nastaven vlastní obslužnou rutinou na zprávy odeslané do fronty.
Logs array Zprávy se zobrazují v protokolech volání funkcí.

Při spuštění v Azure se zprávy zobrazují v Application Insights.
ReturnValue řetězec Slouží k poskytnutí odpovědi, když je výstup nakonfigurovaný jako $return v souboru function.json .

Toto je příklad datové části odpovědi.

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

Příklady

Vlastní obslužné rutiny je možné implementovat v libovolném jazyce, který podporuje příjem událostí HTTP. Následující příklady ukazují, jak implementovat vlastní obslužnou rutinu pomocí programovacího jazyka Go.

Funkce s vazbami

Scénář implementovaný v tomto příkladu obsahuje funkci s názvem order , která přijímá POST objekt s datovou částí představující objednávku produktu. Při odeslání objednávky do funkce se vytvoří zpráva Queue Storage a vrátí se odpověď HTTP.

Implementace

Ve složce s názvem order soubor function.json konfiguruje funkci aktivovanou protokolem HTTP.

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

Tato funkce je definována jako funkce aktivovaná protokolem HTTP, která vrací odpověď HTTP a vypíše zprávu Queue Storage .

V kořenovém adresáři aplikace je soubor host.json nakonfigurovaný tak, aby se spustil spustitelný soubor s názvem handler.exe (handler v Linuxu nebo macOS).

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

Toto je požadavek HTTP odeslaný do modulu runtime Functions.

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

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

Modul runtime functions pak odešle do vlastní obslužné rutiny následující požadavek HTTP:

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

Poznámka

Některé části datové části byly kvůli stručnosti odebrány.

handler.exe je zkompilovaný program vlastní obslužné rutiny Go, který spouští webový server a reaguje na žádosti o vyvolání funkce z hostitele functions.

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

V tomto příkladu vlastní obslužná rutina spustí webový server pro zpracování událostí HTTP a je nastavená tak, aby naslouchala žádostem FUNCTIONS_CUSTOMHANDLER_PORTprostřednictvím .

I když hostitel functions přijal původní požadavek HTTP na /api/order, vyvolá vlastní obslužnou rutinu pomocí názvu funkce (název složky). V tomto příkladu je funkce definována v cestě ./order Hostitel odešle vlastní obslužné rutině požadavek HTTP na cestu ./order

Při POST odesílání požadavků do této funkce jsou data triggeru a metadata funkce k dispozici prostřednictvím textu požadavku HTTP. Původní text požadavku HTTP je přístupný v datové části Data.req.Body.

Odpověď funkce je formátovaná do párů klíč/hodnota, kde Outputs člen obsahuje hodnotu JSON, kde klíče odpovídají výstupům definovaným v souboru function.json .

Toto je příklad datové části, kterou tato obslužná rutina vrací hostiteli Functions.

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

Nastavením výstupu message na rovna datům pořadí, která přišla z požadavku, funkce vypíše data pořadí do nakonfigurované fronty. Hostitel služby Functions také vrátí volajícímu nakonfigurovanou res odpověď HTTP.

Pouze funkce HTTP

U funkcí aktivovaných protokolem HTTP bez dalších vazeb nebo výstupů můžete chtít, aby vaše obslužná rutina pracovala přímo s požadavkem a odpovědí HTTP místo vlastních datových částí požadavků a odpovědí obslužné rutiny. Toto chování je možné nakonfigurovat v souboru host.json pomocí enableForwardingHttpRequest nastavení .

Důležité

Primárním účelem funkce vlastních obslužných rutin je povolit jazyky a moduly runtime, které v současné době nemají prvotřídní podporu na Azure Functions. I když může být možné spouštět webové aplikace pomocí vlastních obslužných rutin, Azure Functions není standardní reverzní proxy server. Některé funkce, jako je streamování odpovědí, HTTP/2 a WebSocket, nejsou k dispozici. Některé komponenty požadavku HTTP, jako jsou určité hlavičky a trasy, mohou být omezeny. U vaší aplikace také může docházet k nadměrnému studenému spuštění.

Pokud chcete tyto okolnosti vyřešit, zvažte spuštění webových aplikací na Azure App Service.

Následující příklad ukazuje, jak nakonfigurovat funkci aktivovanou protokolem HTTP bez dalších vazeb nebo výstupů. Scénář implementovaný v tomto příkladu obsahuje funkci s názvem hello , která přijímá GET nebo POST .

Implementace

Ve složce s názvem hello nakonfiguruje soubor function.json funkci aktivovanou protokolem HTTP.

hello/function.json

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

Funkce je nakonfigurovaná tak, aby přijímala požadavky i GET a POST výsledná hodnota se poskytuje prostřednictvím argumentu s názvem res.

V kořenovém adresáři aplikace je soubor host.json nakonfigurovaný ke spuštění handler.exe a enableForwardingHttpRequest je nastavený na true.

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

Pokud enableForwardingHttpRequest je truenastavená hodnota , chování funkcí pouze protokolu HTTP se liší od výchozího chování vlastních obslužných rutin v těchto ohledech:

  • Požadavek HTTP neobsahuje datovou část vlastního požadavku obslužných rutin. Místo toho hostitel služby Functions vyvolá obslužnou rutinu s kopií původního požadavku HTTP.
  • Hostitel služby Functions vyvolá obslužnou rutinu se stejnou cestou jako původní požadavek, včetně všech parametrů řetězce dotazu.
  • Hostitel služby Functions vrátí kopii odpovědi HTTP obslužné rutiny jako odpověď na původní požadavek.

Následuje požadavek POST na hostitele služby Functions. Hostitel služby Functions pak odešle kopii požadavku vlastní obslužné rutině ve stejné cestě.

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

{
  "message": "Hello World!"
}

Soubor handler.go implementuje webový server a funkci HTTP.

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

V tomto příkladu vlastní obslužná rutina vytvoří webový server pro zpracování událostí HTTP a je nastavená tak, aby naslouchala požadavkům prostřednictvím FUNCTIONS_CUSTOMHANDLER_PORT.

GET Požadavky se zpracovávají vrácením řetězce a POST požadavky mají přístup k textu požadavku.

Trasa pro funkci order je /api/hellostejná jako původní požadavek.

Poznámka

Není FUNCTIONS_CUSTOMHANDLER_PORT veřejný port použitý k volání funkce . Tento port používá hostitel služby Functions k volání vlastní obslužné rutiny.

Nasazení

Vlastní obslužnou rutinu je možné nasadit do každé možnosti hostování Azure Functions. Pokud vaše obslužná rutina vyžaduje závislosti operačního systému nebo platformy (například modul runtime jazyka), možná budete muset použít vlastní kontejner.

Při vytváření aplikace funkcí v Azure pro vlastní obslužné rutiny doporučujeme jako zásobník vybrat .NET Core.

Pokud chcete nasadit vlastní aplikaci obslužné rutiny pomocí nástrojů Azure Functions Core Tools, spusťte následující příkaz.

func azure functionapp publish $functionAppName

Poznámka

Ujistěte se, že všechny soubory potřebné ke spuštění vlastní obslužné rutiny jsou ve složce a jsou součástí nasazení. Pokud je vaše vlastní obslužná rutina binární spustitelný soubor nebo má závislosti specifické pro platformu, ujistěte se, že tyto soubory odpovídají cílové platformě nasazení.

Omezení

  • Webový server vlastní obslužné rutiny se musí spustit do 60 sekund.

Ukázky

Příklady implementace funkcí v různých jazycích najdete v úložišti s ukázkami vlastních obslužných rutin na GitHubu .

Řešení potíží a podpora

Protokolování trasování

Pokud se proces vlastní obslužné rutiny nepodaří spustit nebo pokud má problémy s komunikací s hostitelem služby Functions, můžete zvýšit úroveň protokolu aplikace funkcí na , Trace abyste viděli další diagnostické zprávy z hostitele.

Pokud chcete změnit výchozí úroveň protokolu aplikace funkcí, nakonfigurujte logLevel nastavení v logging části souboru host.json.

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

Hostitel služby Functions vypíše další zprávy protokolu, včetně informací souvisejících s procesem vlastní obslužné rutiny. Protokoly použijte ke zkoumání problémů se spuštěním vlastního procesu obslužné rutiny nebo vyvoláním funkcí ve vlastní obslužné rutině.

Místně se protokoly vytisknou do konzoly.

V Azure se dotazem na trasování Application Insights zobrazí zprávy protokolu. Pokud vaše aplikace vytváří velký objem protokolů, odesílá se do Application Insights jenom podmnožina zpráv protokolu. Zakažte vzorkování , abyste měli jistotu, že se protokolují všechny zprávy.

Testování vlastní obslužné rutiny v izolaci

Aplikace vlastních obslužných rutin jsou procesem webového serveru, takže může být užitečné spustit ho samostatně a otestovat vyvolání funkce odesíláním napodobených požadavků HTTP pomocí nástroje, jako je cURL nebo Postman.

Tuto strategii můžete použít také v kanálech CI/CD ke spouštění automatizovaných testů na vlastní obslužné rutině.

Spouštěcí prostředí

Vlastní obslužné rutiny běží ve stejném prostředí jako typická Azure Functions aplikace. Otestujte obslužnou rutinu a ujistěte se, že prostředí obsahuje všechny závislosti, které potřebuje ke spuštění. U aplikací, které vyžadují další závislosti, může být potřeba je spustit pomocí vlastní image kontejneru hostované v plánu Azure Functions Premium.

Získání podpory

Pokud potřebujete pomoc s aplikací funkcí s vlastními obslužnými rutinami, můžete odeslat žádost prostřednictvím běžných kanálů podpory. Vzhledem k široké škále možných jazyků používaných k vytváření vlastních aplikací obslužných rutin však není podpora neomezená.

Podpora je k dispozici, pokud má hostitel služby Functions problémy se spuštěním nebo komunikací s procesem vlastní obslužné rutiny. V případě problémů specifických pro vnitřní fungování procesu vlastní obslužné rutiny, jako jsou problémy se zvoleným jazykem nebo architekturou, náš tým podpory nemůže v tomto kontextu poskytnout pomoc.

Další kroky

Začněte vytvářet Azure Functions aplikaci v Go nebo Rustu pomocí rychlého startu pro vlastní obslužné rutiny.