自建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模块封装实现。二是须要实现函数间相互调用,如高级用法,多进程调用中存在死循环。
相关文章
相关标签/搜索