如何经过网络远程执行WebAssembly虚拟机

file

本文 demo 了终端用户以及机器用户如何在只使用 HTTP 请求的状况下,经过 web 从 Wasm 函数中找到答案。 对于更喜欢冒险的读者,本文还 demo了如何在相同的基础结构上编写和部署 Wasm 可执行文件。linux

背景

以前的文章,咱们谈到,虽然 Wasm 在客户端确实很受欢迎,但 Wasm 最近也成为了服务器端技术和服务的有力竞争者。git

基于这个想法,《去中心化计算的将来:经过 RPC 从微服务过渡到 WASM》一文提出:将来,分布式计算微服务将会由传统的微服务向 Wasm 基础设施过渡。 这其中的缘由有不少。 其中之一就是,经过 Wasm ,在大多数源代码语言和本地硬件之间能够编写和共享单独的 discrete 函数的逻辑。github

所以,不可避免地,这意味着 Wasm 将适用于大多数应用程序,即便 Wasm 在后端,也能够有效地执行一组管理良好的 discrete 函数来为每一个应用程序服务。web

固然,这种乌托邦式的分布式计算模式须要一些基础,咱们能够在这样的基础上创建本身的定制业务和企业软件。macos

SSVM 概览

虽然咱们的目标看似高不可攀,但其实是能够实现的。在服务端的 WebAssembly 领域,出现了一些使人无比激动的,可以为咱们“铺路”的基础设施。编程

Second State 最近构建了一套软件,使部署和执行服务器端的 Wasm 变得很是方便。json

