Handler kustom Azure Functions

Setiap aplikasi Functions dijalankan oleh handler khusus bahasa. Meskipun Azure Functions mendukung banyak handler bahasa pemrogram secara default, di beberapa kasus sebaiknya Anda menggunakan bahasa pemrograman atau runtime yang berbeda.

Handler kustom adalah server web ringan yang menerima kejadian dari host Functions. Semua bahasa pemrogram yang mendukung primitif HTTP dapat mengimplementasikan handler kustom.

Handler kustom paling cocok untuk situasi di mana Anda ingin:

  • Menerapkan aplikasi fungsi dalam bahasa pemrograman yang saat ini di luar cakupan, seperti Go atau Rust.
  • Menerapkan aplikasi fungsi dalam runtime yang saat ini tidak didukung secara default, seperti Deno.

Dengan handler, Anda dapat menggunakan pemicu dan pengikatan input dan output melalui bundel ekstensi.

Mulai menggunakan handler kustom Azure Functions dengan mulai cepat di Go dan Rust.

Gambaran Umum

Diagram berikut menunjukkan hubungan antara host Functions dan server web yang diterapkan sebagai penanganan kustom.

Gambaran umum handler kustom Azure Functions

  1. Setiap kejadian memicu permintaan yang dikirim ke host Functions. Kejadian adalah pemicu apa pun yang didukung oleh Azure Functions.
  2. Host Functions kemudian mengeluarkan permintaan payload ke server web. Payload memegang data pemicu dan pengikatan input serta metadata lainnya untuk fungsi tersebut.
  3. Server web menjalankan fungsi individual, dan mengembalikan payload respons ke host Functions.
  4. Host Functions meneruskan data dari respons ke pengikatan output fungsi untuk diproses.

Aplikasi Azure Functions yang diterapkan sebagai penanganan kustom harus mengonfigurasi file host.json, local.settings.json, dan function.json sesuai dengan beberapa konvensi.

Struktur aplikasi

Untuk mengimplementasikan penanganan kustom, Anda memerlukan beberapa aspek berikut untuk aplikasi Anda:

  • File host.jsfon pada akar aplikasi Anda
  • File local.settings.json pada akar aplikasi Anda
  • File function.json untuk setiap fungsi (di dalam folder yang cocok dengan nama fungsinya)
  • Perintah, skrip, atau dapat dieksekusi, yang menjalankan server web

Diagram berikut menunjukkan bagaimana file-file ini terlihat pada sistem file untuk fungsi bernama "MyQueueFunction" dan handler kustom yang dapat dieksekusi bernama handler.exe.

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

Konfigurasi

Aplikasi dikonfigurasi melalui file host.json dan local.settings.json.

host.json

host.json memberi tahu host Functions tempat mengirim permintaan dengan menunjuk ke server web yang mampu memproses kejadian HTTP.

Handler kustom ditentukan dengan mengonfigurasi filehost.json beserta detail tentang cara menjalankan server web melalui bagian customHandler tersebut.

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

Bagian customHandler ini menunjuk ke target sebagaimana ditentukan oleh defaultExecutablePath. Target eksekusi dapat berupa perintah, executable, atau file tempat server web diimplementasikan.

Gunakan larik arguments untuk meneruskan argumen apa pun ke executable. Argumen mendukung perluasan variabel lingkungan (pengaturan aplikasi) menggunakan notasi %%.

Anda juga dapat mengubah direktori kerja yang digunakan oleh executable dengan workingDirectory.

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

Pemicu standar beserta pengikatan input dan output tersedia dengan mereferensikan file bundel ekstensi pada file host.json Anda.

local.settings.json

local.settings.json mendefinisikan pengaturan aplikasi yang digunakan saat menjalankan aplikasi fungsi secara lokal. Karena mungkin berisi rahasia, local.settings.json harus dikecualikan dari kontrol kode sumber. Di Azure, gunakan pengaturan aplikasi sebagai gantinya.

