从浏览器内的角度来看,Wasm最近的开发工作,理所当然地受到了广泛好评。在上一篇文章,我们对Rust到Wasm的编译以及简单的浏览器内Wasm执行的案例做了演示。
在另外一篇文章《区块链、硬件与面向服务的架构,WASM即将迎来大爆发?》,里面有绝佳的浏览器内的WASM应用程序示例,并辅以了对WebAssembly(Wasm)的详细解释。
浏览器之外Wasm不仅仅是浏览器的字节码。Wasm有着前所未有的强大的可移植性、高效率和灵活性。因此,我们现在可以做到,以多种不同语言编写浏览器内Wasm应用程序,发展到在所有设备上分发Wasm独立功能单元,在这一点上取得飞跃。
为什么跨越浏览器很重要?网站越复杂,运营成本就越高。散布在分布式系统上的微服务需要尽最大可能做到简单、高效和可靠。对于Facebook、Google这种大公司来说,这些特性意味着可以节省大量能耗,进而节省成本,促成积极成果。
除了这些能轻易做到的,我们还应该积极试验,以找到方法来改善Wasm最终用户/消费者体验。eBay就是一个很好的例子。
为什么选Wasm?首先我们需要了解下“抽象化”。
Wasm允许在最大量的源代码语言中编写和共享每个单个功能的逻辑。Wasm符合我们熟知的最佳软件原则和惯例(DRY和KISS),并提供了必要时在所有设备之间转换可执行代码的方法。
为什么要进行远程过程调用(RemoteProcedureCall)?从进程间通信(IPC)角度来看,最主要的抽象化是基于远程程序调用(RemoteProcedureCall)的概念,简称RPC。(Arpaci-Dusseau和Arpaci-Dusseau,2018)。
要实现这种分布式机器之间普遍存在的互操作性,需要具备允许任何应用程序(以任何语言编写)直接从任何其他分布式机器调用服务的功能,就好像它只是调用自己的本地对象一样。这正是远程过程调用(RPC)技术所实现的。
本文的目标是使用Wasm和RPC在web上执行与语言无关的通用代码。
在下一节中,会讲解如何:
编写自定义的Rust代码并编译为Wasm
设置RPC服务器
在RPC服务器上定义自定义服务
安装Wasm虚拟机(WAVM)
通过HTTPPost(即Curl,Python等)远程执行自定义WebAssembly(Wasm)代码
1.编写自定义的Rust代码并编译为Wasm安装Rust
curl--proto'=https'--()|shsource$HOME/.cargo/env
创建新的Rust项目
cd~cargonew--libadd_numbers_via_wavmcdadd_numbers_via_wavm
编辑文件;添加lib部分,同时也添加依赖项,如下所示
[lib]name="adding_lib"path="src/"crate-type=["cdylib"][depencies]serde_json="1.0"
在命令行中添加必要的Wasm软件和配置
rustuptargetaddwasm32-wasirustupoverridesetnightly
创建一个名为~/.cargo/config的新文件。并将以下构建文本放入这个新创建的配置文件中。
[build]target="wasm32-wasi"
编写一个我们可以调用的有不同的功能的定制的Rust程序。在下面的例子中,函数“double”和“triple”会分别取一个整数并分别乘以2和3。
useserde_json::json;pubexternfnprint(answer:i32){letthe_answer=json!({"Result":answer});println!("{}",the__string());}[no_mangle]pubexternfntriple(x:i32){letz=x*3;print(z);}可以使用以下命令编译上面的代码
cargobuild--release2.设置RPC服务器
这里给大家推荐一个简洁的C++RPC服务器,叫做rpcsrv(链接:)。我们要使用这个C++RPC服务器来接受HTTPPOST并通过C++将它们转换为系统调用。
sudoapt-getupdatesudoapt-~gitclone
使用以下命令开启RPC服务
sudo./rpcsrvd--listen-port80803.在RPC服务器上定义自定义服务
在我们进一步讨论之前,我想简单地讨论一下JSON的使用。我简要地探索了一个关于单值的绝妙概念。Univalue是一个高性能的RAIIC++JSON库和通用值对象类。我之后会找时间针对这个做彻底的研究。
方便起见,我结合使用了UniValue和rapidjson。同样,我也需要更多的时间来研究,来找到数据交换和互操作性的最佳方法,我们之后再进行讨论。
下面的代码用于安装rapidjson。
cd~
在安装rapidjson之后,我修改了原始C++API文件(链接:),以便在rpcsrvcodebase中包含rapidjson功能。
include"rapidjson/"includeiostreamusingnamespacerapidjson;
在这个阶段,我们可以继续在C++代码中使用rapidjson功能。下面是一个示例,演示如何修改修改原始echo函数(链接:)
////RPC"echo"//staticUniValuemyapi_1_echo(constUniValuejreq,constUniValueparams){//Receiveandparseincomingparametersstrings=();constchar*theJson=_str();Documentd;(theJson);//AssignparameterstoC++variablesandprinttoconsoleValuetheService=d["ServiceName"];ValuetheType=d["Type"];ValuetheFunctionName=d["Execution"]["FunctionName"];ValuetheArgument=d["Execution"]["Argument"];coutl;cout"ReceivedanewrequesttoexecuteaserviceonWasmVirtualMachine"l;_str()l;coutl;cout"ServiceNameis:"()l;cout"ServiceTypeis:"()l;cout"Wasmfunctionis:"()l;cout"Wasmfunctionargument:"()l;//ConstructandexecutethecalltoWasmVirtualMachinestringcommandString="wavmrun--abi=wasi--function=";commandString+=();commandString+="";commandString+="~/add_numbers_via_wavm/target/wasm32wasi/release/adding_";commandString+="";commandString+=();cout"\n";cout"Executingcommand"l;stringtheWasmResults=execute_function_and_return_output(commandString);//Printtheresulttoconsolecout"Resultsareasfollows:"l;couttheWasmResultsl;UniValueresult(theWasmResults);cout"Finished."l;//ReturntheresultsbacktothecalleroftheRPCreturnjrpcOk(jreq,result);}4.安装Wasm虚拟机(WAVM)WAVM使用LLVM将WebAssembly代码编译成机器代码,其性能接近原生性能。
下面是安装WAVM的说明
sudoapt-getinstallgccsudoapt-getinstallclangwget5.通过HTTPPost(即Curl、Python等)远程执行自定义WebAssembly(Wasm)代码
执行可以由任何能够生成HTTPPOST的机制执行。例如,从Postman这样的GUI到Linuxcurl命令,当然还有像Python和Javal这样的解释和编译代码库。
下面是在linux命令行中使用Curl的调用代码示例
Curl-传入一段有效的JSON代码
tpmccallum$curl--header"Content-Type:application/json"--requestPOST--data'{"jsonrpc":"2.0","method":"echo","params":{"ServiceName":"Doublethedigits","Type":"Execution","Execution":{"FunctionName":"double","Argument":"10"}},"id":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代码之上)。
ReceivedanewrequesttoexecuteaserviceonWasmVirtualMachine{"ServiceName":"Doublethedigits","Type":"Execution","Execution":{"FunctionName":"double","Argument":"10"}}ServiceNameis:DoublethedigitsServiceTypeis:ExecutionWasmfunctionis:doubleWasmfunctionargument:10ExecutingcommandResultsareasfollows:{"Result":20}Finished.Python-传入一段有效的JSON代码
系统设置
sudoapt-getinstallpython-pippipinstalljson-rpcpipinstallrequests
我们将Python传入一段有效的JSON代码,描述我们需要哪种服务。在这个例子中,我们希望将数字10翻一倍,即调用“FunctionName”:“double”和“Argument”:“10”。
importrequestsimportjsonurl=""payload={"jsonrpc":"2.0","method":"echo","params":{"ServiceName":"Doublethedigits","Type":"Execution","Execution":{"FunctionName":"double","Argument":"10"}},"id":1}response=(url,json=payload).json()现在我们可以看到,响应返回执行Wasm的结果,即“Result”:20。
printresponse{u'jsonrpc':u'2.0',u'result':u'{"Result":20},u'id':1}我们调用另一个服务(即“FunctionName”:“triple”,“Argument”:“10”)再次尝试这个方法
url=""payload={"jsonrpc":"2.0","method":"echo","params":{"ServiceName":"Triplethedigits","Type":"Execution","Execution":{"FunctionName":"triple","Argument":"10"}},"id":1}response=(url,json=payload).json()同样,我们可以看到这个响应是所选服务的正确结果。
printresponse{u'jsonrpc':u'2.0',u'result':u'{"Result":30}',u'id':1}参考文献Arpaci-Dusseau,和Arpaci-Dusseau,,2018,《操作系统:三个简单的部分》,Arpaci-DusseauBooksLLC.
Rossberg,A.,Titzer,B.,Haas,A.,Schuff,D.,Gohman,D.,Wagner,L.,Zakai,A.,Bastien,J.以及Holman,M.(2018),《使用WebAssembly加速网络发展》,ACM通讯,107-115页.
(2019),eBay上的WebAssembly:一个真实世界的案例,[在线资源]可访问:[2019年11月20日访问].





