Egyéni kezelők az Azure Functionshöz

Minden Functions-alkalmazást egy nyelvspecifikus kezelő hajt végre. Bár Azure Functions alapértelmezés szerint számos nyelvkezelőt tartalmaz, előfordulhat, hogy más nyelveket vagy futtatókörnyezeteket szeretne használni.

Az egyéni kezelők egyszerű webkiszolgálók, amelyek eseményeket fogadnak a Functions-gazdagéptől. A HTTP-primitíveket támogató bármely nyelv implementálhat egyéni kezelőt.

Az egyéni kezelők az alábbi helyzetekben a legmegfelelőbbek:

  • Függvényalkalmazás implementálása olyan nyelven, amely jelenleg nem érhető el, például Go vagy Rust.
  • Implementáljon egy függvényalkalmazást egy olyan futtatókörnyezetben, amely jelenleg nincs alapértelmezés szerint kiemelten, például Deno.

Az egyéni kezelők segítségével triggereket, valamint bemeneti és kimeneti kötéseket használhat bővítménykötegeken keresztül.

Első lépések Azure Functions egyéni kezelőkhöz a Go és a Rust rövid útmutatóival.

Áttekintés

Az alábbi ábrán a Functions-gazdagép és az egyéni kezelőként implementált webkiszolgáló közötti kapcsolat látható.

Azure Functions egyéni kezelő áttekintése

  1. Minden esemény elindít egy, a Functions-gazdagépnek küldött kérést. Az esemény bármilyen eseményindító, amelyet a Azure Functions támogat.
  2. A Functions-gazdagép ezután kiad egy hasznos kérést a webkiszolgálónak. A hasznos adat a függvény eseményindító- és bemeneti kötési adatait, valamint egyéb metaadatait tartalmazza.
  3. A webkiszolgáló végrehajtja az egyes függvényeket, és válasz hasznos adatokat ad vissza a Functions-gazdagépnek.
  4. A Functions-gazdagép adatokat ad át a válaszból a függvény kimeneti kötéseinek feldolgozás céljából.

Az egyéni kezelőként implementált Azure Functions-alkalmazásoknak néhány konvenció szerint konfigurálniuk kell a host.json, local.settings.json és function.json fájlokat.

Alkalmazásstruktúra

Egyéni kezelő implementálásához az alábbi szempontokra van szüksége az alkalmazáshoz:

  • Egy host.json fájl az alkalmazás gyökerében
  • Egy local.settings.json fájl az alkalmazás gyökerében
  • Egy function.json fájl minden függvényhez (a függvény nevével egyező mappában)
  • Webkiszolgálót futtató parancs, szkript vagy végrehajtható fájl

Az alábbi diagram bemutatja, hogyan néznek ki ezek a fájlok a fájlrendszerben a "MyQueueFunction" nevű függvényhez és egy handler.exenevű egyéni kezelő végrehajtható fájlhoz.

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

Konfiguráció

Az alkalmazás a host.json és a local.settings.json fájlon keresztül van konfigurálva.

host.json

A host.json a HTTP-események feldolgozására képes webkiszolgálóra mutatva közli a Functions-gazdagéppel, hogy hová kell küldeni a kéréseket.

Az egyéni kezelő a host.json fájl konfigurálásával határozható meg, amely részletesen bemutatja, hogyan futtathatja a webkiszolgálót a customHandler szakaszon keresztül.

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

A customHandler szakasz a által meghatározott defaultExecutablePathcélra mutat. A végrehajtási cél lehet egy parancs, végrehajtható fájl vagy fájl, amelyben a webkiszolgáló implementálva van.

arguments A tömb használatával bármilyen argumentumot átadhat a végrehajtható fájlnak. Az argumentumok támogatják a környezeti változók (alkalmazásbeállítások) bővítését jelöléssel %% .

A végrehajtható fájl által használt munkakönyvtárat a következővel workingDirectoryis módosíthatja: .

{
  "version": "2.0",
  "customHandler": {
    "description": {
      "defaultExecutablePath": "app/handler.exe",
      "arguments": [
        "--database-connection-string",
        "%DATABASE_CONNECTION_STRING%"
      ],
      "workingDirectory": "app"
    }
  }
}
Kötések támogatása

A standard triggerek, valamint a bemeneti és kimeneti kötések a host.json fájl bővítménycsomagjaira hivatkozva érhetők el.

local.settings.json