Untuk handler kustom, atur FUNCTIONS_WORKER_RUNTIME ke Custom dalam local.settings.json.

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

Metadata fungsi

Ketika digunakan dengan handler kustom, konten function.json tidak berbeda dari bagaimana Anda akan mendefinisikan fungsi di bawah konteks lain. Satu-satunya persyaratan adalah file function.json harus berada di dalam folder yang diberi nama sesuai dengan nama fungsinya.

Contoh function.json mengkonfigurasi fungsi yang memiliki pemicu antrean dan pengikatan output antrean. Karena berada dalam folder bernama MyQueueFunction, folder ini mendefinisikan fungsi bernama 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"
    }
  ]
}

Permintaan payload

Ketika pesan antrean diterima, host Functions mengirim permintaan posting HTTP ke handler kustom dengan payload di dalam badan pesan.

Kode berikut menunjukkan contoh permintaan payload. Payload mencakup struktur JSON dengan dua anggota: Data dan Metadata.

Anggota Data menyertakan kunci yang cocok dengan nama input dan pemicu seperti yang didefinisikan dalam larik pengikatan dalam file function.json.

Anggota Metadata menyertakan metadata yang dihasilkan dari sumber kejadain.

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

Menurut konvensi, respons fungsi diformat sebagai pasangan kunci/nilai. Kunci yang didukung meliputi:

Kunci Payload Jenis data Keterangan
Outputs objek Mengandung nilai respons sebagaimana didefinisikan pada larikbindings di function.json.

Misalnya, jika fungsi dikonfigurasi dengan pengikatan output antrean bernama "myQueueOutput", maka Outputs berisi kunci bernama myQueueOutput, yang diatur oleh handler kustom ke pesan yang dikirim ke antrean.
Logs larik Pesan muncul di log pemanggilan Functions.

Saat dijalankan di Azure, pesan muncul di Application Insights.
ReturnValue string Digunakan untuk memberikan respons ketika output dikonfigurasi seperti $return di dalam file function.json.

Ini adalah contoh respons payload.

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

Contoh

Handler kustom dapat diimplementasikan dalam semua bahasa pemrogram yang mendukung penerimaan kejadian HTTP. Contoh berikut menunjukkan cara mengimplementasikan handler kustom menggunakan bahasa pemrogram Go.

Fungsi dengan pengikatan

Skenario yang diterapkan dalam contoh ini menampilkan fungsi bernama order yang menerima POST dengan payload yang mewakili pesanan produk. Saat pesanan di-posting pada fungsi, pesan Queue Storage dibuat dan respons HTTP dikembalikan.

Implementasi

Dalam folder bernama order, file function.json mengkonfigurasi fungsi yang dipicu 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"
    }
  ]
}

Fungsi ini didefinisikan sebagai fungsi yang dipicu HTTP yang mengembalikan respons HTTP dan output pesan Queue storage.

Di akar aplikasi, file host.json dikonfigurasi untuk menjalankan file executable bernama handler.exe ( handler di Linux atau macOS).

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

Ini adalah permintaan HTTP yang dikirim ke runtime Functions.

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

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

Runtime Functions kemudian akan mengirim permintaan HTTP berikut ke handler kustom:

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

Catatan

Beberapa bagian dari payload dihapus untuk keringkasan.

handler.exe adalah program penanganan kustom Go terkompilasi yang menjalankan server web dan merespons permintaan pemanggilan fungsi dari host 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))
}

Dalam contoh ini, handler kustom menjalankan server web untuk menangani kejadian HTTP dan disetel untuk mendengarkan permintaan melalui FUNCTIONS_CUSTOMHANDLER_PORT.

Meskipun host Functions menerima permintaan HTTP asli pada /api/order, host Functions memanggil penanganan kustom menggunakan nama fungsi (nama foldernya). Dalam contoh ini, fungsi didefinisikan pada jalur /order. Host mengirim penanganan kustom permintaan HTTP di jalur /order.

Karena POST permintaan dikirim ke fungsi ini, data pemicu dan metadata fungsi tersedia melalui badan permintaan HTTP. Badan permintaan HTTP asli dapat diakses di dalam Data.req.Bodypayload.

Respons fungsi diformat menjadi pasangan kunci/nilai ketikaOutputs anggota memegang nilai JSON di mana kuncinya cocok dengan output seperti yang didefinisikan dalam file function.json.

Ini adalah contoh payload yang dikembalikan handler kustom ke host Functions.

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

Dengan mengatur output message yang sama dengan data pesanan yang masuk dari permintaan, fungsi akan meng-output data pesanan ke antrean yang dikonfigurasi. Host Functions juga mengembalikan respons HTTP yang dikonfigurasi pada res ke penelepon.

Fungsi HTTP-only

Untuk fungsi yang dipicu HTTP tanpa pengikatan atau output tambahan, Anda mungkin ingin handler kustom Anda bekerja langsung dengan permintaan dan respons HTTP daripada permintaan penanganan kustom dan payload respons. Perilaku ini dapat dikonfigurasi pada host.json menggunakan pengaturan enableForwardingHttpRequest.

Penting

Tujuan utama fitur handler kustom adalah untuk mengaktifkan bahasa pemrogram dan runtime yang saat ini tidak memiliki dukungan kelas satu di Azure Functions. Meskipun aplikasi web dapat dijalankan menggunakan handler kustom, Azure Functions bukan standar proksi terbalik. Beberapa fitur seperti streaming respons, HTTP/2, dan WebSocket tidak tersedia. Beberapa komponen permintaan HTTP seperti header dan rute tertentu mungkin dibatasi. Aplikasi Anda mungkin juga mengalami cold start yang berlebihan.

Untuk mengatasi keadaan ini, pertimbangkan untuk menjalankan aplikasi web Anda di Azure App Service.

Contoh berikut menunjukkan cara mengonfigurasi fungsi yang dipicu HTTP tanpa pengikatan atau output tambahan. Skenario yang diterapkan dalam contoh ini menampilkan fungsi bernama hello yang menerima GET atau POST.

Implementasi

Dalam folder bernama hello, file function.json mengkonfigurasi fungsi yang dipicu HTTP.

hello/function.json

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

Fungsi ini dikonfigurasi untuk menerima kedua permintaan GET dan POST serta nilai hasil disediakan melalui argumen bernama res.

Di akar aplikasi, file host.json dikonfigurasi untuk menjjalankan handler.exe dan enableForwardingHttpRequest disetel ke true.

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

Ketika enableForwardingHttpRequest adalah true, perilaku fungsi http-only berbeda dari perilaku default handler kustom dengan beberapa cara ini:

  • Permintaan HTTP tidak berisi handler kustom meminta payload. Sebaliknya, host Functions memanggil handler kustom melalui salinan permintaan HTTP asli.
  • Host Functions memanggil handler kustom melaui jalur yang sama dengan permintaan asli termasuk semua parameter untai kueri.
  • Host Functions mengembalikan salinan respons HTTP handler sebagai respons terhadap permintaan asli.

Berikut ini adalah permintaan POST ke host Functions. Host Functions kemudian mengirim salinan permintaan ke handler kustom pada jalur yang sama.

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

{
  "message": "Hello World!"
}

File handler.go file menerapkan server web dan fungsi 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))
}

Dalam contoh ini, handler kustom membuat server web untuk menghandel kejadian HTTP dan disetel untuk mendengarkan permintaan melalui FUNCTIONS_CUSTOMHANDLER_PORT.

GET permintaan ditangani dengan mengembalikan string, dan permintaan POST memiliki akses ke badan permintaan.

Rute untuk fungsi pesanan di sini adalah /api/hello, sama dengan permintaan asli.

Catatan

FUNCTIONS_CUSTOMHANDLER_PORT bukan merupakan port dengan akses publik yang digunakan untuk memanggil fungsi. Port ini digunakan oleh host Functions untuk memanggil handler kustom.

