Udostępnij za pośrednictwem


Niestandardowe programy obsługi w usłudze Azure Functions

Każda aplikacja usługi Functions jest wykonywana przez program obsługi specyficzny dla języka. Chociaż usługa Azure Functions domyślnie obsługuje wiele języków , istnieją przypadki, w których można użyć innych języków lub środowisk uruchomieniowych.

Niestandardowe programy obsługi to lekkie serwery internetowe, które odbierają zdarzenia z hosta usługi Functions. Każdy język, który obsługuje typy pierwotne HTTP, może zaimplementować niestandardową procedurę obsługi.

Niestandardowe programy obsługi najlepiej sprawdzają się w sytuacjach, w których chcesz:

  • Zaimplementuj aplikację funkcji w języku, który nie jest obecnie oferowany poza polem, takim jak Go lub Rust.
  • Zaimplementuj aplikację funkcji w środowisku uruchomieniowym, które nie jest obecnie polecane domyślnie, na przykład Deno.

Za pomocą niestandardowych procedur obsługi można używać wyzwalaczy oraz powiązań wejściowych i wyjściowych za pośrednictwem pakietów rozszerzeń.

Rozpocznij pracę z niestandardowymi procedurami obsługi usługi Azure Functions, korzystając z przewodników Szybki start w języku Go i Rust.

Omówienie

Na poniższym diagramie przedstawiono relację między hostem usługi Functions i serwerem internetowym zaimplementowanym jako procedura obsługi niestandardowej.

Omówienie niestandardowej procedury obsługi usługi Azure Functions

  1. Każde zdarzenie wyzwala żądanie wysyłane do hosta usługi Functions. Zdarzenie to dowolny wyzwalacz obsługiwany przez usługę Azure Functions.
  2. Następnie host usługi Functions wystawia ładunek żądania na serwerze internetowym. Ładunek zawiera dane wyzwalacza i powiązania wejściowego oraz inne metadane dla funkcji.
  3. Serwer internetowy wykonuje pojedynczą funkcję i zwraca ładunek odpowiedzi do hosta usługi Functions.
  4. Host usługi Functions przekazuje dane z odpowiedzi na powiązania wyjściowe funkcji do przetwarzania.

Aplikacja usługi Azure Functions zaimplementowana jako niestandardowa procedura obsługi musi skonfigurować pliki host.json, local.settings.json i function.json zgodnie z kilkoma konwencjami.

Struktura aplikacji

Aby zaimplementować niestandardową procedurę obsługi, potrzebne są następujące aspekty aplikacji:

  • Plik host.json w katalogu głównym aplikacji
  • Plik local.settings.json w katalogu głównym aplikacji
  • Plik function.json dla każdej funkcji (wewnątrz folderu zgodnego z nazwą funkcji)
  • Polecenie, skrypt lub plik wykonywalny, który uruchamia serwer internetowy

Na poniższym diagramie pokazano, jak te pliki wyglądają w systemie plików dla funkcji o nazwie "MyQueueFunction" i pliku wykonywalnego niestandardowego programu obsługi o nazwie handler.exe.

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

Konfigurowanie

Aplikacja jest konfigurowana za pośrednictwem plików host.json i local.settings.json .

host.json

host.json informuje hosta usługi Functions, gdzie wysyłać żądania, wskazując serwer internetowy, który może przetwarzać zdarzenia HTTP.

Niestandardowa procedura obsługi jest definiowana przez skonfigurowanie pliku host.json ze szczegółowymi informacjami na temat uruchamiania serwera internetowego customHandler za pośrednictwem sekcji .

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

Sekcja customHandler wskazuje element docelowy zgodnie z definicją elementu defaultExecutablePath. Obiekt docelowy wykonywania może być poleceniem, plikiem wykonywalnym lub plikiem, w którym zaimplementowano serwer internetowy.

arguments Użyj tablicy, aby przekazać wszystkie argumenty do pliku wykonywalnego. Argumenty obsługują rozszerzanie zmiennych środowiskowych (ustawień aplikacji) przy użyciu %% notacji.

Możesz również zmienić katalog roboczy używany przez plik wykonywalny za pomocą polecenia workingDirectory.

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "app/handler.exe",
      "arguments": [
        "--database-connection-string",
        "%DATABASE_CONNECTION_STRING%"
      ],
      "workingDirectory": "app"
    }
  }
}
Obsługa powiązań

Wyzwalacze standardowe wraz z powiązaniami wejściowymi i wyjściowymi są dostępne przez odwoływanie się do pakietów rozszerzeń w pliku host.json .

local.settings.json

