A template project to demonstrate how to run WebAssembly functions as sidecar microservices in dapr

Live Demo

1. Introduction

DAPR is a portable, event-driven runtime that makes it easy for any developer to build resilient, stateless and stateful applications that run on the cloud and edge and embraces the diversity of languages and developer frameworks. It's a Microsoft-incubated open-source project.

WasmEdge is a open-source, high-performance, extensible, and hardware optimized WebAssembly Virtual Machine for automotive, cloud, AI, and blockchain applications.

In this demonstration App, we create two image processing web services, integrated with Dapr. This project is built to demonstrate how to use Dapr to integrate Web applications in any programming language, and how WasmEdge can be embed in Go and Rust applications.

2. Architecture

This project contains mainly three components:

It is a simple Go Web application which is exposed as an endpoint of the whole application. It will render a static HTML page for the user to upload an image, and receive the image from the user, redirect request to internal image APIs.

This Dapr service is written in Golang. It uses WASI to call a prebuild wasm file to classify an image using a Tensorflow model.

This Dapr service is written in Rust. It simply starts a new process for the WasmEdge VM to run and perform grayscale on a image.

doc

3. Prerequisites

4. Build

make pre-install  ## Install WasmEdge dependences
make build        ## Will build all the components

## If you modify the wasm functions project,
## Use the commands in ./functions/grayscale/build.sh 
## and ./functions/classify/build.sh to generate new compiled files
make build-wasm

5. Run

To simplify the deployment, we provide a script to run the services:

make run-api-go ## Run the image-api-go
make run-api-rs ## Run the image-api-rs
make run-web ## Run the Web port service

For each component, you can also run it individually:

Start the web-port service

cd web-port
dapr run --app-id go-web-port \
         --app-protocol http \
         --app-port 8080 \
         --dapr-http-port 3500 \
         --components-path ../config \
         --log-level debug \
         ./web-port

Start the image-api-go service

cd image-api-go
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

Start the image-api-rust service

cd image-api-rs
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

After all the services started, we can use this command to verify:

dapr list
  APP ID        HTTP PORT  GRPC PORT  APP PORT  COMMAND               AGE  CREATED              PID
  go-web-port   3500       44483      8080      ./web-port            15m  2021-08-26 12:19.59  270961
  image-api-rs  3502       41661      9004      ./target/release/...  9m   2021-08-26 12:25.27  285749
  image-api-go  3501       34291      9003      ./image-api-go        9m   2021-08-26 12:25.27  285852

6. Online Demo: Dapr-WasmEdge

7. Appendix: an introduction to Dapr SDK

Dapr provides SDKs for different programming languages. Using the SDKs is the easiest way to run your applications in Dapr.

The SDK contains Client, Service, and Runtime API, and it is easy to use. For example, we use Service SDK in go-sdk to create the image-api-go service

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

In web-port/web_port.go, we use Dapr's Client to send request to Service:

func daprClientSend(image []byte, w http.ResponseWriter) {
	ctx := context.Background()

	// create the client
	client, err := dapr.NewClient()
	if err != nil {
		panic(err)
	}

	content := &dapr.DataContent{
		ContentType: "text/plain",
		Data:        image,
	}

	resp, err := client.InvokeMethodWithContent(ctx, "image-api-go", "/api/image", "post", content)
	if err != nil {
		panic(err)
	}
	log.Printf("dapr-wasmedge-go method api/image has invoked, response: %s", string(resp))
	fmt.Printf("Image classify result: %q\n", resp)
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "%s", string(resp))
}

For any Web Service which don't use Dapr SDK but registered as a Dapr instance, we can still can use http or gRpc to interact with it. Dapr will start a sidecar for each service instance. Essentially, sidecar works as a proxy for a service instance. We send request to sidecar, then the request is forwarded to the service instance. For example, in web-port/web_port.go we send a request to Rust api like this(3502 is the port of Sidecar):

