如何經過網絡遠程執行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 聯繫。

相關文章
相關標籤/搜索