從瀏覽器內的角度來看,Wasm 最近的開發工做,理所固然地受到了普遍好評。在上一篇文章《只需5分鐘,教你如何編寫並執行一個 Rust + WebAssembly 程序》,咱們對 Rust 到 Wasm 的編譯以及簡單的瀏覽器內 Wasm 執行的案例作了演示。html
在另一篇文章《區塊鏈、硬件與面向服務的架構,WASM 即將迎來大爆發?》,裏面有絕佳的瀏覽器內的 WASM 應用程序示例,並輔以了對WebAssembly(Wasm)的詳細解釋。java
Wasm 不只僅是瀏覽器的字節碼。 Wasm 有着史無前例的強大的可移植性、高效率和靈活性。所以,咱們如今能夠作到,以多種不一樣語言編寫瀏覽器內 Wasm 應用程序,發展到在全部設備上分發 Wasm 獨立功能單元,在這一點上取得飛躍。python
Wasm 執行環境能夠包括最少的 shell、移動設備、臺式機和物聯網設備。Wasm 可能會推進從微芯片乃至整個數據中心,這全部一切的發展(Webassembly.org,2019)。
當鏈接到現代 Web 服務時,咱們並不是僅僅與一臺機器進行交互,而是持續和後臺可能數千臺機器進行交互( Arpaci-Dusseau 和Arpaci-Dusseau,2018 )。
網站越複雜,運營成本就越高。散佈在分佈式系統上的微服務須要盡最大可能作到簡單、高效和可靠。對於 Facebook、Google 這種大公司來講,這些特性意味着能夠節省大量能耗,進而節省成本,促成積極成果。linux
除了這些能輕易作到的,咱們還應該積極試驗,以找到方法來改善 Wasm 最終用戶/消費者體驗。 eBay 就是一個很好的例子。ios
利用 Wasm,eBay 最近很好地完善了其移動條形碼掃描儀的實現,恰到好處地爲客戶提供了最優服務( Tech.ebayinc.com,2019)。
首先咱們須要瞭解下「抽象化」。git
雖然操做系統抽象化對於構建分佈式系統來講是一個糟糕的選擇,但編程語言抽象化卻更具意義。 (阿帕奇-杜索和阿帕奇-杜索,2018)。Wasm 做爲從一開始就使用形式語義設計的第一種主流編程語言,進一步地提供了對現代硬件的抽象化的支持(Rossberg等,2018)。
Wasm 容許在最大量的源代碼語言中編寫和共享每一個單個功能的邏輯。Wasm符合咱們熟知的最佳軟件原則和慣例(DRY 和 KISS),並提供了必要時在全部設備之間轉換可執行代碼的方法。github
從進程間通訊(IPC)角度來看,最主要的抽象化是基於遠程程序調用(Remote Procedure Call)的概念,簡稱 RPC。(Arpaci-Dusseau和Arpaci-Dusseau,2018)。web
要實現這種分佈式機器之間廣泛存在的互操做性,須要具有容許任何應用程序(以任何語言編寫)直接從任何其餘分佈式機器調用服務的功能,就好像它只是調用本身的本地對象同樣。 這正是遠程過程調用 (RPC) 技術所實現的。shell
本文的目標是使用 Wasm 和 RPC 在 web 上執行與語言無關的通用代碼。編程
在下一節中,會講解如何:
安裝 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs (https://sh.rustup.rs/) | shsource $HOME/.cargo/env
建立新的 Rust 項目
cd ~ cargo new --lib add_numbers_via_wavm cd add_numbers_via_wavm
編輯 Cargo.toml 文件; 添加 lib 部分 ,同時也添加依賴項,以下所示
[lib] name = "adding_lib" path = "src/adding.rs" crate-type =["cdylib"][dependencies] serde_json = "1.0"
在命令行中添加必要的 Wasm 軟件和配置
rustup target add wasm32-wasi rustup override set nightly
建立一個名爲 ~/.cargo/config 的新文件。並將如下構建文本放入這個新建立的配置文件中。
[build] target = "wasm32-wasi"
編寫一個咱們能夠調用的有不一樣的功能的定製的 Rust 程序。
在下面的例子中,函數「 double」和「 triple」 會分別取一個整數並分別乘以 2 和 3。
use serde_json::json; pub extern fn print(answer: i32){ let the_answer = json!({ "Result": answer}); println!("{}", the_answer.to_string()); } #[no_mangle] pub extern fn double(x: i32){ let z = x * 2; print(z); } #[no_mangle] pub extern fn triple(x: i32){ let z = x * 3; print(z); }
可使用如下命令編譯上面的代碼
cargo build --release
這裏給你們推薦一個簡潔的 C++ RPC 服務器,叫作 [rpcsrv] (https://github.com/jgarzik/rp...。 咱們要使用這個 C++ RPC 服務器來接受 HTTP POST 並經過 C++ 將它們轉換爲系統調用。
sudo apt-get update sudo apt-get -y upgrade sudo apt-get install autoconf sudo apt install libevent-2.1-6 sudo apt-get install libevent-dev sudo apt-get install libtool cd ~ git clone https://github.com/jgarzik/rpcsrv.git cd ~/rpcsrv git submodule update --init ./autogen.sh ./configure make sudo make install
使用如下命令開啓 RPC 服務
sudo ./rpcsrvd --listen-port 8080
在咱們進一步討論以前,我想簡單地討論一下 JSON 的使用。 我簡要地探索了一個關於單值的絕妙概念。 Univalue 是一個高性能的 RAII C++ JSON 庫和通用值對象類。我以後會找時間針對這個作完全的研究。
方便起見,我結合使用了 UniValue 和 rapidjson。 一樣,我也須要更多的時間來研究,來找到數據交換和互操做性的最佳方法,咱們以後再進行討論。
下面的代碼用於安裝 rapidjson。
cd ~ https://github.com/Tencent/rapidjson.git cd ~/rapidjson git submodule update --init mkdir build cd ~/rapidjson/build/ cmake .. sudo cp -rp ~/rapidjson/include/rapidjson /usr/include/
在安裝 rapidjson 以後,我修改了原始 C++ API 文件,以便在 rpcsrvcodebase 中包含 rapidjson 功能。
#include "rapidjson/document.h" #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" #include <iostream> using namespace rapidjson;
在這個階段,咱們能夠繼續在 C++ 代碼中使用 rapidjson 功能。 下面是一個示例,演示如何修改原始 echo 函數
// // RPC "echo" // static UniValue myapi_1_echo(const UniValue & jreq, const UniValue & params) { // Receive and parse incoming parameters string s = params.write(); const char * theJson = s.c_str(); Document d; d.Parse(theJson); // Assign parameters to C++ variables and print to console Value & theService = d["Service Name"]; Value & theType = d["Type"]; Value & theFunctionName = d["Execution"]["FunctionName"]; Value & theArgument = d["Execution"]["Argument"]; cout << endl; cout << "Received a new request to execute a service on Wasm Virtual Machine..." << endl; cout << s.c_str() << endl; cout << endl; cout << "Service Name is: " << theService.GetString() << endl; cout << "Service Type is: " << theType.GetString() << endl; cout << "Wasm function is: " << theFunctionName.GetString() << endl; cout << "Wasm function argument: " << theArgument.GetString() << endl; // Construct and execute the call to Wasm Virtual Machine string commandString = "wavm run --abi=wasi --function="; commandString += theFunctionName.GetString(); commandString += " "; commandString += "~/add_numbers_via_wavm/target/wasm32 wasi / release / adding_lib.wasm "; commandString += " "; commandString += theArgument.GetString(); cout << "\n"; cout << "Executing command ... " << endl; string theWasmResults = execute_function_and_return_output(commandString); // Print the result to console cout << "Results are as follows ...: " << endl; cout << theWasmResults << endl; UniValue result(theWasmResults); cout << "Finished." << endl; // Return the results back to the caller of the RPC return jrpcOk(jreq, result); }
WAVM 使用 LLVM將 WebAssembly 代碼編譯成機器代碼,其性能接近原生性能。
下面是安裝 WAVM的說明
sudo apt-get install gcc sudo apt-get install clang wget https://github.com/WAVM/WAVM/releases/download/nightly%2F2019-11-04/wavm-0.0.0-prerelease-linux.deb sudo apt install ./wavm-0.0.0-prerelease-linux.deb
執行能夠由任何可以生成 HTTP POST 的機制執行。 例如,從 Postman這樣的 GUI 到 Linux curl 命令,固然還有像 Python 和 Java 這樣的解釋和編譯代碼庫。
下面是在 linux 命令行中使用 Curl 的調用代碼示例
Curl - 傳入一段有效的JSON代碼
tpmccallum$ curl --header "Content-Type: application/json" --request POST --data '{"jsonrpc":"2.0","method":"echo","params":{"Service Name": "Double the digits","Type": "Execution","Execution": {"FunctionName": "double","Argument": "10"}}, "id":1}' [http://123.456.78.9:8080/rpc/1](http://localhost:8080/rpc/1)
當查看這個調用代碼時,請記住 Rust 程序( Wasm 最先緣起於 Rust) 有兩個函數: 「 double」和「 triple」。 增長的 RPC 層意味着這些原始函數如今被定義爲兩個單獨的服務。
正如上面所看到的,咱們不只要指定想調用的服務,還要指定所需的單個參數。 當這個 POST 在 web 上執行時,RPC 服務器直接調用 WAVM,而後返回一個 JSON 結果對象給調用代碼。
返回有效的 JSON
{ "jsonrpc": "2.0", "result": { "Result": 20 }, "id": 1 }
返回對象是徹底可配置的,這只是一個返回計算結果的簡單示例。
RPC 服務器輸出
RPC 服務器輸出是可選的,這裏只是爲了演示而建立的。 這裏演示了 RPC 服務器能夠來回傳遞 JSON。其餘格式也有機會內置到 RPC 層(位於 Rust 和 Wasm 代碼之上)。
Received a new request to execute a service on Wasm Virtual Machine... {"Service Name":"Double the digits","Type":"Execution","Execution":{"FunctionName":"double","Argument":"10"}}Service Name is: Double the digits Service Type is: Execution Wasm function is: double Wasm function argument: 10Executing command ...Results are as follows ...: {"Result":20}Finished.
Python - 傳入一段有效的JSON代碼
系統設置
sudo apt-get install python-pip pip install json-rpc pip install requests
咱們將 Python 傳入一段有效的JSON代碼,描述咱們須要哪一種服務。 在這個例子中,咱們但願將數字10翻一倍,即調用「 FunctionName」 : 「 double」和「 Argument」 : 「10」。
>>>import requests >>>import json >>>url = "http://123.456.78.9:8080/rpc/1" >>>payload = { "jsonrpc":"2.0","method":"echo","params":{"Service Name":"Double the digits","Type": "Execution","Execution": {"FunctionName": "double","Argument": "10"}}, "id":1 } >>>response = requests.post(url, json=payload).json()
如今咱們能夠看到,響應返回執行 Wasm 的結果,即「 Result」 : 20。
>>> print response{u'jsonrpc': u'2.0', u'result': u'{"Result":20}, u'id': 1}
咱們能夠經過調用另外一個服務(即「 FunctionName」 : 「 triple」 ,「 Argument」 : 「10」)再次嘗試這個方法
>>>url = "http://123.456.78.9:8080/rpc/1" >>>payload = { "jsonrpc":"2.0","method":"echo","params":{"Service Name":"Triple the digits","Type": "Execution","Execution": {"FunctionName": "triple","Argument": "10"}}, "id":1 } >>>response = requests.post(url, json=payload).json()
一樣,咱們能夠看到這個響應是所選服務的正確結果。
>>> print response {u'jsonrpc': u'2.0', u'result': u'{"Result":30}', u'id': 1}
本文經過 RPC 演示瞭如何使用 Wasm 。我是一名熱情的開源區塊鏈軟件研究人員,也是 SecondState 公司(在達拉斯、奧斯汀、北京和臺北設有辦公室)的核心開發 https://www.SecondState.io/ 。 若是你想了解更多關於 Wasm 和其餘能夠提高業務的技術,請經過電子郵件或 [GitHub] (https://github.com/second-sta... 與咱們聯繫。