荷畔微風 - 在函數計算FunctionCompute中使用WebAssembly

WebAssembly 是一種新的W3C規範,無需插件能夠在全部現代瀏覽器中實現近乎原生代碼的性能。同時因爲 WebAssembly 運行在輕量級的沙箱虛擬機上,在安全、可移植性上比原生進程更加具有優點。同時資源消耗小、啓動速度快的特色也很是適合Serverless的場景。開發者們開始探索WebAssembly在Serverless的應用場景。html

WebAssembly 101

WebAssembly (WASM) 是一種能夠在Web瀏覽器上運行的編譯語言(如C/C++, Rust, Go)的技術方案。WebAssembly採用二進制字節碼格式,運行在基於堆棧的虛擬機上。2017年2月28日,四大主流瀏覽器Chrome, Firefox, Safari和IE共同宣佈 WebAssembly 的最小可行產品(MVP)已經完成。node

相比JavaScript, 使用WebAssembly能夠更高效地在Web瀏覽器中運行代碼邏輯。git

  1. 下載更快:相比相似功能的JavaScript代碼,WebAssembly文件體積更小
  2. 解析更快:因爲WebAssembly採用二進制的中間表示(Intermediate Representation),能夠類比JVM的byte code。無需代碼解析過程,能夠實現毫秒級解碼加載。
  3. 執行更快:因爲 JavaScript 使用動態類型,編譯器很難對代碼實現深度優化;而WebAssembly採用靜態類型,編譯器能夠高效優化;同時JavaScript採用垃圾回收機制,而WebAssembly指令更接近機器碼,須要程序本身控制垃圾回收策略,相比自動垃圾回收效率能夠更高。一般而言,WASM與JavaScript相比,能夠實現30%性能提高,在複雜的計算邏輯中,會有更高的性能提高。同時因爲減小了語言的動態性和採用靜態內存處理,WebAssembly也更加適合要求可預測性能的計算場景。

WebAssembly做爲社區標準,具備良好的可移植性。WebAssembly代碼邏輯能夠一致地運行在不一樣瀏覽器實現中,並與JavaScript或瀏覽器對象進行交互調用。WebAssembly也能夠運行在非瀏覽器環境下。此外WebAssembly運行在一個沙箱化的執行環境中,嚴格遵照瀏覽器安全策略,具備良好的安全性。github

函數計算 + WebAssembly 模塊

Function as a Service(FaaS)是Serverless Computing的重要計算形態,它提供了事件驅動的編程模型,開發者只需編寫和上傳事件響應代碼,而平臺則會負責計算資源的彈性伸縮。web

FaaS主要支持了 Node.js, Python, PHP等解釋型語言,也支持Java, C#等編譯型語言。因爲在Node.js 8.0版本之後已經內置了WebAssembly運行時,在主流的FaaS環境能夠經過Node.js實現調用WebAssembly模塊的能力,好比在AWS LambdaCloudflare Workernpm

在FaaS中利用Node.js調用WebAssembly 模塊,有如下優勢編程

  1. 更好的性能 - 尤爲是計算密集型任務
  2. 可移植性 - 爲NPM編譯原生代碼是一個大坑,在FaaS中運行的原生代碼則須要針對目標執行環境進行構建來保障兼容性。好比AWS Lambda中的原生代碼須要依賴Amazon Linux構建;阿里雲函數計算要求二進制代碼基於Linux 4.4構建。而WebAssembly代碼能夠保證在不一樣的目標環境運行有一致的結果。
  3. 多語言支持 - 相似C/C++, Rust, Golang這樣的已有業務代碼能夠經過編譯成WebAssembly進行復用,好比能夠直接將C編寫的圖像處理應用運行在函數計算中,而不受函數計算支持的現有運行時的限制。

咱們也將作一個小實驗,結合Rust和AssemblyScript兩種語言在阿里雲函數計算(FunctionCompute, FC)場景中體驗WebAssembly。瀏覽器

環境準備

  • 安裝並配置阿里雲函數計算的 Serverless 應用部署的工具 Fun
  • 下載示例代碼,git clone https://github.com/denverdino/fun-wasm
  • (可選) Rust環境須要安裝 wasm-pack

利用Rust構建Serverless WebAssembly應用

Rust是Mozilla的一個新的系統級編程語言。Mozilla同時也是WebAssembly技術最重要的推進者,它基於WebAssembly發佈了wasm-bindgen,目的提高 JavaScript 和 Rust 之間的互操做性,可讓 Rust代碼可以與JavaScript一塊兒使用。安全

本節參考了Scott Logic的文章,咱們將利用wasm-bindgen,在FunctionCompute的Node.js運行時中運行基於Rust編譯的WebAssembly代碼。網絡

$ cd rust-wasm

# 一個簡單的Rust Hello World應用,利用wasm_bindgen聲明與JavaScript代碼的綁定
$ cat src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn hello_world() -> String {
  let mut string = String::new();
  string.push_str("Hello, rust-wasm!");
  return string;
}

# 編譯生成WebAssembly和NodeJS綁定
$ wasm-pack build --target nodejs

# 簡單的函數計算事件響應代碼,在NodeJS中調用WebAssembly邏輯
$ cat index.js
const wasm = require("./pkg/rust_wasm");
var getRawBody = require('raw-body')
module.exports.handler = function (request, response, context) {
    // get request body
    getRawBody(request, function (err, body) {
        response.setStatusCode(200);
        response.send(wasm.hello_world());
    });
}