local.settings.json definiuje ustawienia aplikacji używane podczas lokalnego uruchamiania aplikacji funkcji. Ponieważ może zawierać wpisy tajne, local.settings.json należy wykluczyć z kontroli źródła. Na platformie Azure zamiast tego użyj ustawień aplikacji.

W przypadku niestandardowych procedur obsługi ustaw wartość FUNCTIONS_WORKER_RUNTIME Custom na w local.settings.json.

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

Metadane funkcji

W przypadku użycia z niestandardową procedurą obsługi zawartość function.json nie różni się od sposobu definiowania funkcji w innym kontekście. Jedynym wymaganiem jest to, że pliki function.json muszą znajdować się w folderze o nazwie, aby pasować do nazwy funkcji.

Poniższe function.json konfiguruje funkcję, która ma wyzwalacz kolejki i powiązanie wyjściowe kolejki. Ponieważ znajduje się on w folderze o nazwie MyQueueFunction, definiuje funkcję o nazwie 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"
    }
  ]
}

Ładunek żądania

Po odebraniu komunikatu w kolejce host usługi Functions wysyła żądanie POST HTTP do niestandardowego programu obsługi z ładunkiem w treści.

Poniższy kod reprezentuje przykładowy ładunek żądania. Ładunek zawiera strukturę JSON z dwoma elementami członkowskimi: Data i Metadata.

Element Data członkowski zawiera klucze zgodne z nazwami danych wejściowych i wyzwalaczy zgodnie z definicją w tablicy powiązań w pliku function.json .

Element Metadata członkowski zawiera metadane wygenerowane ze źródła zdarzeń.

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

Ładunek odpowiedzi

Zgodnie z konwencją odpowiedzi funkcji są formatowane jako pary klucz/wartość. Obsługiwane klucze obejmują:

Klucz ładunku Typ danych Uwagi
Outputs obiekt Przechowuje wartości odpowiedzi zdefiniowane przez tablicę bindings w function.json.

Jeśli na przykład funkcja jest skonfigurowana za pomocą powiązania wyjściowego kolejki o nazwie "myQueueOutput", Outputs zawiera klucz o nazwie myQueueOutput, który jest ustawiany przez program obsługi niestandardowej na komunikaty wysyłane do kolejki.
Logs tablica Komunikaty są wyświetlane w dziennikach wywołania usługi Functions.

Podczas uruchamiania na platformie Azure komunikaty są wyświetlane w usłudze Application Insights.
ReturnValue string Służy do dostarczania odpowiedzi, gdy dane wyjściowe są skonfigurowane tak jak $return w pliku function.json .

Jest to przykład ładunku odpowiedzi.

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

Przykłady

Niestandardowe programy obsługi można zaimplementować w dowolnym języku, który obsługuje odbieranie zdarzeń HTTP. W poniższych przykładach pokazano, jak zaimplementować niestandardową procedurę obsługi przy użyciu języka programowania Go.

Funkcja z powiązaniami

Scenariusz zaimplementowany w tym przykładzie zawiera funkcję o nazwie order , która akceptuje POST ładunek reprezentujący zamówienie produktu. Gdy zamówienie jest publikowane w funkcji, jest tworzony komunikat usługi Queue Storage i zwracana jest odpowiedź HTTP.

Implementacja

W folderze o nazwie order plik function.json konfiguruje funkcję wyzwalaną przez protokół 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"
    }
  ]
}

Ta funkcja jest zdefiniowana jako funkcja wyzwalana przez protokół HTTP, która zwraca odpowiedź HTTP i zwraca komunikat usługi Queue Storage.

W katalogu głównym aplikacji plik host.json jest skonfigurowany do uruchamiania pliku wykonywalnego o nazwie handler.exe (handler w systemie Linux lub macOS).

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

Jest to żądanie HTTP wysyłane do środowiska uruchomieniowego usługi Functions.

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

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

Następnie środowisko uruchomieniowe usługi Functions wyśle następujące żądanie HTTP do niestandardowego programu obsługi:

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

Uwaga

Niektóre części ładunku zostały usunięte w celu zwięzłości.

handler.exe to skompilowany niestandardowy program obsługi języka Go, który uruchamia serwer internetowy i odpowiada na żądania wywołania funkcji z hosta usługi 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))
}

W tym przykładzie program obsługi niestandardowej uruchamia serwer internetowy do obsługi zdarzeń HTTP i jest ustawiony na nasłuchiwanie żądań za pośrednictwem .FUNCTIONS_CUSTOMHANDLER_PORT

Mimo że host usługi Functions otrzymał oryginalne żądanie HTTP pod /api/orderadresem , wywołuje niestandardową procedurę obsługi przy użyciu nazwy funkcji (jej nazwy folderu). W tym przykładzie funkcja jest definiowana w ścieżce ./order Host wysyła niestandardowy program obsługi żądania HTTP w ścieżce /order.

