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

介绍

Laf 是一个完全开源的 Serverless 框架,Laf 的 Node.js 运行时容器 (以下简称为 Runtime) 是 Laf 的函数执行环境,依托于 Express.js 框架。采用容器进程常驻的方式,每一个应用对应于一个或多个容器 (弹性伸缩下),底层使用了 Node.js 的 vm 模块,使用 MongoDB 的 watch() 方法来监听函数变更事件,以实现函数发布和配置发布。

Node.js vm 模块

Node.js 的 vm 模块是一个提供虚拟机功能的模块,用于在 Node.js 环境中创建一个独立的 JavaScript 执行环境。它允许在应用程序中运行和控制一段 JavaScript 代码,同时提供了一些安全性隔离性

这个模块包括一些可用于创建隔离的执行环境的函数,使得代码能够在独立的上下文中运行,防止对主应用程序的影响。这在某些情况下可以提供更高的安全性,例如在沙盒环境中执行用户提供的代码,或者实现一些动态加载执行代码的需求。

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

为什么要优化

目前 Laf 的函数运行时存在以下问题:

  1. 频繁使用 Node.js vm 模块重复创建 vm,vm 创建执行的过程中,CPU 消耗很高。在以下对 runtime 的 CPU 火焰图分析可见,在函数执行过程中,有两部分 CPU 执行时间较长,分别是输出函数请求日志vm 创建执行过程

  1. 有时候遇到复杂的函数嵌套引用的时候,会导致循环引用,内存迟迟无法回收,造成内存泄漏,导致 OOM Killed。
  2. 交由 runtime 自己通过 HTTP 调用的形式,异步请求持久化函数日志,性能损耗大,QPS 直接减半
  3. 函数引擎这块的逻辑越来越复杂和臃肿,维护难度很大,急需重构。

如何优化

在前面的分析中,我们知道,当前造成性能瓶颈的原因主要有两点:

  1. 为了实现隔离,vm 模块重复创建,CPU 消耗高,特别是当函数引用达到一定规模时。另一方面,复杂的引用下,甚至会发生内存难以回收造成内存泄漏的问题。
  2. 频繁打印函数请求日志,依赖单线程的 Node.js 通过异步请求处理 console.log 等日志,导致实际业务请求吞吐量下降。

因此,我们采用以下优化思路:

  1. 日志方面:使用标准输出的形式输出日志,交由 K8s 自己采集日志,而不由 runtime 自己处理。

  2. 函数引擎方面:第一次函数调用时,构建并缓存函数模块,下次调用直接取出使用,不需要重复编译,这块更改需要确保以下因素:

    1. 保证这个缓存的函数模块是无状态,即 y = f(x),输入相同的 x,则必然输出确定的 y。
    2. 函数发布时,要及时清理缓存的函数模块。

优化前后架构对比分析

  • 优化前:

  • 优化后:

优化步骤

  1. 改造日志方案为容器日志标准输出,交由 K8s 收集,完全去除日志的有状态依赖。
  2. 重构函数引擎,建立函数模块,每一个函数模块的导出都是一个 JS 对象,无论是代码还是引用的第三方包,都被视作为一个 Module,在代码中只会存在一份,等同于原生的 require / export:
    1. 简化代码,尽可能复用,保留核心逻辑;
    2. 去除函数模块中的有状态部分;
    3. 在函数执行、函数引入处建立函数模块缓存
  3. 针对调试模式,每次函数执行时重新构建函数模块,主动收集执行日志。

核心函数调用逻辑

const vm = require('vm')// 函数列表
const functionList = {a: "const b = require('b'); const func = () => b(); module.exports = func",b: "module.exports = () => 'hello world'"
}// 函数模块缓存
const functionModuleCache = new Map()// 构建函数模块
const buildFunctionModule = (name) => {// 自定义 require 逻辑,用来加载函数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: {},}}// 重新定义 requireconst wrapCode = code => {return `const require = (name) => {return __require(name)}${code}module.exports;`}// 构建模块const script = new vm.Script(wrapCode(functionList[name]))const mod = script.runInNewContext(ctx)// 缓存构建结果functionModuleCache.set(name, mod)return mod
}// 简单写一个入口函数
const main = () => {const func = buildFunctionModule('a')const res = func()console.log(res)
}main()