Menyebarkan

Handler kustom dapat disebarkan ke setiap opsi hosting Azure Functions. Jika handler kustom Anda memerlukan dependensi sistem operasi atau platform (seperti runtime bahasa pemrogram), Anda mungkin perlu menggunakan kontainer kustom.

Saat membuat aplikasi fungsi di Azure untuk handler kustom, kami sarankan Anda memilih .NET Core sebagai tumpukan.

Untuk menyebarkan aplikasi handler kustom menggunakan Azure Functions Core Tools, jalankan perintah berikut.

func azure functionapp publish $functionAppName

Catatan

Pastikan semua file yang diperlukan untuk menjalankan handler kustom Anda ada di folder dan disertakan dalam penyebaran. Jika hander kustom Anda adalah executable biner atau memiliki dependensi khusus platform, pastikan seluruh file ini cocok dengan platform penyebaran target.

Batasan

  • Server web handler kustom harus dimulai dalam 60 detik.

Sampel

Lihat sampel hander kustom dari GitHub repo untuk contoh cara mengimplementasikan fungsi dalam berbagai bahasa pemrogram yang berbeda.

Pemecahan masalah dan dukungan

Pembuatan log jejak

Jika proses handler kustom Anda gagal memulai atau jika mengalami masalah saat berkomunikasi dengan host Functions, Anda dapat meningkatkan tingkat log aplikasi fungsi ke Trace guna melihat lebih banyak pesan diagnostik dari host.

Untuk mengubah tingkat log default aplikasi fungsi, konfigurasikan pengaturan logLevel di bagian logging dari host.json.

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

Host Functions memberikan output pesan log tambahan termasuk informasi yang terkait dengan proses handler kustom. Gunakan log untuk menyelidiki masalah saat memulai proses handler kustom Anda atau memanggil fungsi pada handler kustom Anda.

Secara lokal, log dicetak ke konsol.

Di Azure, kueri Application Insights melakukan pelacakan untuk menampilkan pesan log. Jika aplikasi Anda menghasilkan log dalam jumlah besar, hanya sebagian pesan log yang dikirim ke Application Insights. Nonaktifkan pengambilan sampel untuk memastikan semua pesan tercatat.

Menguji handler kustom secara isolasi

Aplikasi handler kustom adalah proses server web, jadi mungkin berguna untuk memulainya secara mandiri dan menguji pemanggilan fungsi dengan mengirim permintaan HTTP tiruan menggunakan alat seperti cURL atau Postman.

Anda juga dapat menggunakan strategi ini di alur CI/CD untuk menjalankan pengujian otomatis pada handler kustom Anda.

Lingkungan eksekusi

Handler kustom dijalankan di lingkungan yang sama dengan aplikasi Azure Functions biasa. Uji handler Anda untuk memastikan lingkungan berisi semua dependensi yang diperlukan untuk berjalan. Untuk aplikasi yang memerlukan dependensi tambahan, Anda mungkin perlu menjalankannya menggunakan citra kontainer kustom yang di hosting pada paket Azure Functions Premium.

Dapatkan dukungan

Jika memerlukan bantuan pada aplikasi fungsi dengan handler kustom, Anda dapat mengirimkan permintaan melalui saluran dukungan reguler. Namun, karena berbagai bahasa pemrogram yang mungkin digunakan untuk membangun aplikasi handler kustom, dukungan akan terbatas.

Dukungan tersedia jika host Functions memiliki masalah saat memulai atau berkomunikasi dengan proses handler kustom. Untuk masalah khusus mengenai cara kerja di dalam proses handler kustom Anda, seperti masalah dengan bahasa pemrogram atau kerangka kerja yang dipilih, Tim Dukungan kami tidak dapat memberikan bantuan dalam konteks ini.

Langkah berikutnya

Mulai membangun aplikasi Azure Functions di Go atau Rust dengan mulai cepat penanganan kustom.