中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Node.js 實現輕量級云函數功能

發布時間:2021-06-03 16:55:30 來源:億速云 閱讀:317 作者:Leah 欄目:web開發

今天就跟大家聊聊有關 Node.js 實現輕量級云函數功能,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

一、什么是云函數?

云函數是誕生于云服務的一個新名詞,顧名思義,云函數就是在云端(即服務端)執行的函數。各個云函數相互獨立,簡單且目的單一,執行環境相互隔離。使用云函數時,開發者只需要關注業務代碼本身,其它的諸如環境變量、計算資源等,均由云服務提供。

二、為什么需要云函數?

程序員說不想買服務器,于是便有了云服務;
程序員又說連 server 都不想寫了,于是便有了云函數。

Serverless 架構

通常我們的應用,都會有一個后臺程序,它負責處理各種請求和業務邏輯,一般都需要跟網絡、數據庫等 I/O 打交道。而所謂的無服務器架構,就是把除了業務代碼外的所有事情,都交給執行環境處理,開發者不需要知道 server 怎么跑起來,數據庫的 api 怎么調用——一切交給外部,在“溫室”里寫代碼即可。

FaaS

而云函數,正是 serverless 架構得以實現的途徑。我們的應用,將是一個個獨立的函數組成,每一個函數里,是一個小粒度的業務邏輯單元。沒有服務器,沒有 server 程序,“函數即服務”(Functions as a Service)。

三、如何實現?

由于本實現是應用在一個 CLI 工具里面的,函數聲明在開發者的項目文件里,因而大致過程如下:

Node.js 實現輕量級云函數功能

1、函數聲明與存儲聲明

我們的目標是讓云函數的聲明和一般的 js 函數沒什么兩樣:

module.exports = async function (ctx) {
  return 'hahha'
 }
};

由于云函數的執行通常伴隨著接口的調用,所以應該要能支持聲明 http 方法:

module.exports = {
 method: 'POST',
 handler: async function (ctx) {
  return 'hahha'
 }
};

存儲

由于有 method 等配置,因此編譯的時候,需要把上述聲明文件 require 進來,此時,handler 字段是一個 Function 類型的對象。可以調用其 toString 方法,得到字符串類型的函數體:

const f = require('./func.js');
const method = f.method;
const body = f.handler.toString();
// async function (ctx) {
// return 'hahha'
// }

有了字符串的函數體,存儲就很簡單了,直接存在數據庫 string 類型的字段里即可。

2、函數執行

url

如果用于前端調用,每個云函數需要有一個對應的 url,以上述聲明文件的文件名為云函數的唯一名稱的話,可以簡單將 url 設計為:

/f/:funcname

構造獨立作用域(重點)

在 js 世界里,執行一個字符串類型的函數體,有以下這么一些途徑:

  • eval 函數

  • new Function

  • vm 模塊

那么要選哪一種呢?讓我們回顧云函數的特點:各自獨立,互不影響,運行在云端。

關鍵是將每個云函數放在一個獨立的作用域執行,并且沒有訪問執行環境的權限,因此,最優選擇是 nodejs 的 vm 模塊。關于該模塊的使用,可參考官方文檔。

至此,云函數的執行可以分為三步:

  • 從數據庫獲取函數體

  • 構造 context

// ctx 為 koa 的上下文對象 
const sandbox = {
  ctx: {
   params: ctx.params,
   query: ctx.query,
   body: ctx.request.body,
   userid: ctx.userid,
  },
  promise: null,
  console: console
 }
 vm.createContext(sandbox);

執行函數得到結果

const code = `func = ${funcBody}; promise = func(ctx);`;
vm.runInContext(code, sandbox);
const data = await sandbox.promise;

NPM社區的 vm2 模塊針對 vm 模塊的一些安全缺陷做了改進,也可用此模塊,思路大抵相同。

3、引用

雖然說原則上云函數應當互相獨立,各不相欠,但是為了提高靈活性,我們還是決定支持函數間的相互引用,即可以在某云函數中調用另外一個云函數。

聲明

很簡單,加個函數名稱的數組字段就好:

