Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
La Azure Functions esegue il codice dell'app usando gestori specifici per il linguaggio. Questi gestori specifici del linguaggio consentono a Funzioni di supportare la maggior parte dei linguaggi chiave per impostazione predefinita. Tuttavia, potrebbe essere necessario eseguire il codice in un altro linguaggio o pacchetto.
I gestori personalizzati sono server Web leggeri che ricevono eventi dal processo dell'host di Funzioni di Azure. È possibile usare gestori personalizzati per la distribuzione in Funzioni di Azure in qualsiasi progetto di codice che supporta primitive HTTP.
I gestori personalizzati sono particolarmente indicati per le situazioni in cui si desidera:
- Implementare un'applicazione per funzioni in un linguaggio non supportato nativamente, ad esempio Go o Rust.
- Implementare un'app per le funzioni in un runtime attualmente non disponibile per impostazione predefinita, ad esempio Deno.
- Distribuire un server compilato con gli SDK standard MCP su Azure Functions.
Con i gestori personalizzati, è possibile usare trigger e binding di input e output tramite bundle di estensioni.
Introduzione ai gestori personalizzati di Funzioni di Azure con Attività iniziali in Go e Rust.
Panoramica
Il diagramma seguente illustra la relazione tra l'host di Funzioni e un server Web implementato come gestore personalizzato.
- Ogni evento attiva una richiesta inviata all'host di Funzioni. Un evento è qualsiasi trigger supportato da Funzioni di Azure.
- L'host di Funzioni invia quindi un payload di richiesta al server Web. Il payload contiene i dati di trigger e di binding di input e altri metadati della funzione.
- Il server Web esegue la singola funzione e restituisce un payload di risposta all'host di Funzioni.
- L'host di Funzioni passa i dati della risposta ai binding di output della funzione per consentire l'elaborazione.
Un'app Funzioni di Azure implementata come gestore personalizzato deve configurare i file host.json, local.settings.json e function.json in base ad alcune convenzioni.
Implementare server MCP autogestiti
I gestori personalizzati consentono anche di ospitare server MCP compilati usando gli SDK MCP ufficiali in Funzioni di Azure. I gestori personalizzati offrono un'esperienza semplice e semplificata per l'hosting dei server MCP in Azure. Per ulteriori informazioni, vedere Server MCP remoto auto-ospitato in Funzioni Azure.
Note
La possibilità di ospitare server MCP utilizzando le Funzioni di Azure con gli SDK MCP ufficiali è attualmente in versione di anteprima.
Struttura dell'applicazione
Per implementare un gestore personalizzato, l'applicazione richiede gli aspetti seguenti:
- Un file host.json nella radice dell'app
- Un file local.settings.json nella radice dell'app
- Un file function.json per ogni funzione (all'interno di una cartella che corrisponde al nome della funzione)
- Comando, script o eseguibile che esegue un server Web
Il diagramma seguente mostra l'aspetto di questi file nel file system per una funzione denominata "MyQueueFunction" e un eseguibile del gestore personalizzato denominato handler.exe.
| /MyQueueFunction
| function.json
|
| host.json
| local.settings.json
| handler.exe
Configurazione
L'applicazione viene configurata tramite i filehost.json e local.settings.json .
host.json
host.json indirizza l'host delle funzioni su dove inviare le richieste puntando a un server Web in grado di elaborare eventi HTTP.
Definire un gestore personalizzato configurando il file host.json con informazioni dettagliate su come eseguire il server Web tramite la customHandler sezione .
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "handler.exe"
}
}
}
La sezione customHandler punta a una destinazione come definito da defaultExecutablePath. La destinazione di esecuzione può essere un comando, un file eseguibile o un file in cui è implementato il server Web.
Usare la matrice arguments per passare qualsiasi argomento all'eseguibile. Gli argomenti supportano l'espansione delle variabili di ambiente (impostazioni dell'applicazione) usando %% la notazione.
È inoltre possibile modificare la directory di lavoro usata dall'eseguibile con workingDirectory.
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "app/handler.exe",
"arguments": [
"--database-connection-string",
"%DATABASE_CONNECTION_STRING%"
],
"workingDirectory": "app"
}
}
}
Supporto per i binding
I trigger standard insieme ai binding di input e output sono disponibili facendo riferimento ai bundle di estensioni nel file host.json.
local.settings.json
local.settings.json definisce le impostazioni dell'applicazione usate durante l'esecuzione dell'app per le funzioni in locale. Poiché potrebbe contenere segreti, escludere local.settings.json dal controllo del codice sorgente. In Azure usare invece le impostazioni dell'applicazione.
Per i gestori personalizzati, impostare FUNCTIONS_WORKER_RUNTIME su Custom in local.settings.json.
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "Custom"
}
}
Metadati della funzione
Quando si usa un gestore personalizzato, il contenuto delfunction.json è uguale a quando si definisce una funzione in qualsiasi altro contesto. L'unico requisito è che è necessario inserire function.json file in una cartella denominata in modo che corrisponda al nome della funzione.
Il function.json seguente configura una funzione con un trigger della coda e un binding di output della coda. Poiché si trova in una cartella denominata MyQueueFunction, definisce una funzione denominata 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"
}
]
}
Payload della richiesta
Quando l'host di Funzioni riceve un messaggio di coda, invia una richiesta post HTTP al gestore personalizzato con un payload nel corpo.
Il codice seguente mostra un payload di richiesta di esempio. Il payload include una struttura JSON con due membri: Data e Metadata.
Il membro Data include chiavi che corrispondono ai nomi di input e trigger definiti nella matrice di binding nel file function.json.
Il membro Metadata include i metadati generati dall'origine evento.
{
"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"
}
}
}
Payload della risposta
Per convenzione, le risposte delle funzioni vengono formattate come coppie chiave/valore. Le chiavi supportate includono:
|
|
Tipo di dati | Osservazioni |
|---|---|---|
Outputs |
object | Contiene i valori di risposta definiti dalla matrice bindings in function.json.Ad esempio, se una funzione è configurata con un'associazione di output della coda denominata "myQueueOutput", Outputs contiene una chiave denominata myQueueOutput, che il gestore personalizzato imposta sui messaggi inviati alla coda. |
Logs |
array | Messaggi visualizzati nei log chiamate di Funzioni. Durante l'esecuzione in Azure, i messaggi vengono visualizzati in Application Insights. |
ReturnValue |
string | Viene usato per fornire una risposta quando un output è configurato come $return nel file function.json. |
Questa tabella mostra un esempio di payload di risposta.
{
"Outputs": {
"res": {
"body": "Message enqueued"
},
"myQueueOutput": [
"queue message 1",
"queue message 2"
]
},
"Logs": [
"Log message 1",
"Log message 2"
],
"ReturnValue": "{\"hello\":\"world\"}"
}
Esempi
È possibile implementare gestori personalizzati in qualsiasi linguaggio che supporti la ricezione di eventi HTTP. Gli esempi seguenti illustrano come implementare un gestore personalizzato usando il linguaggio di programmazione Go.
Funzione con binding
Questo esempio mostra una funzione denominata order che accetta una POST richiesta con un payload che rappresenta un ordine di prodotto. Quando si invia un ordine alla funzione, viene creato un messaggio di Queue Storage e viene restituita una risposta HTTP.
Implementazione
In una cartella denominata order, il file function.json configura la funzione attivata da 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"
}
]
}
Questa funzione è definita come funzione attivata da HTTP che restituisce una risposta HTTP e genera un messaggio di archiviazione code.
Nella radice dell'app, il file host.json è configurato per eseguire un file eseguibile denominato handler.exe (handler in Linux o macOS).
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "handler.exe"
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
Si tratta della richiesta HTTP inviata al runtime di Funzioni.
POST http://127.0.0.1:7071/api/order HTTP/1.1
Content-Type: application/json
{
"id": 1005,
"quantity": 2,
"color": "black"
}
Il runtime di Funzioni invia la richiesta HTTP seguente al gestore personalizzato:
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": {
}
}
Note
Alcune parti del payload sono state rimosse per brevità.
handler.exe è il programma del gestore personalizzato Go compilato che esegue un server Web e risponde alle richieste di chiamata funzione dell'host di Funzioni.
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 questo esempio, il gestore personalizzato esegue un server Web per gestire gli eventi HTTP e ascolta le richieste tramite il componente specifico FUNCTIONS_CUSTOMHANDLER_PORT.
Anche se l'host delle Funzioni riceve la richiesta HTTP originale in /api/order, attiva il gestore personalizzato utilizzando il nome della funzione (il suo nome della cartella). In questo esempio la funzione viene definita nel percorso di /order. L'host invia al gestore personalizzato una richiesta HTTP nel percorso di /order.
Quando si inviano POST richieste a questa funzione, i dati del trigger e i metadati della funzione sono disponibili tramite il corpo della richiesta HTTP. È possibile accedere al corpo della richiesta HTTP originale nel payload.Data.req.Body
La risposta della funzione viene formattata in coppie chiave/valore in cui il membro Outputs contiene un valore JSON dove le chiavi corrispondono agli output come definito nel file function.json.
Si tratta di un esempio di payload restituito da questo gestore all'host di Funzioni.
{
"Outputs": {
"message": "{\"id\":1005,\"quantity\":2,\"color\":\"black\"}",
"res": {
"body": "Order enqueued"
}
},
"Logs": null,
"ReturnValue": null
}
Impostando l'output message uguale ai dati dell'ordine provenienti dalla richiesta, la funzione restituisce tali dati dell'ordine alla coda configurata. L'host di Funzioni restituisce al chiamante anche la risposta HTTP configurata in res.
Funzione solo HTTP
Per le funzioni attivate da HTTP senza associazioni o output aggiuntivi, è possibile che il gestore funzioni direttamente con la richiesta e la risposta HTTP anziché con i payload di richiesta e risposta del gestore personalizzato. È possibile configurare questo comportamento in host.json usando l'impostazione enableProxyingHttpRequest , che supporta lo streaming delle risposte.
Importante
Lo scopo principale della funzionalità dei gestori personalizzati è abilitare linguaggi e runtime che attualmente non dispongono del supporto di prima classe in Funzioni di Azure. Anche se è possibile eseguire applicazioni Web usando gestori personalizzati, Funzioni di Azure non è un proxy inverso standard. Alcuni componenti della richiesta HTTP, ad esempio alcune intestazioni e route, potrebbero essere limitati. L'applicazione potrebbe anche riscontrare un avvio a freddo eccessivo.
Per far fronte a queste circostanze, valutare l'esecuzione delle app Web nel servizio app di Azure.
L'esempio seguente illustra come configurare una funzione attivata da HTTP senza binding o output aggiuntivi. Lo scenario implementato in questo esempio include una funzione denominata hello che accetta un GET o POST.
Implementazione
In una cartella denominata hello, il file function.json configura la funzione attivata da HTTP.
hello/function.json
{
"bindings": [
{
"type": "httpTrigger",
"authLevel": "anonymous",
"direction": "in",
"name": "req",
"methods": ["get", "post"]
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
La funzione è configurata per accettare le GET richieste e POST e il valore del risultato viene fornito tramite un argomento denominato res.
Nella radice dell'app, il file host.json è configurato per eseguire handler.exe e enableProxyingHttpRequest è impostato su true.
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "handler.exe"
},
"enableProxyingHttpRequest": true
}
}
Di seguito è riportata una richiesta POST all'host funzioni. L'host delle funzioni invia quindi la richiesta al gestore personalizzato.
POST http://127.0.0.1:7071/api/hello HTTP/1.1
Content-Type: application/json
{
"message": "Hello World!"
}
Il file handler.go implementa un server Web e una funzione 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))
}
In questo esempio, il gestore personalizzato crea un server Web per gestire gli eventi HTTP e rimane in ascolto delle richieste tramite il FUNCTIONS_CUSTOMHANDLER_PORT.
le richieste GET vengono gestite restituendo una stringa e le richieste POST hanno accesso al corpo della richiesta.
La route per la funzione order è /api/hello, uguale alla richiesta originale.
Note
FUNCTIONS_CUSTOMHANDLER_PORT non è la porta pubblica usata per chiamare la funzione. L'host delle funzioni usa questa porta per chiamare il gestore personalizzato.
Distribuzione
È possibile distribuire un gestore personalizzato in ogni opzione di hosting di Funzioni di Azure. Se il gestore richiede dipendenze del sistema operativo o della piattaforma ,ad esempio un runtime di linguaggio, potrebbe essere necessario usare un contenitore personalizzato.
Quando si crea un'app per le funzioni in Azure per gestori personalizzati, selezionare .NET Core come stack.
Per distribuire un'app del gestore personalizzata usando Azure Functions Core Tools, eseguire il comando seguente.
func azure functionapp publish $functionAppName
Note
Assicurarsi che tutti i file necessari per eseguire il gestore personalizzato si trovino nella cartella e siano inclusi nella distribuzione. Se il gestore personalizzato è un eseguibile binario o ha dipendenze specifiche della piattaforma, assicurarsi che questi file corrispondano alla piattaforma di distribuzione di destinazione.
Restrizioni
- Il server Web del gestore personalizzato deve essere avviato entro 60 secondi.
Esempi
Per esempi di come implementare funzioni in un'ampia gamma di linguaggi diversi, vedere il repository GitHub di esempi di gestori personalizzati.
Risoluzione dei problemi e supporto
Registrazione della traccia
Se il processo del gestore personalizzato non riesce ad avviarsi o ha problemi di comunicazione con l'host di Funzioni, aumentare il livello di log dell'app per le funzioni fino a Trace per visualizzare più messaggi diagnostici da parte dell'host.
Per modificare il livello di log predefinito dell'app per le funzioni, configurare l'impostazione logLevel nella sezione logging di host.json.
{
"version": "2.0",
"customHandler": {
"description": {
"defaultExecutablePath": "handler.exe"
}
},
"logging": {
"logLevel": {
"default": "Trace"
}
}
}
L'host delle funzioni restituisce messaggi di log aggiuntivi, incluse le informazioni correlate al gestore personalizzato. Usare i log per analizzare i problemi durante l'avvio del processo del gestore personalizzato o durante la chiamata delle funzioni nel gestore personalizzato.
In locale, i log vengono stampati nella console.
In Azure, eseguire query sulle tracce di Application Insights per visualizzare i messaggi di log. Se l'app produce un volume elevato di log, ad Application Insights viene inviato un solo sottoinsieme di messaggi di log. Disabilitare il campionamento per assicurarsi che tutti i messaggi vengano registrati.
Testare il gestore personalizzato in isolamento
Le app del gestore personalizzate sono processi del server Web, quindi potrebbe essere utile avviarli autonomamente e testare le chiamate di funzione inviando richieste HTTP fittizie. Per l'invio di richieste HTTP con payload, assicurarsi di scegliere uno strumento che consente di proteggere i dati. Per altre informazioni, vedere strumenti di test HTTP.
È inoltre possibile usare questa strategia nelle pipeline CI/CD per eseguire test automatizzati nel gestore personalizzato.
Ambiente di esecuzione
I gestori personalizzati vengono eseguiti nello stesso ambiente di un'app Funzioni di Azure tipica. Testare il gestore per assicurarsi che l'ambiente contenga tutte le dipendenze necessarie per l'esecuzione. Per le app che richiedono dipendenze aggiuntive, potrebbe essere necessario eseguirle usando un'immagine del contenitore personalizzata ospitata nel piano Premium di Funzioni di Azure.
Supporto
Se è necessaria assistenza su un'app per le funzioni con gestori personalizzati, è possibile inviare una richiesta tramite i normali canali di supporto. Tuttavia, a causa dell'ampia gamma di linguaggi possibili usati per creare app di gestori personalizzati, il supporto non è illimitato.
Il supporto è disponibile se l'host di Funzioni presenta problemi durante l'avvio o la comunicazione con il processo del gestore personalizzato. Per problemi specifici dei lavori interni del processo del gestore personalizzato, ad esempio problemi con il linguaggio o il framework scelto, il team di supporto non può fornire assistenza in questo contesto.
Passaggi successivi
Informazioni di base sulla creazione di un'app Funzioni di Azure in Go o Rust con l'avvio rapido dei gestori personalizzati.