A local.settings.json a függvényalkalmazás helyi futtatásakor használt alkalmazásbeállításokat határozza meg. Mivel titkos kódokat tartalmazhat, a local.settings.json fájlt ki kell zárni a verziókövetésből. Az Azure-ban használja inkább az alkalmazásbeállításokat.

Egyéni kezelők esetén állítsa a értéket FUNCTIONS_WORKER_RUNTIME a Customlocal.settings.json fájlban.

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

Függvény metaadatai

Ha egyéni kezelővel használja, a function.json tartalma nem különbözik attól, ahogyan más kontextusban definiálna egy függvényt. Az egyetlen követelmény, hogy a function.json fájloknak a függvény nevével egyező nevű mappában kell lenniük.

Az alábbi function.json egy üzenetsor-eseményindítóval és egy üzenetsor kimeneti kötésével rendelkező függvényt konfigurál. Mivel egy MyQueueFunction nevű mappában található, definiál egy MyQueueFunction nevű függvényt.

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

Hasznos adat kérése

Üzenetsor érkezésekor a Functions-gazdagép http-post kérést küld az egyéni kezelőnek, amelynek hasznos adatai a törzsben találhatóak.

Az alábbi kód egy mintakérés hasznos adatait jelöli. A hasznos adat egy JSON-struktúrát tartalmaz, amelynek két tagja van: Data és Metadata.

A Data tag olyan kulcsokat tartalmaz, amelyek megfelelnek a function.json fájl bindings tömbjében meghatározott bemeneti és triggerneveknek.

A Metadata tag tartalmazza az eseményforrásból létrehozott metaadatokat.

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

Válasz hasznos adatai

A függvényválaszok konvenció szerint kulcs-érték párokként vannak formázva. A támogatott kulcsok a következők:

Hasznosadat-kulcs Adattípus Megjegyzések
Outputs object A function.json tömb által bindings meghatározott válaszértékeket tartalmazza.

Ha például egy függvény "myQueueOutput" nevű üzenetsorkimeneti kötéssel van konfigurálva, akkor Outputs egy nevű myQueueOutputkulcsot tartalmaz, amelyet az egyéni kezelő állít be az üzenetsorba küldött üzenetekre.
Logs array Az üzenetek megjelennek a Függvényhívási naplókban.

Az Azure-ban való futtatáskor az üzenetek megjelennek az Application Insightsban.
ReturnValue sztring Válasz megadására szolgál, ha a kimenet a function.json fájlban van konfigurálva$return.

Ez egy példa a válasz hasznos adataira.

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

Példák

Az egyéni kezelők bármely olyan nyelven implementálhatók, amely támogatja a HTTP-események fogadását. Az alábbi példák bemutatják, hogyan implementálhat egyéni kezelőket a Go programozási nyelv használatával.

Függvény kötésekkel

Az ebben a példában implementált forgatókönyv egy nevű order függvényt tartalmaz, amely egy termékrendelést jelképező hasznos adattal rendelkező függvényt fogad el POST . A függvény megrendelésének közzétételekor létrejön egy Queue Storage-üzenet, és a rendszer HTTP-választ ad vissza.

Implementálás

Az order nevű mappában a function.json fájl konfigurálja a HTTP által aktivált függvényt.

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

Ez a függvény http által aktivált függvényként van definiálva , amely HTTP-választ ad vissza, és üzenetsortár-üzenetet ad ki.

Az alkalmazás gyökerében a host.json fájl úgy van konfigurálva, hogy futtasson egy nevű végrehajtható fájlt handler.exe (handler Linux vagy macOS rendszeren).

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

Ez a Functions-futtatókörnyezetnek küldött HTTP-kérés.

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

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

A Functions-futtatókörnyezet ezután a következő HTTP-kérést küldi el az egyéni kezelőnek:

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

Megjegyzés

A hasznos adatok egyes részei el lettek távolítva a rövidség kedvéért.

handler.exe a lefordított Go egyéni kezelőprogram, amely webkiszolgálót futtat, és válaszol a Functions-gazdagép függvényhívási kérelmeire.

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

Ebben a példában az egyéni kezelő egy webkiszolgálót futtat a HTTP-események kezeléséhez, és úgy van beállítva, hogy a használatával figyelje a FUNCTIONS_CUSTOMHANDLER_PORTkéréseket.

Annak ellenére, hogy a Functions-gazdagép eredeti HTTP-kérést kapott a címen /api/order, meghívja az egyéni kezelőt a függvény nevével (mappaneve). Ebben a példában a függvény a elérési útjában van definiálva /order. A gazdagép http-kérést küld az egyéni kezelőnek a elérési útjára /order.