client := &http.Client{}
	// http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>
	req, err := http.NewRequest("POST", "http://localhost:3502/v1.0/invoke/image-api-rs/method/api/image", bytes.NewBuffer(image))
	if err != nil {
		panic(err)
	}
	req.Header.Set("Content-Type", "text/plain")
	resp, _ := client.Do(req)
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Fprintf(w, "%s", body)
Owner
Second State
Fast, safe, portable & serverless. Deploy Rust functions in edge computing, Jamstack, SaaS and service mesh applications.
Second State
Comments
  • Create WasmEdge HTTP server sidecars

    Create WasmEdge HTTP server sidecars

    We would like to create two sidecars that are just wasmedge or wasmedge-tensorflow-lite. Instead of using GO or Rust to implement HTTP servers and then bootstrap wasmedge, we would just run those wasmedge-based server apps in sidecars.

    Sidecar #1: implement the grayscale function in Rust.

    https://github.com/second-state/wasmedge_wasi_socket/tree/main/examples/http_server

    Sidecar #2: implement the tensorflow function in JS

    https://github.com/second-state/wasmedge-quickjs#http-request

    https://github.com/second-state/wasmedge-quickjs#tensorflow

    https://www.secondstate.io/articles/embed-javascript-in-rust/

    After that, modify the web port to show all 4 sidecar apps.

  • It seems that memory leak occurs when image-api-go is called.

    It seems that memory leak occurs when image-api-go is called.

    I replace the web-port imageHandler func with the following code, to make it easier to observe(making more requests on one click). The memory usage of image-api-go is keeping growing when click classify.

    type mock struct {}
    
    func (m *mock)WriteHeader(statusCode int) {
    }
    
    func (m *mock)Header() http.Header {
    	return nil
    }
    func (m *mock)Write(b []byte) (int, error) {
    	return len(b), nil
    }
    
    func imageHandler(w http.ResponseWriter, r *http.Request) {
    	println("imageHandler ....")
    	body, err := ioutil.ReadAll(r.Body)
    
    	if err != nil {
    		println("error: ", err.Error())
    		panic(err)
    	}
    	api := r.Header.Get("api")
    	if api == "go" {
    		daprClientSend(body, w)
    		for i:=0;i<10;i++ {
    			go func() {
    				for i:=0;i<10;i++ {
    					daprClientSend(body, &mock{})
    				}
    			}()
    		}
    	} else {
    		httpClientSend(body, w)
    	}
    }
    
  • Use wasmedge-bindgen instead of wasm-bindgen in the GO SDK example

    Use wasmedge-bindgen instead of wasm-bindgen in the GO SDK example

    The current example uses wasm-bindgen to pass call parameters from the GO app to the WASM function. However, wasm-bindgen does not work with the Rust WASI target since Rust version 1.51. We now have a better solution for bindgen since WasmEdge 0.9.0. It is called wasmedge-bindgen:

    https://wasmedge.org/book/en/embed/go/function.html

    Can we refactor the GO host example to use wasmedge-bindgen?

  • Image formats

    Image formats

    The grayscale service only supports the GIF and PNG formats. But the classify service only supports the JPG format. Can we make them consistent?

    Perhaps supports JPGs by default in both services? Thanks.

  • GitHub Pages

    GitHub Pages

    I have configured GitHub Pages to serve from the main branch's root directory. So, our static web page is now available here:

    https://second-state.github.io/dapr-wasm/web/

    We do not actually need a GitHub Action for this. As soon as we update the main branch, it will be automatically deployed.

    We could, however, to use GitHub Actions to copy just the /web folder to another branch (e.g., gh-pages) and then deploy the gh-pages branch as the web site's root.

  • Create a customized WasmEdge for dapr

    Create a customized WasmEdge for dapr

    The WasmEdge GO SDK allows developers to register host functions to the runtime, and create a customized version of WasmEdge that includes the host functions.

    We should add Dapr GO SDK functions into a customized version of WasmEdge.

    We should then create Rust APIs for those Dapr host functions so that WasmEdge app developers can use them.

  • Error: error getting topics from app - unexpected end of JSON input - error occurred while beginning pubsub messages

    Error: error getting topics from app - unexpected end of JSON input - error occurred while beginning pubsub messages

    Hi,

    I am performing the grayscale tutorial example i.e. make run-api-rs the following error is occurring.

    ERRO[0000] error getting topics from app: unexpected end of JSON input  app_id=image-api-rs instance=wasm-oci-demo scope=dapr.runtime type=log ver=1.4.2
    ERRO[0000] error occurred while beginning pubsub messages: error getting topics from app: unexpected end of JSON input  app_id=image-api-rs instance=wasm-oci-demo scope=dapr.runtime type=log ver=1.4.2
    INFO[0000] dapr initialized. Status: Running. Init Elapsed 9.416037000000001ms  app_id=image-api-rs instance=wasm-oci-demo scope=dapr.runtime type=log ver=1.4.2
    

    The following video (which starts just as the error occurs) demonstrates the above error in action.

    https://youtu.be/A5EhO7cNTCw?t=392

    It seems that dapr list is displaying the correct output (in terms of apps, ports etc.)

    wasmedge@wasm-oci-demo:~$ dapr list
      APP ID        HTTP PORT  GRPC PORT  APP PORT  COMMAND               AGE  CREATED              PID     
      go-web-port   3500       40659      8080      ./web-port            36s  2021-10-05 02:38.25  334410  
      image-api-rs  3502       34493      9004      ./target/debug/im...  23s  2021-10-05 02:38.38  334530  
      image-api-go  3501       40639      9003      ./image-api-go        9s   2021-10-05 02:38.52  334658 
    

    Is it possible that the grayscale needs to be added to the config i.e. https://github.com/second-state/dapr-wasm/blob/main/config/conf.json

    Any suggestions would be greatly appreciated.

    Thanks Tim

  • update wasmedge_wasi_socket version to 0.2.0

    update wasmedge_wasi_socket version to 0.2.0

    reoslve Mismatched function type. Expected: FuncType {params{i32 , i32 , i32} returns{i32}} , Got: FuncType {params{i32 , i32} returns{i32}} issue: #25

  • Seems the wasm file is missed

    Seems the wasm file is missed

    https://github.com/second-state/dapr-wasm/blob/main/image-api-go/image_api.go#L50 The resource loaded seems not exist. Besides would you mind provide the code before compiling into wasm?

  • Inconsistent document/webpage link to this repo

    Inconsistent document/webpage link to this repo

    I see there are multiple article link to this repo with obsolete information: https://www.infoq.com/articles/webassembly-dapr-wasmedge/?utm_source=email&utm_medium=Web-Development&utm_campaign=newsletter&utm_content=11162021 https://wasmedge.org/book/en/use_cases/frameworks/mesh/dapr.html

    It seems above links are having a Golang microservice, I think it's better if you can maintain both repo or update again the article. Thanks

WebAssembly interop between Go and JS values.

vert Package vert provides WebAssembly interop between Go and JS values. Install GOOS=js GOARCH=wasm go get github.com/norunners/vert Examples Hello W

Dec 28, 2022
WebAssembly for Proxies (Golang host implementation)

WebAssembly for Proxies (GoLang host implementation) The GoLang implementation for proxy-wasm, enabling developer to run proxy-wasm extensions in Go.

Dec 29, 2022
🐹🕸️ WebAssembly runtime for Go
🐹🕸️ WebAssembly runtime for Go

Wasmer Go Website • Docs • Slack Channel A complete and mature WebAssembly runtime for Go based on Wasmer. Features Easy to use: The wasmer API mimics

Jan 2, 2023
🐹🕸️ WebAssembly runtime for Go
🐹🕸️ WebAssembly runtime for Go

Wasmer Go Website • Docs • Slack Channel A complete and mature WebAssembly runtime for Go based on Wasmer. Features Easy to use: The wasmer API mimics

Dec 29, 2022
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers
Golang-WASM provides a simple idiomatic, and comprehensive API and bindings for working with WebAssembly for Go and JavaScript developers

A bridge and bindings for JS DOM API with Go WebAssembly. Written by Team Ortix - Hamza Ali and Chan Wen Xu. GOOS=js GOARCH=wasm go get -u github.com/

Dec 22, 2022
A package to build progressive web apps with Go programming language and WebAssembly.
A package to build progressive web apps with Go programming language and WebAssembly.

Go-app is a package for building progressive web apps (PWA) with the Go programming language (Golang) and WebAssembly (Wasm). Shaping a UI is done by

Dec 30, 2022
A package to build progressive web apps with Go programming language and WebAssembly.
A package to build progressive web apps with Go programming language and WebAssembly.

Go-app is a package for building progressive web apps (PWA) with the Go programming language (Golang) and WebAssembly (Wasm). Shaping a UI is done by

Jan 2, 2023
Vugu: A modern UI library for Go+WebAssembly (experimental)

Vugu: A modern UI library for Go+WebAssembly (experimental)

Jan 3, 2023
WebAssembly runtime for wasmer-go
WebAssembly runtime for wasmer-go

gowasmer When compiling Go to WebAssembly, the Go compiler assumes the WebAssembly is going to run in a JavaScript environment. Hence a wasm_exec.js f

Dec 28, 2022
Tiny, blazing fast WebAssembly compute

Sat, the tiny WebAssembly compute module Sat (as in satellite) is an experiment, and isn't ready for production use. Please try it out and give feedba

Jan 5, 2023
WebAssembly Lightweight Javascript Framework in Go (AngularJS Inspired)

Tango Lightweight WASM HTML / Javascript Framework Intro WebAssembly is nice, Go on the web is nice, so I ported Tangu to Go and WebAssembly. Tangu is

Dec 20, 2022
Running a Command line tool written in Go on browser with WebAssembly

Running a command line tool written in Go on browser with WebAssembly This repo contains code/assets from the article Files: . ├── article.md

Dec 30, 2022
This library provides WebAssembly capability for goja Javascript engine

This module provides WebAssembly functions into goja javascript engine.

Jan 10, 2022
A Brainfuck to WebAssembly compiler written in Go.

brainfuck2wasm A Brainfuck to WebAssembly compiler written in Go. I am writing this compiler for a Medium article. When I complete the compiler, I'll

Jun 6, 2022
Dom - A Go API for different Web APIs for WebAssembly target

Go DOM binding (and more) for WebAssembly This library provides a Go API for dif

Jan 7, 2023
wazero: the zero dependency WebAssembly runtime for Go developers

wazero: the zero dependency WebAssembly runtime for Go developers WebAssembly is a way to safely run code compiled in other languages. Runtimes execut

Jan 2, 2023
Run WASM tests inside your browser

wasmbrowsertest Run Go wasm tests easily in your browser. If you have a codebase targeting the wasm platform, chances are you would want to test your

Dec 16, 2022
The Dapr CLI allows you to setup Dapr on your local dev machine or on a Kubernetes cluster

Dapr CLI The Dapr CLI allows you to setup Dapr on your local dev machine or on a

Dec 23, 2021
A test repo to demonstrate the current (go1.17.2) issue when trying to use retractA test repo to demonstrate the current (go1.17.2) issue when trying to use retract

test-go-mod-retract This is a test repo to demonstrate the current (go1.17.2) issue when trying to use retract in go.mod to retract a version in a non

Oct 16, 2021
This Go based project of Aadhyarupam Innovators demonstrate the code examples for building microservices, integration with cloud services (Google Cloud Firestore), application configuration management (Viper) etc.

This Go based project of Aadhyarupam Innovators demonstrate the code examples for building microservices, integration with cloud services (Google Cloud Firestore), application configuration management (Viper) etc.

Dec 22, 2022