module.exports = {
 method: 'POST',
 use: ['func1', 'func2'],
 handler: async function (ctx) {
  return 'hahha'
 }
};

注入

也很簡單,根據依賴鏈把函數都找出來,全部掛載在 ctx 下就好,深度優先或者廣度優先都可以。

if (func.use) {
  const funcs = {};
  const fnames = func.use;
  for (let i = 0; i < fnames.length; i++) {
    const fname = fnames[i];
    await getUsedFuncs(ctx, fname, funcs);
  }

  const funcCode = `{
    ${Object.keys(funcs).map(fname => `${fname}:${funcs[fname]}`).join('\n')}
  }`;

  code = `ctx.methods=${funcCode};$[code]`;
} else {
  code = `ctx.methods={};$[code]`;
}

// 獲取所有依賴的函數
const getUsedFuncs = async (ctx, funcName, methods) => {
  const func = getFunc(funcName);
  methods[funcName] = func.body;
  if (func.use) {
    const uses = func.use.split(',');
    for (let i = 0; i < uses.length; i++) {
      await getUsedFuncs(ctx,uses[i], methods);
    }
  }
}

依賴循環

既然可以相互依賴,那必然會可能出現 a→b→c→a 這種循環的依賴情況,所以需要在開發者提交云函數的時候,檢測依賴循環。

檢測的思路也很簡單,在遍歷依賴鏈的過程中,每一個單獨的鏈條都記錄下來,如果發現當前遍歷到的函數在鏈條里出現過,則發生循環。

const funcMap = {};
flist.forEach((f) => {
  funcMap[f.name] = f;
});

const chain = [];
flist.forEach((f) => {
  getUseChain(f, chain);
});

function getUseChain(f, chain) {
  if (chain.includes(f.name)) {
    throw new Error(`函數發生循環依賴:${[...chain, f.name].join('→')}`);
  } else {
    f.use.forEach((fname) => {
      getUseChain(funcMap[fname], [...chain, f.name]);
    });
  }
}

4、性能

上述方案中,每次云函數執行的時候,都需要進行一下幾步:

  • 獲取函數體

  • 編譯代碼

  • 構造作用域和獨立環境

  • 執行

步驟3,因為每次執行的參數都不一樣,也會有不同請求并發執行同一個函數的情況,所以作用域 ctx 無法復用;步驟4是必須的,那么可優化點就剩下了1和2。

代碼緩存

vm 模塊提供了代碼編譯和執行分開處理的接口,因此每次獲取到函數體字符串之后,先編譯成 Script 對象:

// ...get code
const script = new vm.Script(code);

執行的時候可以直接傳入編譯好的 Script 對象:

// ...get sandbox
vm.createContext(sandbox);
script.runInContext(sandbox);
const data = await sandbox.promise;

函數體緩存

簡單的緩存,不需要很復雜的更新機制,定一個時間閾值,超過后拉取新的函數體并編譯得到 Script 對象,然后緩存起來即可:

const cacheFuncs = {};
// ...get script
cacheFuncs[funcName] = {
  updateTime: Date.now(),
  script,
};

// cache time: 60 sec
const cacheFunc = cacheFuncs[cacheKey];

if (cacheFunc && (Date.now() - cacheFunc.updateTime) <= 60000) {
  const sandbox = { /*...*/ }
  vm.createContext(sandbox);
  cacheFunc.script.runInContext(sandbox);
  const data = await saandbox.promise;
  return data;
} else {
  // renew cache
}

看完上述內容,你們對 Node.js 實現輕量級云函數功能有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

东方市| 温州市| 开化县| 呼和浩特市| 巫山县| 平舆县| 伊川县| 蚌埠市| 交口县| 思茅市| 石台县| 长治县| 体育| 铜梁县| 商都县| 通江县| 静乐县| 临高县| 浪卡子县| 泸州市| 萨迦县| 达日县| 望奎县| 大渡口区| 弥勒县| 汉阴县| 库车县| 宣武区| 湾仔区| 巴青县| 东安县| 罗源县| 中宁县| 临武县| 交城县| 溆浦县| 天津市| 邵东县| 双江| 惠安县| 阳朔县|