FaaS in Go mit WASM, WASI und Rust

Dieser Beitrag lässt sich am besten als Technologiedemo beschreiben; es führt Webserver, Plugins, WebAssembly, Go, Rust und ABIs zusammen. Folgendes wird angezeigt:

So laden Sie WASM-Code mit WASI in eine Go-Umgebung und verbinden ihn mit einem Webserver. Wie man Webserver-Plugins in jeder Sprache implementiert, die in WASM kompiliert werden kann. So übersetzen Sie Go-Programme in WASM, das WASI verwendet. So übersetzen Sie Rust-Programme in WASM, das WASI verwendet. Wie man WAT-Code (WebAssembly Text) schreibt, der WASI verwendet, um mit einer Nicht-JS-Umgebung zu interagieren.

Wir werden einen einfachen FAAS-Server (Function as a Service) in Go erstellen, der es uns ermöglicht, Module in jeder Sprache zu schreiben, die ein WASM-Ziel hat. Im Vergleich zu bestehenden Technologien ist es etwas zwischen den Cloud-Funktionen der GCP, Cloud Run und dem guten alten CGI.

Der FAAS-Server

Wir beginnen unsere eingehende Analyse mit dem FAAS-Server selbst (vollständiger Code hier). Der HTTP-Verwaltungsteil ist einfach:

