架构实战——Dapr 的轻量级、安全、便携和高性能运行时( 三 )

一旦它在 HTTP POST 请求中接收到图像文件,它就会调用 WasmEdge 中的 WebAssembly 函数来执行图像处理任务 。它创建了一个 WasmEdge 实例来与 WebAssembly 程序交互 。
pub fn image_process(buf: &Vec<u8>) -> Vec<u8> {let mut child = Command::new("./lib/wasmedge-tensorflow-lite").arg("./lib/grayscale.wasm").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn().expect("failed to execute child");{// limited borrow of stdinlet stdin = child.stdin.as_mut().expect("failed to get stdin");stdin.write_all(buf).expect("failed to write to stdin");}let output = child.wait_with_output().expect("failed to wait on child");output.stdout}【架构实战——Dapr 的轻量级、安全、便携和高性能运行时】以下 Dapr CLI 命令在 Dapr 运行时环境中启动微服务 。
$ cd image-api-rs$ sudo dapr run --app-id image-api-rs--app-protocol http--app-port 9004--dapr-http-port 3502--components-path ../config--log-level debug./target/debug/image-api-rs$ cd ../Tensorflow 边车image-api-go sidecar 应用程序是用 Go 编写的 。它应该已经 lib/classify_bg.wasm 安装了上一步中的 WebAssembly 功能 。请参考 functions/bin/install.sh 脚本来安装 WasmEdge Runtime Go SDK 。
Sidecar 微服务运行一个事件循环,在路径上侦听传入的 HTTP 请求 /api/image 。
func main() {s := daprd.NewService(":9003")if err := s.AddServiceInvocationHandler("/api/image", imageHandlerWASI); err != nil {log.Fatalf("error adding invocation handler: %v", err)}if err := s.Start(); err != nil && err != http.ErrServerClosed {log.Fatalf("error listenning: %v", err)}}一旦它在 HTTP POST 请求中接收到图像文件,它就会调用 WasmEdge 中的 WebAssembly 函数来执行基于 Tensorflow 的图像分类任务 。它利用 WasmEdge 的 Go API 与 WebAssembly 程序进行交互 。
func imageHandlerWASI(_ context.Context, in *common.InvocationEvent) (out *common.Content, err error) {image := in.Datavar conf = wasmedge.NewConfigure(wasmedge.REFERENCE_TYPES)conf.AddConfig(wasmedge.WASI)var vm = wasmedge.NewVMWithConfig(conf)var wasi = vm.GetImportObject(wasmedge.WASI)wasi.InitWasi(os.Args[1:],/// The argsos.Environ(),/// The envs[]string{".:."}, /// The mapping directories[]string{},/// The preopens will be empty)/// Register WasmEdge-tensorflow and WasmEdge-imagevar tfobj = wasmedge.NewTensorflowImportObject()var tfliteobj = wasmedge.NewTensorflowLiteImportObject()vm.RegisterImport(tfobj)vm.RegisterImport(tfliteobj)var imgobj = wasmedge.NewImageImportObject()vm.RegisterImport(imgobj)vm.LoadWasmFile("./lib/classify_bg.wasm")vm.Validate()vm.Instantiate()res, err := vm.ExecuteBindgen("infer", wasmedge.Bindgen_return_array, image)ans := string(res.([]byte))vm.Delete()conf.Delete()out = &common.Content{Data:[]byte(ans),ContentType: in.ContentType,DataTypeURL: in.DataTypeURL,}return out, nil}以下 Dapr CLI 命令在 Dapr 运行时环境中启动微服务 。
$ cd image-api-go$ sudo dapr run --app-id image-api-go--app-protocol http--app-port 9003--dapr-http-port 3501--log-level debug--components-path ../config./image-api-go$ cd ../Web UI 边车Web UI 服务 web-port 是一个用 Go 编写的简单 Web 服务器 。它提供静态文件夹中的静态 HTML 和 JavaScript 文件,并将上传到 /api/hello 的图像发送到 灰度 或 分类 边车的 /api/image 端点 。
func main() {http.HandleFunc("/static/", staticHandler)http.HandleFunc("/api/hello", imageHandler)println("listen to 8080 ...")log.Fatal(http.ListenAndServe(":8080", nil))}func staticHandler(w http.ResponseWriter, r *http.Request) {// ... read and return the contents of HTML css and JS files ...}func imageHandler(w http.ResponseWriter, r *http.Request) {// ... ...api := r.Header.Get("api")if api == "go" {daprClientSend(body, w)} else {httpClientSend(body, w)}}// Send to the image-api-go sidecar (classify) via the Dapr APIfunc daprClientSend(image []byte, w http.ResponseWriter) {// ... ...resp, err := client.InvokeMethodWithContent(ctx, "image-api-go", "/api/image", "post", content)// ... ...}// Send to the image-api-rs sidecar (grayscale) via the HTTP APIfunc httpClientSend(image []byte, w http.ResponseWriter) {// ... ...req, err := http.NewRequest("POST", "http://localhost:3502/v1.0/invoke/image-api-rs/method/api/image", bytes.NewBuffer(image))// ... ...}page.js中的 JavaScript 只是将图像上传到 web-port sidecar 的 /api/hello 端点,web-port 将根据请求标头请求分类或灰度微服务 api 。
function runWasm(e) {const reader = new FileReader();reader.onload = function (e) {setLoading(true);var req = new XMLHttpRequest();req.open("POST", '/api/hello', true);req.setRequestHeader('api', getApi());req.onload = function () {// ...display results ...};const blob = new Blob([e.target.result], {type: 'application/octet-stream'});req.send(blob);};console.log(image.file)reader.readAsArrayBuffer(image.file);}


推荐阅读