A POST függvénynek küldött kérések során az eseményindító adatai és a függvény metaadatai a HTTP-kérés törzsében érhetők el. Az eredeti HTTP-kérelemtörzs a hasznos adatokban Data.req.Bodyérhető el.

A függvény válasza kulcs/érték párokra van formázva, ahol a Outputs tag egy JSON-értéket tartalmaz, ahol a kulcsok megegyeznek a function.json fájlban meghatározott kimenetekkel.

Ez egy példa hasznos adatra, amelyet ez a kezelő visszatér a Functions-gazdagéphez.

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

Ha a message kimenetet a kérelemből kapott rendelési adatokkal egyenlőre állítja, a függvény az adatokat a konfigurált üzenetsorba rendezi. A Functions-gazdagép a hívónak konfigurált res HTTP-választ is visszaadja.

CSAK HTTP-függvény

A TOVÁBBI kötések és kimenetek nélküli HTTP-aktivált függvények esetében előfordulhat, hogy azt szeretné, hogy a kezelő közvetlenül a HTTP-kéréssel és -válaszokkal dolgozzon az egyéni kezelői kérések és válaszok hasznos adatai helyett. Ez a viselkedés a host.json fájlban konfigurálható a enableForwardingHttpRequest beállítással.

Fontos

Az egyéni kezelők funkció elsődleges célja, hogy engedélyezze azokat a nyelveket és futtatókörnyezeteket, amelyek jelenleg nem rendelkeznek első osztályú támogatással Azure Functions. Bár a webalkalmazások egyéni kezelőkkel is futtathatók, a Azure Functions nem szabványos fordított proxy. Egyes funkciók, például a válaszstreamelés, a HTTP/2 és a WebSocket nem érhetők el. A HTTP-kérés egyes összetevői, például bizonyos fejlécek és útvonalak korlátozottak lehetnek. Előfordulhat, hogy az alkalmazás túl hidegindítást is tapasztal.

Ezen körülmények megoldásához fontolja meg a webalkalmazások futtatását Azure App Service.

Az alábbi példa bemutatja, hogyan konfigurálhat EGY HTTP által aktivált függvényt további kötések és kimenetek nélkül. Az ebben a példában implementált forgatókönyv egy nevű hello függvényt tartalmaz, amely elfogadja a vagy POST a függvénytGET.

Implementálás

A hello nevű mappában a function.json fájl konfigurálja a HTTP által aktivált függvényt.

hello/function.json

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

A függvény úgy van konfigurálva, hogy fogadja a és POST a kéréseket isGET, és az eredményérték egy nevű resargumentumon keresztül van megadva.

Az alkalmazás gyökerében a host.json fájl úgy van konfigurálva, hogy futjon handler.exe , és enableForwardingHttpRequest a következőre truevan állítva: .

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

Ha enableForwardingHttpRequest az , truea csak HTTP-függvények viselkedése az alábbi módokon különbözik az alapértelmezett egyéni kezelők viselkedésétől:

  • A HTTP-kérelem nem tartalmazza az egyéni kezelők kérelem hasznos adatait. Ehelyett a Functions-gazdagép meghívja a kezelőt az eredeti HTTP-kérés másolatával.
  • A Functions-gazdagép az eredeti kéréssel megegyező elérési úttal hívja meg a kezelőt, beleértve a lekérdezési sztring paramétereit is.
  • A Functions-gazdagép visszaadja a kezelő HTTP-válaszának másolatát az eredeti kérésre adott válaszként.

Az alábbiakban egy POST-kérést küldünk a Functions-gazdagépnek. A Functions-gazdagép ezután elküldi a kérelem másolatát az egyéni kezelőnek ugyanazon az útvonalon.

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

{
  "message": "Hello World!"
}

A fájlkezelő.go fájl webkiszolgálót és HTTP-függvényt implementál.

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

Ebben a példában az egyéni kezelő létrehoz egy webkiszolgálót a HTTP-események kezeléséhez, és úgy van beállítva, hogy a használatával figyelje a FUNCTIONS_CUSTOMHANDLER_PORTkéréseket.

GET a kéréseket egy sztring visszaadásával kezeli a rendszer, a kérések pedig POST hozzáférhetnek a kérelem törzséhez.

A rendelési függvény útvonala /api/helloitt ugyanaz, mint az eredeti kérelem.

Megjegyzés

A FUNCTIONS_CUSTOMHANDLER_PORT nem a függvény meghívásához használt nyilvános port. Ezt a portot használja a Functions-gazdagép az egyéni kezelő meghívásához.