下面的图表显示了构成这个系统的一些组件,即:后端

  • SSVMRPC —— 使用 Rust 编写的远程过程调用 (RPC) 实现,能够方便地与 SecondState 的无状态(Stateless)虚拟机 SSVM 进行代码部署和代码执行交互
  • SSVMContainer ーー处在网络和 SSVM 传入请求之间的 Rust 应用程序。 此应用程序处理 Wasm 应用程序的部署并管理服务的执行(即,Wasm 应用程序内部的可调用函数)。由于 SSVM 执行无状态执行,它还管理应用程序状态。
  • SSVM ( https://github.com/second-sta... )ーー 高性能、硬件优化、无状态、基于堆栈的 Wasm 虚拟机。 SSVM 能够执行任意二进制文件 ,同时对于 AI区块链特定的应用也是高度优化的。

file

Second State 研发的 SSVM 有一个核心的优点,就是使用者不须要知道它的内部工做机制。事实上,要使用 SSVM 执行服务器端的 Wasm,您只须要发出一个简单的 HTTP 请求。浏览器

下文将演示“调用应用程序的函数” ,但在此以前,让咱们先花几分钟的时间进行一次快速的技术深刻讨论。安全

技术解析

本节将展现如何在 SSVM 上建立和部署本身的 Wasm 可执行文件。咱们将在 Ubuntu 操做系统上使用 Rust,建立和部署 Demo。

安装Rust

sudo apt-get update
sudo apt-get -y upgrade
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

建立新的应用程序

cd ~
cargo new --lib add
cd add

设置 Wasm 特定的系统配置

将如下内容添加到 Cargo.toml 文件

[lib]
name = "add_lib"
path = "src/lib.rs"
crate-type =["cdylib"]

编写源代码

打开新文件 src/lib.rs 并加上如下代码

#[no_mangle]
pub extern fn add_two_numbers(_x: i32, _y: i32) -> i32{
_x + _y
}

Wasm 系统配置

rustup target add wasm32-wasi
rustup override set nightly

编译到Wasm

cargo build --release --target=wasm32-wasi

上面的集合将在 target/wasm32-wasi/release/add_lib.wasm 建立新的Wasm文件。这个文件咱们将部署在 SecondState 的 Wasm 基础设施 SSVM 上。

在部署这个应用程序时,咱们将遵循这个特定的 HTTP POST 规范

快速了解已编译的 Wasm 文件

WAT

若是想查看新建立的 Wasm 应用程序的文本表示(称为“ WebAssembly Text format”或简称“ WAT”) ,安装很是有用的 WABT工具包就能够。

只需运行如下命令,就能够将 Wasm 转换为 Wat。

./wasm2wat ~/add/target/wasm32-wasi/release/add_lib.wasm -o ~/add/target/wasm32-wasi/release/add_lib.wat

文本展现(WAT) 将大概以下:

(module
(type (;0;) (func (param i32 i32) (result i32)))
(func $add_two_numbers (type 0) (param i32 i32) (result i32)
local.get 1
local.get 0
i32.add)
(table (;0;) 1 1 funcref)
(memory (;0;) 16)
(global (;0;) (mut i32) (i32.const 1048576))
(global (;1;) i32 (i32.const 1048576))
(global (;2;) i32 (i32.const 1048576))
(export "memory" (memory 0))
(export "__data_end" (global 1))
(export "__heap_base" (global 2))
(export "add_two_numbers" (func $add_two_numbers)))

Wasm

您将注意到原始的 add_lib.wasm 没法随意查看, 由于它是一个可执行的二进制文件。 此外,在没有任何优化的状况下,默认由 Rust 编译生成 Wasm 文件大约为1800000字节。 就Wasm而言,这是很大的。

咱们将使用 xxd 命令将 Wasm 文件转换为十六进制(用于 HTTP POST 的 JSON 数据)。 可是,我建议在转换以前,缩小原来的 Wasm 文件。

xxd -p target/wasm32-wasi/release/add_lib.wasm | tr -d $'\n'

缩小 Wasm 可执行文件的一个很是简单的方法是再次使用超赞的 wabt toolkit * 。 除了在这里,咱们还将以另外一种方式转换回来,即,将刚刚建立的 wat 文件转换回 wasm,以下所示。

./wat2wasm ~/add/target/wasm32-wasi/release/add_lib.wat -o ~/add/target/wasm32-wasi/release/add_lib.wasm

如今能够安全地执行上面的 xxd 命令了。

这些 wabt 转换的整体结果是, Wasm 可执行文件的大小从以前的 1800000字节变为 4000字节。 新的 Wasm 可执行文件的十六进制表示形式(在 xxd 命令以后)如今看起来像这样(仅供参考)。

0061736d0100000001070160027f7f017f030201000405017001010105030100100619037f01418080c0000b7f00418080c0000b7f00418080c0000b073704066d656d6f727902000a5f5f646174615f656e6403010b5f5f686561705f6261736503020f6164645f74776f5f6e756d6265727300000a09010700200120006a0b

复制粘贴很容易了,对吗?

部署应用

Wasm 文件的这个十六进制转储须要一个小调整。 在部署应用程序以前,咱们须要在 Wasm 十六进制字符串的开头添加一个 0x

下面是咱们如何经过 Curl 部署上面的 Wasm 应用程序的示例。

Curl

注意咱们手动添加的 0x,在字节码的开头!

curl --header "Content-Type: application/json" \
--request POST \
--data '{
"request": {
"application": {
"storage": "file_system",
"bytecode": "0x0061736d0100000001070160027f7f017f030201000405017001010105030100100619037f01418080c0000b7f00418080c0000b7f00418080c0000b073704066d656d6f727902000a5f5f646174615f656e6403010b5f5f686561705f6261736503020f6164645f74776f5f6e756d6265727300000a09010700200120006a0b","name": "Add"}}}' \
http://13.54.168.1:8080/deploy_wasm_application

Postman — GUI HTTP 客户端

file

若是使用 GUI HTTP 客户机发出 POST 请求,那么下面是您传入的等效 JSON。

一样,请注意咱们在字节码开始时手动添加的 0x

{
 "request": {
  "application": {
   "storage": "file_system",
   "bytecode": "0x0061736d0100000001070160027f7f017f030201000405017001010105030100100619037f01418080c0000b7f00418080c0000b7f00418080c0000b073704066d656d6f727902000a5f5f646174615f656e6403010b5f5f686561705f6261736503020f6164645f74776f5f6e756d6265727300000a09010700200120006a0b",
   "name": "Add"
  }
 }
}

响应

