成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

基于 Node.js 的輕量級云函數(shù)功能實(shí)現(xiàn)

noONE / 906人閱讀

摘要:各個(gè)云函數(shù)相互獨(dú)立,簡單且目的單一,執(zhí)行環(huán)境相互隔離。而云函數(shù),正是架構(gòu)得以實(shí)現(xiàn)的途徑。獲取所有依賴的函數(shù)依賴循環(huán)既然可以相互依賴,那必然會可能出現(xiàn)這種循環(huán)的依賴情況,所以需要在開發(fā)者提交云函數(shù)的時(shí)候,檢測依賴循環(huán)。

導(dǎo)語

在萬物皆可云的時(shí)代,你的應(yīng)用甚至不需要服務(wù)器。云函數(shù)功能在各大云服務(wù)中均有提供,那么,如何用“無所不能”的 node.js 實(shí)現(xiàn)呢?

一、什么是云函數(shù)?

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

二、為什么需要云函數(shù)?

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

Serverless 架構(gòu)

通常我們的應(yīng)用,都會有一個(gè)后臺程序,它負(fù)責(zé)處理各種請求和業(yè)務(wù)邏輯,一般都需要跟網(wǎng)絡(luò)、數(shù)據(jù)庫等 I/O 打交道。而所謂的無服務(wù)器架構(gòu),就是把除了業(yè)務(wù)代碼外的所有事情,都交給執(zhí)行環(huán)境處理,開發(fā)者不需要知道 server 怎么跑起來,數(shù)據(jù)庫的 api 怎么調(diào)用——一切交給外部,在“溫室”里寫代碼即可。

FaaS

而云函數(shù),正是 serverless 架構(gòu)得以實(shí)現(xiàn)的途徑。我們的應(yīng)用,將是一個(gè)個(gè)獨(dú)立的函數(shù)組成,每一個(gè)函數(shù)里,是一個(gè)小粒度的業(yè)務(wù)邏輯單元。沒有服務(wù)器,沒有 server 程序,“函數(shù)即服務(wù)”(Functions as a Service)。

三、如何實(shí)現(xiàn)?

由于本實(shí)現(xiàn)是應(yīng)用在一個(gè) CLI 工具里面的,函數(shù)聲明在開發(fā)者的項(xiàng)目文件里,因而大致過程如下:

1、函數(shù)聲明與存儲 聲明

我們的目標(biāo)是讓云函數(shù)的聲明和一般的 js 函數(shù)沒什么兩樣:

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

由于云函數(shù)的執(zhí)行通常伴隨著接口的調(diào)用,所以應(yīng)該要能支持聲明 http 方法:

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

由于有 method 等配置,因此編譯的時(shí)候,需要把上述聲明文件 require 進(jìn)來,此時(shí),handler 字段是一個(gè) Function 類型的對象??梢哉{(diào)用其 toString 方法,得到字符串類型的函數(shù)體:

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

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

2、函數(shù)執(zhí)行 url

如果用于前端調(diào)用,每個(gè)云函數(shù)需要有一個(gè)對應(yīng)的 url,以上述聲明文件的文件名為云函數(shù)的唯一名稱的話,可以簡單將 url 設(shè)計(jì)為:

/f/:funcname
構(gòu)造獨(dú)立作用域(重點(diǎn))

在 js 世界里,執(zhí)行一個(gè)字符串類型的函數(shù)體,有以下這么一些途徑:

eval 函數(shù)

new Function

vm 模塊

那么要選哪一種呢?讓我們回顧云函數(shù)的特點(diǎn):各自獨(dú)立,互不影響,運(yùn)行在云端
關(guān)鍵是將每個(gè)云函數(shù)放在一個(gè)獨(dú)立的作用域執(zhí)行,并且沒有訪問執(zhí)行環(huán)境的權(quán)限,因此,最優(yōu)選擇是 nodejs 的 vm 模塊。關(guān)于該模塊的使用,可參考官方文檔。
至此,云函數(shù)的執(zhí)行可以分為三步:

從數(shù)據(jù)庫獲取函數(shù)體

構(gòu)造 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);

執(zhí)行函數(shù)得到結(jié)果

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

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

3、引用

雖然說原則上云函數(shù)應(yīng)當(dāng)互相獨(dú)立,各不相欠,但是為了提高靈活性,我們還是決定支持函數(shù)間的相互引用,即可以在某云函數(shù)中調(diào)用另外一個(gè)云函數(shù)。

聲明