func httpHandler(w http.ResponseWriter, req *http.Request) { Teile := strings.Split(strings.Trim(req.URL.Path, "/"), "/") wenn len(Teile) < 1 { http.Error(w, "want /{modulename} prefix", http.StatusBadRequest) Rückmeldung } mod:= teile[0] log.Printf("Modul %v angefordert mit Anfrage %v", mod, req.URL.Query()) env := map[string]string{ "http_path": req.URL.Pfad, "http_method": req.Methode, "http_host": erf. Gastgeber, "http_query": req.URL.Query().Encode(), "remote_addr": req.RemoteAddr, } modpath := fmt.Sprintf("target/%v.wasm", mod) log.Printf("Modul %v wird geladen", modpath) out, err := invokeWasmModule(mod, modpath, env) wenn err != nil { log.Printf("Fehler beim Laden des Moduls %v", modpath) http.Error(w, "Modul kann nicht gefunden werden "+modpath, http.StatusNotFound) Rückmeldung } // Die Standardausgabe des Moduls wird in die Antwort geschrieben. fmt.Fprint(w, out) } Hauptfunktion() { mux := http.NewServeMux() mux.HandleFunc("/", httpHandler) log.Fatal(http.ListenAndServe(":8080", mux)) }

Dieser Server lauscht auf Port 8080 (fühlen Sie sich frei, ihn zu ändern oder besser konfigurierbar zu machen) und registriert einen Catch-All-Handler für den Root-Pfad. Der Handler analysiert die tatsächliche Anforderungs-URL, um den Modulnamen zu finden. Es speichert dann einige Informationen, die an das geladene Modul in der Umgebungskarte weitergegeben werden.

Das geladene Modul wird mit einer Dateisystemsuche im Zielverzeichnis relativ zur FAAS-Server-Binärdatei gefunden. Dies alles dient nur zu Demonstrationszwecken und kann natürlich leicht geändert werden. Der Handler ruft dann InvokeWasmModule auf, auf das wir gleich noch eingehen werden. Diese Funktion gibt die Standardausgabe des aufgerufenen Moduls zurück, die der Handler in der HTTP-Antwort ausgibt.

Ausführen von WASM-Code in Go

Wie führe ich ein gegebenes WASM-Modul programmgesteuert in Go aus? Es gibt mehrere hochwertige WASM-Laufzeiten, die außerhalb der Browserumgebung funktionieren, und viele von ihnen haben Go-Bindungen; zum Beispiel wasmtime-go. Was mir jedoch am besten gefällt, ist Wazero; Dies ist eine reine, abhängigkeitsfreie Go-Laufzeitumgebung, für die keine Voraussetzungen gelten, außer dass ein Go-Get ausgeführt wird. Unser FAAS-Server verwendet Wazero, um WASM-Module zu laden und auszuführen.

Hier ist invokeWasmModule:

//invokeWasmModule ruft das angegebene WASM-Modul auf (als Dateipfad angegeben), // Setzen seiner Umgebungsvariablen basierend auf env. Gibt die Standardausgabe des Moduls zurück. func invokeWasmModule(string modname, string wasmPath, env map[string]string) (string, error) { ctx := Kontext.Hintergrund() r:= wazero.NewRuntime(ctx) zurückstellen r.Close(ctx) wasi_snapshot_preview1.MustInstantiate(ctx, r) // Wasm-Laufzeit instanziieren und die vom Host exportierten Funktionen konfigurieren // die das wasm-Modul für Protokollierungszwecke verwenden kann. _, err := r.NewHostModuleBuilder("env"). NewFunctionBuilder(). WithFunc(func(v uint32) { log.Printf("[%v]:%v", Mod-Name, v) }). Exportieren ("log_i32"). NewFunctionBuilder(). MitSpaß...

FaaS in Go mit WASM, WASI und Rust

Dieser Beitrag lässt sich am besten als Technologiedemo beschreiben; es führt Webserver, Plugins, WebAssembly, Go, Rust und ABIs zusammen. Folgendes wird angezeigt:

So laden Sie WASM-Code mit WASI in eine Go-Umgebung und verbinden ihn mit einem Webserver. Wie man Webserver-Plugins in jeder Sprache implementiert, die in WASM kompiliert werden kann. So übersetzen Sie Go-Programme in WASM, das WASI verwendet. So übersetzen Sie Rust-Programme in WASM, das WASI verwendet. Wie man WAT-Code (WebAssembly Text) schreibt, der WASI verwendet, um mit einer Nicht-JS-Umgebung zu interagieren.

Wir werden einen einfachen FAAS-Server (Function as a Service) in Go erstellen, der es uns ermöglicht, Module in jeder Sprache zu schreiben, die ein WASM-Ziel hat. Im Vergleich zu bestehenden Technologien ist es etwas zwischen den Cloud-Funktionen der GCP, Cloud Run und dem guten alten CGI.

Der FAAS-Server

Wir beginnen unsere eingehende Analyse mit dem FAAS-Server selbst (vollständiger Code hier). Der HTTP-Verwaltungsteil ist einfach:

func httpHandler(w http.ResponseWriter, req *http.Request) { Teile := strings.Split(strings.Trim(req.URL.Path, "/"), "/") wenn len(Teile) < 1 { http.Error(w, "want /{modulename} prefix", http.StatusBadRequest) Rückmeldung } mod:= teile[0] log.Printf("Modul %v angefordert mit Anfrage %v", mod, req.URL.Query()) env := map[string]string{ "http_path": req.URL.Pfad, "http_method": req.Methode, "http_host": erf. Gastgeber, "http_query": req.URL.Query().Encode(), "remote_addr": req.RemoteAddr, } modpath := fmt.Sprintf("target/%v.wasm", mod) log.Printf("Modul %v wird geladen", modpath) out, err := invokeWasmModule(mod, modpath, env) wenn err != nil { log.Printf("Fehler beim Laden des Moduls %v", modpath) http.Error(w, "Modul kann nicht gefunden werden "+modpath, http.StatusNotFound) Rückmeldung } // Die Standardausgabe des Moduls wird in die Antwort geschrieben. fmt.Fprint(w, out) } Hauptfunktion() { mux := http.NewServeMux() mux.HandleFunc("/", httpHandler) log.Fatal(http.ListenAndServe(":8080", mux)) }

Dieser Server lauscht auf Port 8080 (fühlen Sie sich frei, ihn zu ändern oder besser konfigurierbar zu machen) und registriert einen Catch-All-Handler für den Root-Pfad. Der Handler analysiert die tatsächliche Anforderungs-URL, um den Modulnamen zu finden. Es speichert dann einige Informationen, die an das geladene Modul in der Umgebungskarte weitergegeben werden.

Das geladene Modul wird mit einer Dateisystemsuche im Zielverzeichnis relativ zur FAAS-Server-Binärdatei gefunden. Dies alles dient nur zu Demonstrationszwecken und kann natürlich leicht geändert werden. Der Handler ruft dann InvokeWasmModule auf, auf das wir gleich noch eingehen werden. Diese Funktion gibt die Standardausgabe des aufgerufenen Moduls zurück, die der Handler in der HTTP-Antwort ausgibt.

Ausführen von WASM-Code in Go

Wie führe ich ein gegebenes WASM-Modul programmgesteuert in Go aus? Es gibt mehrere hochwertige WASM-Laufzeiten, die außerhalb der Browserumgebung funktionieren, und viele von ihnen haben Go-Bindungen; zum Beispiel wasmtime-go. Was mir jedoch am besten gefällt, ist Wazero; Dies ist eine reine, abhängigkeitsfreie Go-Laufzeitumgebung, für die keine Voraussetzungen gelten, außer dass ein Go-Get ausgeführt wird. Unser FAAS-Server verwendet Wazero, um WASM-Module zu laden und auszuführen.

Hier ist invokeWasmModule:

//invokeWasmModule ruft das angegebene WASM-Modul auf (als Dateipfad angegeben), // Setzen seiner Umgebungsvariablen basierend auf env. Gibt die Standardausgabe des Moduls zurück. func invokeWasmModule(string modname, string wasmPath, env map[string]string) (string, error) { ctx := Kontext.Hintergrund() r:= wazero.NewRuntime(ctx) zurückstellen r.Close(ctx) wasi_snapshot_preview1.MustInstantiate(ctx, r) // Wasm-Laufzeit instanziieren und die vom Host exportierten Funktionen konfigurieren // die das wasm-Modul für Protokollierungszwecke verwenden kann. _, err := r.NewHostModuleBuilder("env"). NewFunctionBuilder(). WithFunc(func(v uint32) { log.Printf("[%v]:%v", Mod-Name, v) }). Exportieren ("log_i32"). NewFunctionBuilder(). MitSpaß...

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow