日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

开源 Serverless 框架 Laf 性能优化实践

發(fā)布時間:2023/12/24 windows 61 coder
生活随笔 收集整理的這篇文章主要介紹了 开源 Serverless 框架 Laf 性能优化实践 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

介紹

Laf 是一個完全開源的 Serverless 框架,Laf 的 Node.js 運行時容器 (以下簡稱為 Runtime) 是 Laf 的函數(shù)執(zhí)行環(huán)境,依托于 Express.js 框架。采用容器進程常駐的方式,每一個應(yīng)用對應(yīng)于一個或多個容器 (彈性伸縮下),底層使用了 Node.js 的 vm 模塊,使用 MongoDB 的 watch() 方法來監(jiān)聽函數(shù)變更事件,以實現(xiàn)函數(shù)發(fā)布和配置發(fā)布。

Node.js vm 模塊

Node.js 的 vm 模塊是一個提供虛擬機功能的模塊,用于在 Node.js 環(huán)境中創(chuàng)建一個獨立的 JavaScript 執(zhí)行環(huán)境。它允許在應(yīng)用程序中運行和控制一段 JavaScript 代碼,同時提供了一些安全性隔離性

這個模塊包括一些可用于創(chuàng)建隔離的執(zhí)行環(huán)境的函數(shù),使得代碼能夠在獨立的上下文中運行,防止對主應(yīng)用程序的影響。這在某些情況下可以提供更高的安全性,例如在沙盒環(huán)境中執(zhí)行用戶提供的代碼,或者實現(xiàn)一些動態(tài)加載執(zhí)行代碼的需求。

原文鏈接:https://forum.laf.run/d/1146

為什么要優(yōu)化

目前 Laf 的函數(shù)運行時存在以下問題:

  1. 頻繁使用 Node.js vm 模塊重復(fù)創(chuàng)建 vm,vm 創(chuàng)建執(zhí)行的過程中,CPU 消耗很高。在以下對 runtime 的 CPU 火焰圖分析可見,在函數(shù)執(zhí)行過程中,有兩部分 CPU 執(zhí)行時間較長,分別是輸出函數(shù)請求日志vm 創(chuàng)建執(zhí)行過程

  1. 有時候遇到復(fù)雜的函數(shù)嵌套引用的時候,會導(dǎo)致循環(huán)引用,內(nèi)存遲遲無法回收,造成內(nèi)存泄漏,導(dǎo)致 OOM Killed。
  2. 交由 runtime 自己通過 HTTP 調(diào)用的形式,異步請求持久化函數(shù)日志,性能損耗大,QPS 直接減半
  3. 函數(shù)引擎這塊的邏輯越來越復(fù)雜和臃腫,維護難度很大,急需重構(gòu)。

如何優(yōu)化

在前面的分析中,我們知道,當(dāng)前造成性能瓶頸的原因主要有兩點:

  1. 為了實現(xiàn)隔離,vm 模塊重復(fù)創(chuàng)建,CPU 消耗高,特別是當(dāng)函數(shù)引用達(dá)到一定規(guī)模時。另一方面,復(fù)雜的引用下,甚至?xí)l(fā)生內(nèi)存難以回收造成內(nèi)存泄漏的問題。
  2. 頻繁打印函數(shù)請求日志,依賴單線程的 Node.js 通過異步請求處理 console.log 等日志,導(dǎo)致實際業(yè)務(wù)請求吞吐量下降。

因此,我們采用以下優(yōu)化思路:

  1. 日志方面:使用標(biāo)準(zhǔn)輸出的形式輸出日志,交由 K8s 自己采集日志,而不由 runtime 自己處理。

  2. 函數(shù)引擎方面:第一次函數(shù)調(diào)用時,構(gòu)建并緩存函數(shù)模塊,下次調(diào)用直接取出使用,不需要重復(fù)編譯,這塊更改需要確保以下因素:

    1. 保證這個緩存的函數(shù)模塊是無狀態(tài),即 y = f(x),輸入相同的 x,則必然輸出確定的 y。
    2. 函數(shù)發(fā)布時,要及時清理緩存的函數(shù)模塊。