优化效果

压测

下面以 Laf 应用最低配置 0.1c 128m 为例进行压测。

  1. 常规 HTTP 请求:

    数据量测试结果QPS
    10 并发请求 1000 次110
    100 并发请求 1000 次122
  2. WebSocket 连接

    每秒创建 100 个 websocket 连接,当创建 1 万个 websocket 连接时,资源占用情况如下:

真实案例

某个跑在 laf 上的应用,日活数十万,原来需要 4 个 G 的内存,优化后,内存降至 512 MB 以下,CPU 只需要不到 1 核

附加彩蛋

除此之外,我们还做了不少额外的工作:

  1. 日志支持根据不同 Level,以不同的颜色输出。
  2. 通过重定向自定义依赖安装路径,现在支持安装和内置依赖版本不同的依赖包。
  3. 拦截器现在支持类似 koa 洋葱圈结构的前拦截和后拦截的写法,详情查看 Laf 文档。
  4. ...

总结

通过优化 Laf 运行时,我们在将每个应用的成本降低至原来的 1/10 的同时,还大大提高了性能和稳定性,成功把 Laf 的价格打了下来 ~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/222361.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

el-table自定义表格数据

如上所示: 表格内的数据是:当前班级所在名次段的人数 / 当前班级1至n名的累计人数 5/12 也就是 5/75 需要变更为: 截至到当前名次段总人数(上次考试) / 截至到当前名次段总人数(本次考试&#xff09…

阿里云RDS MySQL 数据如何快速同步到 ClickHouse

云数据库 RDS MySQL 和 云数据库 ClickHouse 是阿里云推出的两个备受欢迎的数据库解决方案,它们为用户提供了可靠的数据存储方案、分析数仓方案,本文介绍如何快速将 RDS MySQL 的数据同步到云数据库 ClickHouse。 如何快速将RDSMySQL的数据同步到云数据库…

数据结构与算法之美学习笔记:35 | Trie树:如何实现搜索引擎的搜索关键词提示功能?

目录 前言什么是“Trie 树”?如何实现一棵 Trie 树?Trie 树真的很耗内存吗?Trie 树与散列表、红黑树的比较解答开篇内容小结 前言 本节课程思维导图: 搜索引擎的搜索关键词提示功能,我想你应该不陌生吧?为…

css3 clip-path剪切图片

大致看了一下,反正以后用到就慢慢调吧 剪切四个角 clip-path: polygon(14px 0, calc(100% - 14px) 0, 100% 14px, 100% calc(100% - 14px), calc(100% - 14px) 100%, 14px 100%, 0 calc(100% - 14px), 0 14px); 三角形 clip-path: polygon(50% 0,0 100%, 100% 100…

案例058:基于微信小程序的智能社区服务管理系统

文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…

【C语言】SCU安全项目1-FindKeys

目录 前言 命令行参数 16进制转字符串 extract_message1 process_keys12 extract_message2 main process_keys34 前言 因为这个学期基本都在搞CTF的web方向,C语言不免荒废。所幸还会一点指针相关的知识,故第一个安全项目做的挺顺利的&#xff0c…

C语言--有一个3*4的矩阵,求出其中最大值的那个元素的值,以及其所在的行号和列号

一.题目描述 有一个3*4的矩阵,要求求出其中最大值的那个元素的值,以及其所在的行号和列号 比如:给定一个3*4的矩阵如下 输出结果:最大值为 12 ,行号为3, 列号为2 二.思路分析 打擂台算法: 先思考…

VRRP(虚拟路由冗余协议)

一.VRRP简介 1.VRRP是什么 Virtual route Redundancy Protocol,也叫虚拟路由器冗余协议。 利用VRRP,一组路由器协同工作,单只有一个处于Master状态,处于该状态的路由器(的接口)承担实际的数据流量转发任…

微信小程序uniapp记住密码

记住密码功能 在请求登录接口成功后,我们需要判断用户是否勾选记住密码,如果是,则将记住密码状态、账号信息存入本地。 下次登录时,获取本地的记住密码状态,如果为true则获取本地存储的账号信息,将信息回填…

循环验证表单信息

1.需求 要求在提交申请时校验每个地址使用信息的必填项是否填写完整 2.最终效果 3.具体操作 <el-dialog v-model"data.applyVisible" title"申请地址" center destroy-on-close><el-button type"primary" click"handleTabsAdd&…

【docker】docker入门与安装

Docker 一、入门 Docker的主要目标是&#xff1a;Build, Ship and Run Any App, Anywhere&#xff0c;也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理&#xff0c;使用户的APP及其运行环境能做到一次镜像,处处运行。 Docker运行速度快的原因 Docker有比虚拟…

图论专栏一《图的基础知识》

图论&#xff08;Graph Theory&#xff09;是数学的一个分支。它以图为研究对象。图论中的图是由若干给定的点及连接两点的线所构成的图形&#xff0c;这种图形通常用来描述某些实体之间的某种特定关系&#xff0c;用点代表实体&#xff0c;用连接两点的线表示两个实体间具有的…

实验06:VLAN配置

1.实验目的&#xff1a; VLAN&#xff08;Virtual Local Area Network&#xff09;是一种通过逻辑方式而不是物理方式划分局域网的技术&#xff0c;可以提高网络性能、安全性和管理效率。VLAN的划分方法有基于端口、基于MAC地址、基于协议和基于IP组播等。VLAN之间的通信需要路…

Oracle数据库本地部署结合内网穿透实现公网环境PLSQL远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

什么是纯净IP?如何判断IP地址的纯净度?有哪些干净IP推荐?

您是否想知道什么使代理“干净”或如何确保您的代理不会将您列入网站的黑名单&#xff1f;对于通过代理访问网络的人来说&#xff0c;干净的代理是无缝在线体验的重要组成部分。在这篇文章中&#xff0c;我们将深入研究干净代理的世界&#xff0c;并探讨决定其质量的因素。 一、…

SiteGround SITE TOOLS主机站点工具设置教程

当你使用SiteGround搭建WordPress或WooCommerce网站后&#xff0c;你会经常登录到两个不同的网站后台&#xff1a;一个是SiteGround的Site Tools后台&#xff0c;用于进行网站的安全、速度优化、FTP工具和网站备份等技术操作&#xff1b;另一个是WordPress网站后台&#xff0c;…

C# 字符串格式化

写在前面 在日常编程中&#xff0c;经常需要对字符串进行格式化操作&#xff0c;以便呈现为不同的格式&#xff0c;满足各种各样的显示需求&#xff0c;C#的字符串格式化参数是非常丰富的&#xff0c;这里做个简单的列举&#xff0c;以供后续参考和延伸。 代码实现 var curr…

vite+vue3+electron搭建项目

编辑器使用vscode&#xff0c;打开一个空文件夹 第一步 初始化vite项目 初始化vite项目&#xff0c;命令 npm init vite 第二步 下载依赖 进入新建的项目&#xff0c;下载依赖&#xff0c;命令 cd vite-projec npm i第三步 使用cnpm下载 electron依赖 新建一个终端&#…

HTTP 408错误:请求超时,如何避免

大家好&#xff0c;今天我们来聊聊一个常见的问题——HTTP 408错误&#xff0c;也就是请求超时。这个错误就像是一个网络中的时间旅行者&#xff0c;总是不期而至&#xff0c;让人摸不着头脑。但是别担心&#xff0c;我有一些方法可以帮助你避免这个错误。 首先&#xff0c;我…

Electron 跨平台打包

最近利用 Electron 制作跨平台安装包&#xff0c;记录步骤&#xff0c;踩坑多多。 首先&#xff0c;一步步搭建项目 一、搭建环境 初始化 package.json&#xff0c;这里要求 node 版本不低于14.16&#xff0c;我用的 v14.16.0&#xff0c;16版本在 Linux 下容易出现安装依赖…