如何通過網絡遠程執行 WebAssembly 虛擬機


如何通過網絡遠程執行 WebAssembly 虛擬機


本文 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 虛擬機


Second State 研發的 SSVM 有一個核心的優勢,就是使用者不需要知道它的內部工作機制。事實上,要使用 SSVM 執行服務器端的 Wasm,您只需要發出一個簡單的 HTTP 請求。

下文將演示“調用應用程序的函數” ,但在此之前,讓我們先花幾分鐘的時間進行一次快速的技術深入討論。

技術解析

本節將展示如何在 SSVM 上創建和部署自己的 Wasm 可執行文件。我們將在 Ubuntu 操作系統上使用 Rust,創建和部署 Demo。

安裝Rust

sudo apt-get updatesudo apt-get -y upgradecurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shsource $HOME/.cargo/env
xxxxxxxxxxsudo apt-get updatesudo apt-get -y upgradecurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shsource $HOME/.cargo/env​

創建新的應用程序

cd ~cargo new --lib addcd add
xxxxxxxxxxcd ~cargo new --lib addcd add​

設置 Wasm 特定的系統配置

將以下內容添加到 Cargo.toml 文件

[lib]name = "add_lib"path = "src/lib.rs"crate-type =["cdylib"]
xxxxxxxxxx[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}
xxxxxxxxxx#[no_mangle]pub extern fn add_two_numbers(_x: i32, _y: i32) -> i32{_x + _y}​

Wasm 系統配置

rustup target add wasm32-wasirustup override set nightly
xxxxxxxxxxrustup target add wasm32-wasirustup override set nightly​

編譯到Wasm

cargo build --release --target=wasm32-wasi
xxxxxxxxxxcargo 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
xxxxxxxxxx./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 1local.get 0i32.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)))
xxxxxxxxxx(module(type (;0;) (func (param i32 i32) (result i32)))(func $add_two_numbers (type 0) (param i32 i32) (result i32)local.get 1local.get 0i32.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'
xxxxxxxxxxxxd -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
xxxxxxxxxx./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
xxxxxxxxxx0061736d0100000001070160027f7f017f030201000405017001010105030100100619037f01418080c0000b7f00418080c0000b7f00418080c0000b073704066d656d6f727902000a5f5f646174615f656e6403010b5f5f686561705f6261736503020f6164645f74776f5f6e756d6265727300000a09010700200120006a0b​ 

複製粘貼很容易了,對嗎?

部署應用

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
xxxxxxxxxxcurl --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 虛擬機


如果使用 GUI HTTP 客戶機發出 POST 請求,那麼下面是您傳入的等效 JSON。

同樣,請注意我們在字節碼開始時手動添加的 0x !

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

響應

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

應用成功部署

當應用程序部署時,將返回一個唯一標識符,即 0xa9d57ac0f5046512 。 以後調用應用程序的函數時,需要記住/ 保存這個標識符。

以上部分是技術分析。接下來,讓我們看看如何通過 HTTP 調用一個應用的函數。

調用一個應用的函數

調用應用程序的函數不止侷限於用戶。 這篇文章解釋瞭如何通過 Curl 等調用 Wasm 函數,你可以更好地理解請求和響應細節。


如何通過網絡遠程執行 WebAssembly 虛擬機


事實上,大多數時候,這些函數都是由機器編程調用的。至少,它們將通過網頁瀏覽器或手機應用程序構建,並通過最終用戶的“點擊”來執行。

現在讓我們開始調用應用程序的函數。

將下面的 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
xxxxxxxxxxcurl --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"]  }}
xxxxxxxxxx{ "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"} 
xxxxxxxxxx{ "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是什麼呢?

虛擬機快照

虛擬機快照是由 SSVM 本身生成的數據。

最重要的是要記住 SSVM 本身是無狀態的。 每次調用 SSVM 都會引起一個新的、乾淨的 SSVM 實例。

vm_snapshot 數據允許總體系統存儲 SSVM 的最新已知狀態。 通過存儲這個 vm_snapshot 信息,我們可以確保 SSVM 能夠從上次沒完成的地方繼續。 使用這種方法,可以在下一次執行期間恢復 SSVM 的最後已知狀態。

我們在本文開頭提到,終端用戶並不需要了解系統的內部工作機制。 簡單地說,如果最終用戶重複調用一個函數,系統將代表它們處理所有的vm_snapshot (VM 狀態)。

有狀態的 Wasm 執行即服務

本文 demo 了一個簡單的有狀態 Wasm 執行環境。在這個環境中,無論是終端用戶或機器都可以使用每個discrete Wasm 函數的邏輯進行交互; 僅通過網絡就可以使用 HTTP POST 請求。

這個演示只使用了簡單的應用程序函數行 add_two_numbers 添加兩個數字,但當然,您可以按照需求自由編寫任何邏輯。


分享到:


相關文章: