自建faas利器之安全運行環境

書接上文

上次談到了如何搭建本身的faas?,被同行評論有些標題黨,這篇文章將接着上文,來介紹一些乾貨。同時在寫這篇文章的時候,核心功能vmbox已經開源,歡迎你們點贊fork。javascript

faas是雲廠商提出的一種函數即服務的程序部署模式,以函數爲核心,實現以函數粒度的服務伸縮,這項技術很是複雜,有不少的技術資料,你們能夠查看雲廠商的文檔。咱們這裏聊到的是比較簡單的實現方式,以傳統服務爲基礎,借用nodejs的沙盒能力,實現安全執行用戶的函數,達到函數即服務的目的前端

原由

相信你們項目中都會遇到多個項目使用同一個函數,該函數具備規則複雜邏輯獨立等特色的純函數,前端每每是發佈一個npm包,直接引入,粗暴點的直接複製到另外一個項目中。假設校驗身份證件號的真僞等函數,有時後端也須要用,因爲語言的差別,直接複用不太可能,將這類函數抽象成爲單獨的函數的想法便應運而生,決定嘗試最前沿的編程思想(前端的世界就是瘋狂試探),自建faas工程就這樣開始了。java

目標

將js函數轉化爲API服務,即實現函數即服務node

安全執行環境vmbox

實現上述目標的核心難題是js函數的安全執行環境,上一期已經談到node的沙盒存在的問題,這裏再也不贅述。具體的解決方案,已經開源,你們能夠點擊查看vmbox。對源碼感興趣的同窗能夠對照上篇文章中的流程圖查看代碼,歡迎貢獻。git

迴歸正題,vmbox是通過真實項目檢驗的node沙盒庫,具備六大特色github

  • 死循環強制退出(一旦執行超過指定時間,可能存在死循環,kill子進程,釋放資源)
  • 跨進程函數調用(使用IPC跨進程調用函數)
  • 函數互相調用(藉助context實現函數間調用)
  • 內部任務隊列(子進程忙碌狀態,任務腳本進入隊列等待)
  • 進程自治(殺死自啓動)
  • 返回promise(編程體驗上只須要做爲一個異步任務便可)

vmbox的實例只有一個run方法,返回值是一個promise,接收三個參數數據庫

參數名 類型 是否選填 默認值 簡介
code string 必填 - 運行的js代碼
context object 選填 {} 函數運行上下文
stack boolean 選填 false 函數內調用其餘函數,記錄函數調用棧

若是代碼運行出錯,會使用Promise.reject(error)拋出異常,須要對異常進行捕獲npm

基本用法編程

const VMBox = require('vmbox');
const vmBox = new VMBox({
  timeout: 100,
  asyncTimeout: 500
});

const context = {
  sum(a, b){
    return a + b;
  }
}

const fn = `sum(2, 3)`

vmBox.run(fn).then(console.log)
// 打印5
複製代碼

高級用法後端

藉助函數運行上下文,能夠作不少事情,下面實現了一個從函數內部調用其餘函數的方法。

const VMBox = require('vmbox');
const vmBox = new VMBox({
  timeout: 100,
  asyncTimeout: 500
});

const fnGroup = {
  sum: `async function main({params, fn}){ const {a, b} = params; return a + b }`,
  caller: `async function main({params, fn}){ return await fn.call('sum', params); }`
};

async function run(code, context, stack = false) {
  const runCode = code + `;\n(async () => { return await main({params, fn}); })()`
  return vmBox.run(runCode, context, stack);
}

const fn = {
  call: (name, params) => {
    const code = fnGroup[name];
    if (code) {
      return run(code, { params, fn }, true);
    } else {
      return null;
    }
  }
}

const context = {
  fn,
  params: {
    a: 10,
    b: 20
  }
}

const code = fnGroup.caller;
try {
  const res = await run(code, context);
  console.log(res); // 打印30
} catch (error) {
  console.log(error);
}
複製代碼

關注點

  • vmbox可以解決vm2異步死循環的問題,可是進程啓動的成本比較高,所以在使用過程當中儘可能避免進程的重啓,提升運行效率。
  • context是函數的上下文,能夠注入任何想提供給函數的功能,擴展函數的能力,好比:數據庫訪問能力、http能力等。
  • 關注代碼的同窗會注意到,爲何不採用多進程模型而採用單進程?這裏有兩個理由,一是node服務運行環境通常單核,單進程實用性更強,若是須要多進程,能夠採用node的cluster模塊封裝實現。二是須要實現函數間相互調用,如高級用法,多進程調用中存在死循環。
相關文章
相關標籤/搜索