優(yōu)化前后架構(gòu)對比分析

  • 優(yōu)化前:

  • 優(yōu)化后:

優(yōu)化步驟

  1. 改造日志方案為容器日志標(biāo)準(zhǔn)輸出,交由 K8s 收集,完全去除日志的有狀態(tài)依賴。
  2. 重構(gòu)函數(shù)引擎,建立函數(shù)模塊,每一個函數(shù)模塊的導(dǎo)出都是一個 JS 對象,無論是代碼還是引用的第三方包,都被視作為一個 Module,在代碼中只會存在一份,等同于原生的 require / export:
    1. 簡化代碼,盡可能復(fù)用,保留核心邏輯;
    2. 去除函數(shù)模塊中的有狀態(tài)部分;
    3. 在函數(shù)執(zhí)行、函數(shù)引入處建立函數(shù)模塊緩存
  3. 針對調(diào)試模式,每次函數(shù)執(zhí)行時重新構(gòu)建函數(shù)模塊,主動收集執(zhí)行日志。

核心函數(shù)調(diào)用邏輯

const vm = require('vm')

// 函數(shù)列表
const functionList = {
    a: "const b = require('b'); const func = () => b(); module.exports = func",
    b: "module.exports = () => 'hello world'"
}

// 函數(shù)模塊緩存
const functionModuleCache = new Map()

// 構(gòu)建函數(shù)模塊
const buildFunctionModule = (name) => {
    // 自定義 require 邏輯,用來加載函數(shù)
    const customRequire = (specifier) => {
        if (functionModuleCache.has(specifier)) {
            return functionModuleCache.get(specifier)
        }
        if(functionList[specifier]) {
            return buildFunctionModule(specifier)
        }
        return require(specifier)
    }
    
    // 全局上下文
    const ctx = {
        __require: customRequire,
        module: {
            exports: {},
        }
    }

    // 重新定義 require
    const wrapCode = code => {
        return `
        const require = (name) => {
            return __require(name)
        }

        ${code}
        module.exports;
        `
    }
    
    // 構(gòu)建模塊
    const script = new vm.Script(wrapCode(functionList[name]))
    const mod = script.runInNewContext(ctx)
    // 緩存構(gòu)建結(jié)果
    functionModuleCache.set(name, mod)
    return mod
}

// 簡單寫一個入口函數(shù)
const main = () => {
    const func = buildFunctionModule('a')
    const res = func()
    console.log(res)
}

main()

優(yōu)化效果

壓測

下面以 Laf 應(yīng)用最低配置 0.1c 128m 為例進行壓測。

  1. 常規(guī) HTTP 請求:

    數(shù)據(jù)量 測試結(jié)果 QPS
    10 并發(fā)請求 1000 次 110
    100 并發(fā)請求 1000 次 122
  2. WebSocket 連接

    每秒創(chuàng)建 100 個 websocket 連接,當(dāng)創(chuàng)建 1 萬個 websocket 連接時,資源占用情況如下:

真實案例

某個跑在 laf 上的應(yīng)用,日活數(shù)十萬,原來需要 4 個 G 的內(nèi)存,優(yōu)化后,內(nèi)存降至 512 MB 以下,CPU 只需要不到 1 核

附加彩蛋

除此之外,我們還做了不少額外的工作:

  1. 日志支持根據(jù)不同 Level,以不同的顏色輸出。
  2. 通過重定向自定義依賴安裝路徑,現(xiàn)在支持安裝和內(nèi)置依賴版本不同的依賴包。
  3. 攔截器現(xiàn)在支持類似 koa 洋蔥圈結(jié)構(gòu)的前攔截和后攔截的寫法,詳情查看 Laf 文檔。
  4. ...

總結(jié)

通過優(yōu)化 Laf 運行時,我們在將每個應(yīng)用的成本降低至原來的 1/10 的同時,還大大提高了性能和穩(wěn)定性,成功把 Laf 的價格打了下來 ~

總結(jié)

以上是生活随笔為你收集整理的开源 Serverless 框架 Laf 性能优化实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。