Telepítése

Az egyéni kezelők minden Azure Functions üzemeltetési lehetőséghez üzembe helyezhetők. Ha a kezelőnek operációs rendszer- vagy platformfüggőségekre (például nyelvi futtatókörnyezetre) van szüksége, előfordulhat, hogy egyéni tárolót kell használnia.

Amikor egyéni kezelők számára hoz létre függvényalkalmazást az Azure-ban, javasoljuk, hogy a .NET Core-t válassza veremként.

Ha egyéni kezelőalkalmazást szeretne üzembe helyezni Azure Functions Core Tools használatával, futtassa a következő parancsot.

func azure functionapp publish $functionAppName

Megjegyzés

Győződjön meg arról, hogy az egyéni kezelő futtatásához szükséges összes fájl megtalálható a mappában, és szerepel az üzembe helyezésben. Ha az egyéni kezelő bináris végrehajtható fájl, vagy platformspecifikus függőségekkel rendelkezik, győződjön meg arról, hogy ezek a fájlok megfelelnek a cél üzembehelyezési platformnak.

Korlátozások

  • Az egyéni kezelő webkiszolgálójának 60 másodpercen belül el kell indulnia.

Példák

Tekintse meg az egyéni kezelői mintákat a GitHub-adattárban , és példákat talál a függvények különböző nyelveken történő implementálására.

Hibaelhárítás és támogatás

Nyomkövetési naplózás

Ha az egyéni kezelőfolyamat nem indul el, vagy problémákat tapasztal a Functions-gazdagéppel való kommunikáció során, növelheti a függvényalkalmazás naplószintjét, hogy Trace további diagnosztikai üzeneteket jelenítsen meg a gazdagépről.

A függvényalkalmazás alapértelmezett naplószintjének módosításához konfigurálja a beállítást a logLevellogginghost.json szakaszban.

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

A Functions-gazdagép további naplóüzeneteket ad ki, beleértve az egyéni kezelőfolyamattal kapcsolatos információkat is. A naplók segítségével megvizsgálhatja az egyéni kezelőfolyamat elindításával vagy az egyéni kezelő függvényeinek meghívásával kapcsolatos problémákat.

A rendszer helyileg nyomtatja ki a naplókat a konzolra.

Az Azure-ban az Application Insights-nyomkövetések lekérdezése a naplóüzenetek megtekintéséhez. Ha az alkalmazás nagy mennyiségű naplót hoz létre, a rendszer csak a naplóüzenetek egy részét küldi el az Application Insightsnak. Tiltsa le a mintavételezést , hogy az összes üzenet naplózva legyen.

Egyéni kezelő tesztelése elkülönítésben

Az egyéni kezelőalkalmazások webkiszolgálói folyamatok, ezért hasznos lehet önállóan elindítani, és tesztelni a függvényhívásokat, ha HTTP-kéréseket küldenek egy olyan eszközzel, mint a cURL vagy a Postman.

Ezt a stratégiát a CI-/CD-folyamatokban is használhatja automatizált tesztek futtatásához az egyéni kezelőn.

Végrehajtási környezet

Az egyéni kezelők ugyanabban a környezetben futnak, mint egy tipikus Azure Functions alkalmazás. Tesztelje a kezelőt annak ellenőrzéséhez, hogy a környezet tartalmazza-e az összes futtatandó függőséget. A további függőségeket igénylő alkalmazások esetében előfordulhat, hogy egy Azure Functions Premium-csomagban üzemeltetett egyéni tárolórendszerkép használatával kell futtatnia őket.

Támogatás kérése

Ha egyéni kezelőkkel rendelkező függvényalkalmazással kapcsolatban van szüksége segítségre, a kéréseket rendszeres támogatási csatornákon keresztül küldheti el. Az egyéni kezelőalkalmazások készítéséhez használható nyelvek széles választéka miatt azonban a támogatás nem korlátlan.

A támogatás akkor érhető el, ha a Functions-gazdagépnek problémái vannak az egyéni kezelőfolyamat elindításával vagy kommunikációjával. Az egyéni kezelőfolyamat belső működésével kapcsolatos problémák, például a választott nyelvvel vagy keretrendszerrel kapcsolatos problémák esetén a támogatási csapatunk nem tud segítséget nyújtani ebben a környezetben.

Következő lépések

Az egyéni kezelők rövid útmutatójával megkezdheti a Azure Functions-alkalmazások készítését a Go-ban vagy a Rustban.