This repo provides an accelerator to build and host Web Assemblies (Wasm) on server side. Intentionally, it does not make use of any of the existing frameworks, this allows us to explain the core concepts better.
WebAssembly (abbreviated as Wasm) is a binary instruction format for a stack-based virtual machine. It is designed as a standardised portable compilation target for programming languages, enabling deployment on the web for client and server applications, equally. Until recently, Wasm was used mostly in web browsers (client side), this is now changing fast, as we see the benefits of Wasm being realised on server side or edge as well.
When Wasm is hosted outside of web browser, it needs an interface to interact with the outside world, this interface is provided WASI (Web Assembly System Interface) which is hosted by a standalone runtime called Wasmtime. WASI interfaces makes Wasm portable to multiple operating systems' APIs (e.g. both POSIX and Windows) and Wasmtime runtime SDKs allows us to host the runtime in other applications which may be written in any of the supported SDK languages, enabling the development of Wasm orchestrators.
This question has been answered already by many organisations and individuals alike, but here are some of the key tenets/promises of Wasm:
-
Portable (Hardware, Language and Platform)
-
Secure (sandboxed and secure by default)
-
Near native performance
-
Lightweight (when compared to containers)
Wasm modules run in a sandbox provided by its runtime, this provides a default isolation boundary for the modules i.e. no access to the host resources. This is a great starting point, however most modules require some kind of interfacing with the host which is executing these modules, or access to the resources outside of their isolation boundary. Commonly, interfacing with the outside world (from a Wasm module perspective) falls into the following buckets:
- Guest Wasm module needs to access host file system, this may be required to access a config file or persist data.
- Guest Wasm module needs to make outbound http calls, this may be required to make a call to a RESTful service.
- Guest Wasm module needs to host a service on socket:port (endpoint), this may be required to host pub-sub messaging server.
This accelerator addresses these common challenges by making use of WASI, in three primary ways:
- Using WASI's pre-built APIs, that is, file system APIs and socket APIs to provide pre-opened directory and socket access to the Wasm module.
- Importing host funtions to Wasm modules, as these functions are defined in the host they have access to resources at the host level. Additionally, this mechanism provides a way to achieve IoC/DI (Inversion of Control/Dependency Injection) pattern in Wasm modules.
- Exporting Wasm functions to host, these functions can be called by the host to bootstrap the Wasm module or interact with the module while its running.
The diagram below provides the conceptual view of the above points:
Modern applications consist of multiple services (often following microservices approach), these services interact with each other to achieve a business function. To enable this ecosystem of services to work together, we need to have a resilient messaging ecosystem, usually implemented by a pub-sub component. In this solution, this pub-sub component is implemented as Wasm module hosting a pseudo pub-sub messaging server on an endpoint (a WASI pre-opened socket). This scenario is described in the diagram below:
WIT (Web-Assembly Interface Types) host functions import approach allows the guest Wasm module to import functions from the host app. Consider WIT as a contract definition language between Wasm and host app via WASI, wit-bindgen tool takes this contract and generates bindings for different languages, very similar to how we create bindings for Open API Specs. These import functions are written in host app programming language e.g. Rust/C#/Go, allowing us to write functions to access resources on the host e.g. networking stack to connect to cloud. This is explained “here (ignore some older termonologies)”. Same WIT based approach is used for exporting functions from Wasm module to Host, where they can be called.
Solution implemented in the repo consist of the following components:
Host service encapsulates Wasm runtime, this runtime executes guest Wasm modules which are pre-compiled into Wasm bytecode. The service makes use of Rust lang for development, but it could also be developed in any other language which is supported by Wasmtime SDK. Key responsiblities of this service are:
- Embed Wasmtime runtime SDK
- Configures WASI to
- Pre-open directory at host machine level to allow Wasm modules to access it.
- Pre-open network socket and provide its handle to Wasm modules to listen traffic on. This is particularly useful if you want to host server in Wasm modules.
- Export functions defined in the host service to Wasm modules.
- Import functions from wasm modules, which can be called from host service to interact with the modules.
There are three Wasm modules in this solution:
-
Gateway
Role of this Wasm module is to send http post to the external endpoint (create one at https://requestbin.com for testing) , it will subscribes to the events on Server(psuedo pub-sub) module.
--gateway-allowed-host
cmdline parameter in host app defines the permitted host which this wasm module can post messages to, specify your http post endpoint via this parameter for WASI to allow access and in Wasm module's config filemodules/gateway_module/src/config.rs
to post to this endpoint. -
Server
Role of this Wasm module is to run a server which listens on a pre-opened socket, in this solution a psuedo pub/sub module.
--server-socket-address
cmdline parameter in host app defines the endpoint on which server listens. -
Telemetry
Role of this module is to emit events which will be sent to Server/pub-sub module.
- Open the solution in Codespaces
- Compile modules to wasm32-wasi:
-
cd modules/telemetry_module
run
cargo build --target wasm32-wasi
-
cd modules/gateway_module
run
cargo build --target wasm32-wasi
-
cd modules/server_module
run
cargo build --target wasm32-wasi
-
- Compile and run Host app with Wasmtime and WASI importing/exporting functions to/from the above Wasm modules:
cd host
- run
cargo run -- --gateway-allowed-host "https://eouig31wcbg8fl.m.pipedream.net" --server-socket-address "127.0.0.1:8080"