# Serverless應用部署模板,利用HTTP Trigger進行訪問
$ cat template.yml
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  fun:
    Type: 'Aliyun::Serverless::Service'
    rust-wasm:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        CodeUri: ./
        Description: 'http trigger demo!'
        Runtime: nodejs8
      Events:
        http-test:
          Type: HTTP
          Properties:
            AuthType: ANONYMOUS
            Methods: ['GET', 'POST', 'PUT']

部署並測試

$ fun deploy
...

Waiting for service fun to be deployed...
    Waiting for function rust-wasm to be deployed...
        Waiting for packaging function rust-wasm code...
        package function rust-wasm code done, the number of files you have packaged is:11
        Waiting for HTTP trigger http-test to be deployed...
        methods: [ 'GET', 'POST', 'PUT' ]
        url: https://xxxxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fun/rust-wasm/
        function http-test deploy success
    function rust-wasm deploy success
service fun deploy success


$ curl https://xxxxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fun/rust-wasm/
Hello, rust-wasm!

利用AssemblyScript構建Serverless WebAssembly應用

AssemblyScript能夠將一個 TypeScript 嚴格的子集編譯成 WebAssembly。AssemblyScript 使用與 TypeScript 相同的語法,但使用了本身的標準庫來支撐 WebAssembly 的功能,這意味着開發者沒必要爲了編寫 WebAssembly 而去學習新的編程語言,這是一個巨大的優勢。並且AssemblyScript是針對WebAssembly設計的語言,它能夠生成更加簡練的WebAssembly代碼,並更加簡單地與JavaScript集成交互。

$ cd assemblyscript

# 下面是一個簡單的Fibonacci數列的遞歸實現
$ cat assembly/index.ts
// The entry file of your WebAssembly module.

export function fib(n: i32): i32 {
  let t: i32;
  let a: i32 = 0;
  let b: i32 = 1;
  for (let i: i32 = 0; i < n; i++) {
    t = a + b;
    a = b;
    b = t;
  }
  return b;
}

# 生成WebAsssembly目標代碼
$ npm run asbuild

# 以下是AssemblyScript本身生成的膠水代碼,用於加載WebAssembly模塊
$ cat  wasm.js
const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {
  env: {
    abort(_msg, _file, line, column) {
       console.error("abort called at index.ts:" + line + ":" + column);
    }
  }
};
Object.defineProperty(module, "exports", {
  get: () => new WebAssembly.Instance(compiled, imports).exports
});

# 事件響應代碼,在NodeJS中調用WebAssembly邏輯,根據query string輸入參數計算Fibonacci結果
$ cat index.js
const { fib } = require('./wasm');
var getRawBody = require('raw-body')
module.exports.handler = function (request, response, context) {
    // get request body
    getRawBody(request, function (err, body) {
        let strValue = request.queries.n
        let n = 10 // Default value
        if (strValue) {
            n = parseInt(strValue)
           }
        response.setStatusCode(200);
        result = fib(n)
        response.send(result.toString());
    });
};

# Serverless應用部署模板,利用HTTP Trigger進行訪問
$ cat template.yml
ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  fun:
    Type: 'Aliyun::Serverless::Service'
    assemblyscript:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        CodeUri: ./
        Description: 'http trigger demo!'
        Runtime: nodejs8
      Events:
        http-test:
          Type: HTTP
          Properties:
            AuthType: ANONYMOUS
            Methods: ['GET', 'POST', 'PUT']

部署並測試

$ fun deploy
...
Waiting for service fun to be deployed...
    Waiting for function assemblyscript to be deployed...
        Waiting for packaging function assemblyscript code...
        package function assemblyscript code done, the number of files you have packaged is:1040
        Waiting for HTTP trigger http-test to be deployed...
        methods: [ 'GET', 'POST', 'PUT' ]
        url: https://xxxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fun/assemblyscript/
        function http-test deploy success
    function assemblyscript deploy success
service fun deploy success

$ curl https://xxxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fun/assemblyscript/\?n\=10
89

$ curl https://xxxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/fun/assemblyscript/\?n\=40
165580141

展望將來

WebAssembly仍是一個很是年輕的技術,大量的功能有待完善,可是它已經展示出巨大的潛力,逐漸在非Web領域獲得應用,如區塊鏈的智能合約實現。Mozilla在19年3月推出了一個標準化的 WebAssembly System Interface(WebAssembly系統接口,簡稱 WASI),來定義WASM與系統資源的交互,好比文件系統訪問,內存管理,網絡鏈接等。軟件開發商能夠針對具體的操做系統和運行環境提供不一樣的接口實現。它的目標是在不一樣設備和操做系統上運行相同的 WebAssembly 代碼,這與Java 提出的 「build once run anywhere」 目標有些相似,可是WebAssembly + WASI徹底基於開放生態,並提供更好的可移植性和安全性。

將來隨着WebAssembly自身技術和工具鏈的成熟,WebAssembly將有機會成爲一個跨語言、跨平臺的Serverless runtime,進一步推進雲原生應用的進化。社區也開始在相關領域進行探索,好比Fastly公司爲其邊緣計算平臺推出 Lucet 項目,它針對Serverless場景提供了優化的WebAssembly編譯器和運行時,能夠將實例化 WebAssembly 模塊的時間下降到數十微妙,內存開銷下降到只需幾KB。

WebAssembly和WASI等技術遠非完美,它們還在快速迭代演進中,期待它給咱們帶來更多驚喜。

 

本文做者:易立

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索