W miarę POST wysyłania żądań do tej funkcji dane wyzwalacza i metadane funkcji są dostępne za pośrednictwem treści żądania HTTP. Dostęp do oryginalnej treści żądania HTTP można uzyskać w ładunku Data.req.Body.

Odpowiedź funkcji jest formatowana w pary klucz/wartość, w których element członkowski przechowuje wartość JSON, w której Outputs klucze są zgodne z danymi wyjściowymi zdefiniowanymi w pliku function.json .

Jest to przykładowy ładunek, który ten program obsługi powraca do hosta usługi Functions.

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

message Ustawiając dane wyjściowe równe danych zamówienia, które pochodzą z żądania, funkcja generuje dane, które nakazują dane do skonfigurowanej kolejki. Host usługi Functions zwraca również odpowiedź HTTP skonfigurowaną w res obiekcie wywołującym.

Funkcja tylko http

W przypadku funkcji wyzwalanych przez protokół HTTP bez dodatkowych powiązań lub danych wyjściowych program obsługi może pracować bezpośrednio z żądaniem HTTP i odpowiedzią zamiast niestandardowych ładunków żądań i odpowiedzi programu obsługi. To zachowanie można skonfigurować w host.json przy użyciu enableForwardingHttpRequest ustawienia .

Ważne

Podstawowym celem funkcji niestandardowych procedur obsługi jest włączenie języków i środowisk uruchomieniowych, które nie mają obecnie najwyższej jakości obsługi w usłudze Azure Functions. Chociaż może być możliwe uruchamianie aplikacji internetowych przy użyciu niestandardowych procedur obsługi, usługa Azure Functions nie jest standardowym zwrotnym serwerem proxy. Niektóre funkcje, takie jak przesyłanie strumieniowe odpowiedzi, http/2 i zestawy WebSocket, nie są dostępne. Niektóre składniki żądania HTTP, takie jak niektóre nagłówki i trasy, mogą być ograniczone. Aplikacja może również napotkać nadmierne zimne uruchomienie.

Aby rozwiązać te okoliczności, rozważ uruchomienie aplikacji internetowych w usłudze aplikacja systemu Azure Service.

W poniższym przykładzie pokazano, jak skonfigurować funkcję wyzwalaną przez protokół HTTP bez dodatkowych powiązań ani danych wyjściowych. Scenariusz zaimplementowany w tym przykładzie zawiera funkcję o nazwie hello , która akceptuje GET element lub POST .

Implementacja

W folderze o nazwie hello plik function.json konfiguruje funkcję wyzwalaną przez protokół HTTP.

hello/function.json

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

Funkcja jest skonfigurowana do akceptowania zarówno żądań, jak GET i POST , a wartość wyniku jest dostarczana za pośrednictwem argumentu o nazwie res.

W katalogu głównym aplikacji plik host.json jest skonfigurowany do uruchamiania handler.exe i enableForwardingHttpRequest jest ustawiony na truewartość .

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

Gdy enableForwardingHttpRequest parametr to true, zachowanie funkcji tylko http różni się od domyślnego zachowania niestandardowych procedur obsługi w następujące sposoby:

  • Żądanie HTTP nie zawiera niestandardowego ładunku żądania obsługi. Zamiast tego host usługi Functions wywołuje procedurę obsługi z kopią oryginalnego żądania HTTP.
  • Host usługi Functions wywołuje procedurę obsługi z tą samą ścieżką co oryginalne żądanie, w tym wszystkie parametry ciągu zapytania.
  • Host usługi Functions zwraca kopię odpowiedzi HTTP programu obsługi jako odpowiedź na oryginalne żądanie.

Poniżej znajduje się żądanie POST do hosta usługi Functions. Następnie host usługi Functions wysyła kopię żądania do niestandardowej procedury obsługi w tej samej ścieżce.

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

{
  "message": "Hello World!"
}

Plik handler.go implementuje serwer internetowy i funkcję 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))
}

W tym przykładzie program obsługi niestandardowej tworzy serwer internetowy do obsługi zdarzeń HTTP i jest ustawiony na nasłuchiwanie żądań za pośrednictwem .FUNCTIONS_CUSTOMHANDLER_PORT

GET żądania są obsługiwane przez zwrócenie ciągu, a POST żądania mają dostęp do treści żądania.

Trasa dla funkcji order w tym miejscu jest /api/hellotaka sama jak oryginalne żądanie.

Uwaga

Nie FUNCTIONS_CUSTOMHANDLER_PORT jest publicznym portem używanym do wywoływania funkcji. Ten port jest używany przez hosta usługi Functions do wywoływania niestandardowej procedury obsługi.

Wdrażanie

Program obsługi niestandardowej można wdrożyć w każdej opcji hostingu usługi Azure Functions. Jeśli program obsługi wymaga zależności systemu operacyjnego lub platformy (na przykład środowiska uruchomieniowego języka), może być konieczne użycie niestandardowego kontenera.

Podczas tworzenia aplikacji funkcji na platformie Azure dla niestandardowych procedur obsługi zalecamy wybranie platformy .NET Core jako stosu.

Aby wdrożyć niestandardową aplikację obsługi przy użyciu narzędzi Azure Functions Core Tools, uruchom następujące polecenie.

func azure functionapp publish $functionAppName

Uwaga

Upewnij się, że wszystkie pliki wymagane do uruchomienia niestandardowej procedury obsługi znajdują się w folderze i uwzględnione we wdrożeniu. Jeśli niestandardowa procedura obsługi jest binarnym plikiem wykonywalnym lub ma zależności specyficzne dla platformy, upewnij się, że te pliki są zgodne z docelową platformą wdrażania.

Ograniczenia

  • Niestandardowy serwer sieci Web programu obsługi musi być uruchomiony w ciągu 60 sekund.

Przykłady

Zapoznaj się z repozytorium GitHub z niestandardowymi przykładami obsługi, aby zapoznać się z przykładami implementacji funkcji w różnych językach.

Rozwiązywanie problemów i pomoc techniczna

Rejestrowanie śledzenia

Jeśli nie można uruchomić niestandardowego procesu obsługi lub jeśli ma problemy z komunikacją z hostem usługi Functions, możesz zwiększyć poziom dziennika aplikacji funkcji, aby wyświetlić Trace więcej komunikatów diagnostycznych z hosta.

Aby zmienić domyślny poziom dziennika aplikacji funkcji, skonfiguruj logLevel ustawienie w logging sekcji host.json.

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

Host usługi Functions generuje dodatkowe komunikaty dziennika, w tym informacje związane z niestandardowym procesem obsługi. Użyj dzienników, aby zbadać problemy podczas uruchamiania niestandardowego procesu obsługi lub wywoływania funkcji w niestandardowej procedurze obsługi.

Lokalnie dzienniki są drukowane w konsoli programu .

Na platformie Azure wykonaj zapytanie o ślady usługi Application Insights, aby wyświetlić komunikaty dziennika. Jeśli aplikacja generuje dużą liczbę dzienników, do usługi Application Insights są wysyłane tylko podzbiór komunikatów dziennika. Wyłącz próbkowanie , aby upewnić się, że wszystkie komunikaty są rejestrowane.

Testowanie niestandardowej procedury obsługi w izolacji

Niestandardowe aplikacje obsługi to proces serwera internetowego, dlatego warto uruchomić je samodzielnie i przetestować wywołania funkcji przez wysyłanie pozornych żądań HTTP. W przypadku wysyłania żądań HTTP z ładunkami upewnij się, że wybrano narzędzie, które zapewnia bezpieczeństwo danych. Aby uzyskać więcej informacji, zobacz Narzędzia do testowania HTTP.

Tę strategię można również użyć w potokach ciągłej integracji/ciągłego wdrażania do uruchamiania testów automatycznych w niestandardowej procedurze obsługi.

Środowisko wykonywania

Niestandardowe programy obsługi działają w tym samym środowisku co typowa aplikacja usługi Azure Functions. Przetestuj program obsługi, aby upewnić się, że środowisko zawiera wszystkie zależności, które należy uruchomić. W przypadku aplikacji, które wymagają dodatkowych zależności, może być konieczne ich uruchomienie przy użyciu niestandardowego obrazu kontenera hostowanego w planie Premium usługi Azure Functions.

Uzyskiwanie pomocy technicznej

Jeśli potrzebujesz pomocy w aplikacji funkcji z niestandardowymi procedurami obsługi, możesz przesłać żądanie za pośrednictwem zwykłych kanałów pomocy technicznej. Jednak ze względu na szeroką gamę możliwych języków używanych do tworzenia niestandardowych aplikacji obsługi nie jest nieograniczona.

Obsługa jest dostępna, jeśli host usługi Functions ma problemy z uruchamianiem lub komunikowaniem się z niestandardowym procesem obsługi. W przypadku problemów specyficznych dla wewnętrznego działania niestandardowego procesu obsługi, takich jak problemy z wybranym językiem lub strukturą, nasz zespół pomocy technicznej nie może udzielić pomocy w tym kontekście.

Następne kroki

Rozpocznij tworzenie aplikacji usługi Azure Functions w języku Go lub Rust, korzystając z niestandardowego przewodnika Szybki start dla procedur obsługi.