使用 WebAssembly 设计和构建理想化的分布式计算解决方案

使用 WebAssembly 设计和构建理想化的分布式计算解决方案_第1张图片

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

背景

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

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

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

当然,这种乌托邦式的分布式计算模式需要一些基础,我们可以在这样的基础上建立自己的定制业务和企业软件。

SSVM 概览

虽然我们的目标看似遥不可及,但实际上是可以实现的。在服务端的 WebAssembly 领域,出现了一些令人无比激动的,能够为我们“铺路”的基础设施。

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

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

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

使用 WebAssembly 设计和构建理想化的分布式计算解决方案_第2张图片

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 客户端

使用 WebAssembly 设计和构建理想化的分布式计算解决方案_第3张图片

如果使用 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 函数,你可以更好地理解请求和响应细节。

使用 WebAssembly 设计和构建理想化的分布式计算解决方案_第4张图片

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

现在让我们开始调用应用程序的函数。

将下面的 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 添加两个数字,但当然,您可以按照需求自由编写任何逻辑。

如果您对本文中的技术有任何疑问或需要任何帮助,请 GitHub与 Second State 联系。

你可能感兴趣的:(WebAssembly,Wasm,微服务,Rust)