很簡單,加個(gè)函數(shù)名稱的數(shù)組字段就好:

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

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

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("
")}
    }`;

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

// 獲取所有依賴的函數(shù)
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);
        }
    }
}
依賴循環(huán)

既然可以相互依賴,那必然會可能出現(xiàn) a→b→c→a 這種循環(huán)的依賴情況,所以需要在開發(fā)者提交云函數(shù)的時(shí)候,檢測依賴循環(huán)。
檢測的思路也很簡單,在遍歷依賴鏈的過程中,每一個(gè)多帶帶的鏈條都記錄下來,如果發(fā)現(xiàn)當(dāng)前遍歷到的函數(shù)在鏈條里出現(xiàn)過,則發(fā)生循環(huán)。

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(`函數(shù)發(fā)生循環(huán)依賴:${[...chain, f.name].join("→")}`);
    } else {
        f.use.forEach((fname) => {
            getUseChain(funcMap[fname], [...chain, f.name]);
        });
    }
}
4、性能

上述方案中,每次云函數(shù)執(zhí)行的時(shí)候,都需要進(jìn)行一下幾步:

獲取函數(shù)體

編譯代碼

構(gòu)造作用域和獨(dú)立環(huán)境

執(zhí)行

步驟3,因?yàn)槊看螆?zhí)行的參數(shù)都不一樣,也會有不同請求并發(fā)執(zhí)行同一個(gè)函數(shù)的情況,所以作用域 ctx 無法復(fù)用;步驟4是必須的,那么可優(yōu)化點(diǎn)就剩下了1和2。

代碼緩存

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

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

執(zhí)行的時(shí)候可以直接傳入編譯好的 Script 對象:

// ...get sandbox
vm.createContext(sandbox);
script.runInContext(sandbox);
const data = await sandbox.promise;
函數(shù)體緩存

簡單的緩存,不需要很復(fù)雜的更新機(jī)制,定一個(gè)時(shí)間閾值,超過后拉取新的函數(shù)體并編譯得到 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
}
四、參考資料 相關(guān)文章

什么是Serverless(無服務(wù)器)架構(gòu)?

業(yè)界的 serverless

騰訊云 - 無服務(wù)云函數(shù)
阿里云 - 函數(shù)計(jì)算
AWS - Lambda
Azure - Azure Functions

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105385.html

相關(guān)文章

  • 第三方庫

    摘要:微信支付,支付寶支付,銀聯(lián)支付三大支付總結(jié)支付寶植入總結(jié)支付寶的植基于和百度地圖的組件庫基于百度地圖封裝的組件庫,使用這個(gè)庫最好需要先了解和百度地圖。 Commento - 多說 & Disqus 開源替代品 Commento - 多說 & Disqus 開源替代品 anime.js 簡單入門教程 強(qiáng)大輕量的動(dòng)畫庫 anime.js 入門教程 來自B站的開源的MagicaSakura源...

    seanHai 評論0 收藏0
  • 第三方庫

    摘要:微信支付,支付寶支付,銀聯(lián)支付三大支付總結(jié)支付寶植入總結(jié)支付寶的植基于和百度地圖的組件庫基于百度地圖封裝的組件庫,使用這個(gè)庫最好需要先了解和百度地圖。 Commento - 多說 & Disqus 開源替代品 Commento - 多說 & Disqus 開源替代品 anime.js 簡單入門教程 強(qiáng)大輕量的動(dòng)畫庫 anime.js 入門教程 來自B站的開源的MagicaSakura源...

    gityuan 評論0 收藏0
  • 什么是Node.js

    Node.js從2009年誕生至今,已經(jīng)發(fā)展了兩年有余,其成長的速度有目共睹。從在github的訪問量超過Rails,到去年底Node.jsS創(chuàng)始人Ryan Dalh加盟Joyent獲得企業(yè)資助,再到今年發(fā)布Windows移植版本,Node.js的前景獲得了技術(shù)社區(qū)的肯定。InfoQ一直在關(guān)注Node.js的發(fā)展,在今年的兩次Qcon大會(北京站和杭州站)都有專門的講座。為了更好地促進(jìn)Node.j...

    CrazyCodes 評論0 收藏0
  • 2015 年度小結(jié)(技術(shù)方面)

    摘要:因?yàn)槁酚蓪用媸軜I(yè)務(wù)影響很大,經(jīng)常修改一些功能的行為,所以后來大部分測試都是針對層面的單元測試。在我了解的過程中,我發(fā)現(xiàn)中文網(wǎng)絡(luò)上對的討論非常分散,于是我創(chuàng)建了中文社區(qū),到年末已經(jīng)有個(gè)注冊用戶和個(gè)帖子了。 https://jysperm.me/2016/02/programming-of-2015/ 從 2014 年末開始開發(fā)的一個(gè)互聯(lián)網(wǎng)金融項(xiàng)目終于在今年三月份上線了,這是一個(gè) Node...

    宋華 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<