本文 demo 了终端用户以及机器用户如何在只使用 HTTP 请求的情况下,通过 web 从 Wasm 函数中找到答案。 对于更喜欢冒险的读者,本文还 demo了如何在相同的基础结构上编写和部署 Wasm 可执行文件。
在之前的文章,我们谈到,虽然 Wasm 在客户端确实很受欢迎,但 Wasm 最近也成为了服务器端技术和服务的有力竞争者。
基于这个想法,《去中心化计算的未来:通过 RPC 从微服务过渡到 WASM》一文提出:未来,分布式计算微服务将会由传统的微服务向 Wasm 基础设施过渡。 这其中的原因有很多。 其中之一就是,通过 Wasm ,在大多数源代码语言和本地硬件之间可以编写和共享单独的 discrete 函数的逻辑。
因此,不可避免地,这意味着 Wasm 将适用于大多数应用程序,即使 Wasm 在后端,也可以有效地执行一组管理良好的 discrete 函数来为每个应用程序服务。
当然,这种乌托邦式的分布式计算模式需要一些基础,我们可以在这样的基础上建立自己的定制业务和企业软件。
虽然我们的目标看似遥不可及,但实际上是可以实现的。在服务端的 WebAssembly 领域,出现了一些令人无比激动的,能够为我们“铺路”的基础设施。
Second State 最近构建了一套软件,使部署和执行服务器端的 Wasm 变得非常方便。
下面的图表显示了构成这个系统的一些组件,即:
Second State 研发的 SSVM 有一个核心的优势,就是使用者不需要知道它的内部工作机制。事实上,要使用 SSVM 执行服务器端的 Wasm,您只需要发出一个简单的 HTTP 请求。
下文将演示“调用应用程序的函数” ,但在此之前,让我们先花几分钟的时间进行一次快速的技术深入讨论。
本节将展示如何在 SSVM 上创建和部署自己的 Wasm 可执行文件。我们将在 Ubuntu 操作系统上使用 Rust,创建和部署 Demo。
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
将以下内容添加到 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
}
rustup target add wasm32-wasi
rustup override set nightly
cargo build --release --target=wasm32-wasi
上面的集合将在 target/wasm32-wasi/release/add_lib.wasm
创建新的Wasm文件。这个文件我们将部署在 SecondState 的 Wasm 基础设施 SSVM 上。
在部署这个应用程序时,我们将遵循这个特定的 HTTP POST 规范。
如果想查看新创建的 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)))
您将注意到原始的 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 应用程序的示例。
注意我们手动添加的 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
如果使用 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 函数,你可以更好地理解请求和响应细节。
事实上,大多数时候,这些函数都是由机器编程调用的。至少,它们将通过网页浏览器或手机应用程序构建,并通过最终用户的“点击”来执行。
现在让我们开始调用应用程序的函数。
将下面的 curl 命令复制并粘贴到终端中。也可以使用类似于 Postman 这样的图形用户界面,来执行这个 HTTP 请求。
不要被下面的 --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
{
"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 状态)。
本文 demo 了一个简单的有状态 Wasm 执行环境。在这个环境中,无论是终端用户或机器都可以使用每个discrete Wasm 函数的逻辑进行交互。 仅通过网络就可以使用 HTTP POST 请求。
这个演示只使用了简单的应用程序函数行 add_two_numbers
添加两个数字,但当然,您可以按照需求自由编写任何逻辑。
如果您对本文中的技术有任何疑问或需要任何帮助,请 GitHub与 Second State 联系。