{"response":{"application":{"name":"Add","uuid":"0xa9d57ac0f5046512"},"status":"success"}}

应用成功部署

当应用程序部署时,将返回一个惟一标识符,即 0xa9d57ac0f5046512 。 之后调用应用程序的函数时,须要记住/ 保存这个标识符。

以上部分是技术分析。接下来,让咱们看看如何经过 HTTP 调用一个应用的函数。

调用一个应用的函数

调用应用程序的函数不止局限于用户。 这篇文章解释了如何经过 Curl 等调用 Wasm 函数,你能够更好地理解请求和响应细节。

file

事实上,大多数时候,这些函数都是由机器编程调用的。至少,它们将经过网页浏览器或手机应用程序构建,并经过最终用户的“点击”来执行。

如今让咱们开始调用应用程序的函数。

将下面的 curl 命令复制并粘贴到终端中。也可使用相似于 Postman 这样的图形用户界面,来执行这个 HTTP 请求。

命令行ー Curl 语法示例

不要被下面的 --data 弄得晕头转向, 它其实是至关简单明了的。更多信息参见 HTTP POST 规范 。实际上,咱们只是调用函数 add_two_numbers 将两个数字相加并传入两个数字 [“2” ,“2”] ,指望返回值为 “4”

curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"request": {"application": {"storage": "file_system", "uuid": "0xa9d57ac0f5046512"},"function": {"name": "add_two_numbers", "arguments": ["2", "2"],"argument_types": ["i32", "i32"], "return_types": ["i32"]},"modules": ["rust"] }}' \
  http://13.54.168.1:8080/execute_wasm_function

GUI — Postman JSON 示例

{
 "request": {
  "application": {
   "storage": "file_system", 
   "uuid": "*0xa9d57ac0f5046512*"
  },
  "function": {
   "name": "*add_two_numbers*", 
   "arguments": ["*2*", "*2*"],
   "argument_types": ["i32", "i32"], 
         "return_types": ["i32"]
  },
  "modules": ["rust"] 
 }
}

响应

上述两种方法都将生成结果对象,以下所示。返回值为 "return_value":["4"],结果是正确的。

{
 "result": {
  "error_message": "",
  "gas": 0,
  "gas_used": 6,
  "return_value": [
   "4"
  ],
  "status": "Succeeded",
  "vm_snapshot": {
   "global": [
    [
     0,
     "0x0000000000100000"
    ],
    [
     1,
     "0x0000000000100000"
    ],
    [
     2,
     "0x0000000000100000"
    ]
   ]
  }
 },
 "service_name": "0xa9d57ac0f5046512_1578786333_add_two_numbers",
 "uuid": "0xa9d57ac0f5046512"
}

你可能会注意到,在返回数据中有一部分是 vm_snapshot vm_snapshot是什么呢?

虚拟机快照

虚拟机快照是由 Second State 的虚拟机(SSVM) 自己生成的数据。

最重要的是要记住 SSVM 自己是无状态的。 每次调用 SSVM 都会引发一个新的、干净的 SSVM 实例。

vm_snapshot 数据容许整体系统存储 SSVM 的最新已知状态。 经过存储这个 vm_snapshot 信息,咱们能够确保 SSVM 可以从上次没完成的地方继续。 使用这种方法,能够在下一次执行期间恢复 SSVM 的最后已知状态。

咱们在本文开头提到,终端用户并不须要了解系统的内部工做机制。 简单地说,若是最终用户重复调用一个函数,系统将表明它们处理全部的vm_snapshot (VM 状态)。

有状态的 Wasm 执行即服务

本文 demo 了一个简单的有状态 Wasm 执行环境。在这个环境中,不管是终端用户或机器均可以使用每一个discrete Wasm 函数的逻辑进行交互。 仅经过网络就可使用 HTTP POST 请求。

这个演示只使用了简单的应用程序函数行 add_two_numbers 添加两个数字,但固然,您能够按照需求自由编写任何逻辑。

若是您对本文中的技术有任何疑问或须要任何帮助,请 GitHubSecond State 联系。

相关文章
相关标签/搜索