Netlify 中的 WebAssembly Serverless 函数
ztj100 2024-11-17 04:04 14 浏览 0 评论
Netlify 是一个开发和托管 Jamstack 应用的平台。实际上,Jamstack 是Netlify 的创始人 Mathias Biilmann 于 2015年造出来的一个词。 Netlify 也是 JamstackConf 的主要组织者。
Jamstack 应用程序由一个静态 UI (HTML 和 JavaScript) 和一系列 serverless 函数组成。动态 UI 元素由 JavaScript 向 serverless 函数获取数据生成。Jamstack 的好处有很多,但这其中最重要的好处之一是性能绝佳。由于 UI 不再从中心服务器的 runtime 生成,因此服务器上的负载要少得多,我们可以通过边缘网络(例如 CDN)部署 UI。
但是边缘 CDN 只解决了分发静态 UI 文件的问题。后端 serverless 函数可能仍然很慢。事实上,流行的 Serverless 平台存在众所周知的性能问题,例如冷启动缓慢,尤其是对于交互式应用程序而言。在这方面, WebAssembly 大有可为。
使用由 CNCF 托管的云原生 WebAssembly runtime WasmEdge ,开发者可以编写部署在公共云或边缘计算节点上的高性能serverless 函数。本文中,我们将探索如何使用 Rust 编写的 WasmEdge 函数来支持 Netlify 应用程序后端。
为什么用 WebAssembly 实现 Netlify 函数
Netlify 平台已经有一个非常易于使用的用于部署函数的 serverless 框架。正如上面讨论的,使用 WebAssembly 以及 WasmEdge 是为了进一步提高性能。用 C/C++、Rust 和 Swift 写的高性能函数可以轻松编译成 WebAssembly。这些 WebAssembly 函数比 serverless 函数中常用的 JavaScript 或 Python 快得多。
可是,如果原始性能是唯一的目标,为什么不直接将这些函数编译为机器本地可执行文件呢(本地客户端或者 NaCl)?这是因为 Netlify 已经在使用 Firecracker microVM 在 AWS Lambda 中安全地运行这些函数。
我们对未来的愿景是在云原生基础架构中将 WebAssembly 作为轻量级的 runtime 与 Docker 、 microVM 并列运行。与类似 Docker 容器或 microVM 相比,WebAssembly 提供更高的性能并消耗更少的资源。但就目前而言,Netlify 仅支持在 microVM 中运行 WebAssembly。
相比运行容器化 NaCl 程序,在 microVM 中运行 WebAssembly 函数有很多优势。
首先,WebAssembly 为独立的函数提供了细颗粒度的 runtime 隔离。一个微服务可以有多个函数,并支持在 microVM 中运行的服务。 WebAssembly 可以让微服务更安全、更稳定。
第二,WebAssembly 字节码是可移植的。开发者只需构建一次,无需担心未来底层 Netlify serverless runtime 的改变或更新。它还允许开发者在其它云环境中重复使用相同的 WebAssembly 函数。
第三, WebAssembly 应用很容易部署和管理。与 NaCl 动态库和可执行文件相比,它们具有更少的平台依赖性和复杂性。
最后, WasmEdge Tensorflow API 提供了最符合 Rust 规范的、执行 Tensorflow 模型的方式。WasmEdge 安装了 Tensorflow 依赖库的正确组合,并为开发者提供了统一的 API。
概念和解释说了很多,趁热打铁,我们来看看示例应用吧!
准备工作
由于我们的 demo WebAssembly 函数是用 Rust 编写的,因此您需要安装 Rust 编译器。确保按如下方式安装 wasm32-wasi 编译器目标,以生成 WebAssembly 字节码。
$ rustup target add wasm32-wasi
demo 应用前端是用 Next.js 写的,并且部署在 Netlify 上。我们假设你已经有使用 Next.js 和 Netlify 的基本知识了。
示例 1: 图片处理
我们的第一个 demo 应用程序是让用户上传图片,然后调用 serverless 函数将其变成黑白图片。 开始之前,你可以试用一下这个部署在 Netlify 上的 demo。
首先 fork demo 应用的 GitHub repo 。 要将该应用部署到 Netlify,只需将你的 GitHub repo 添加到 Netlify 上。
这个 repo 是 Netlify 平台的标准 Next.js 应用程序。后端 serverless 函数在 api/functions/image_grayscale 文件夹中。 src/main.rs 文件包含 Rust 程序的源代码。 Rust 程序从 STDIN 读取图片数据,然后将黑白图片输出到 STDOUT。
use hex;
use std::io::{self, Read};
use image::{ImageOutputFormat, ImageFormat};
fn main() {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf).unwrap();
let image_format_detected: ImageFormat = image::guess_format(&buf).unwrap();
let img = image::load_from_memory(&buf).unwrap();
let filtered = img.grayscale();
let mut buf = vec![];
match image_format_detected {
ImageFormat::Gif => {
filtered.write_to(&mut buf, ImageOutputFormat::Gif).unwrap();
},
_ => {
filtered.write_to(&mut buf, ImageOutputFormat::Png).unwrap();
},
};
io::stdout().write_all(&buf).unwrap();
io::stdout().flush().unwrap();
}
使用 Rust 的 cargo 工具将 Rust 程序构建为为 WebAssembly 字节码或者原生代码。
$ cd api/functions/image-grayscale/
$ cargo build --release --target wasm32-wasi
将 build artifacts 复制到 api 文件夹。
$ cp target/wasm32-wasi/release/grayscale.wasm ../../
Netlify 函数在设置 serverless 函数时运行 api/pre.sh 。 这时会安装 WasmEdge runtime,然后将 WebAssembly 字节码程序编译为一个本地的 so 库,从而更快地执行。
api/hello.js 文本加载 WasmEdge runtime,在 WasmEdge 中启动编译好的 WebAssembly 程序,并通过 STDIN 传递上传的图像数据。这里请注意, api/hello.js 会运行由 api/pre.sh 生成的编译好的 grayscale.so 文件,从而得到更好的性能。
const fs = require('fs');
const { spawn } = require('child_process');
const path = require('path');
module.exports = (req, res) => {
const wasmedge = spawn(
path.join(__dirname, 'wasmedge'),
[path.join(__dirname, 'grayscale.so')]);
let d = [];
wasmedge.stdout.on('data', (data) => {
d.push(data);
});
wasmedge.on('close', (code) => {
let buf = Buffer.concat(d);
res.setHeader('Content-Type', req.headers['image-type']);
res.send(buf);
});
wasmedge.stdin.write(req.body);
wasmedge.stdin.end('');
}
这样就完成了。 接下来将 repo 部署到Netlify ,就得到了一个 Jamstack 应用。该应用有着基于 Rust 和 WebAssembly 的高性能 serverless 后端。
示例 2: AI 推理
第二个demo应用让用户上传图像,然后调用 serverless 函数来识别图片中的主要物体。
它与上一个示例在同一个 GitHub repo ,但是在 tensorflow 分支。 用于图片识别的后端 serverless 函数在该分支的 api/functions/image-classification 文件夹中。 src/main.rs 文件包含了 Rust 程序的源代码。 Rust 程序从 STDIN 读取图像数据,然后将文本输出输出到 STDOUT。 它用 WasmEdge Tensorflow API 来运行 AI 推理。
pub fn main() {
// Step 1: Load the TFLite model
let model_data: &[u8] = include_bytes!("models/mobilenet_v1_1.0_224/mobilenet_v1_1.0_224_quant.tflite");
let labels = include_str!("models/mobilenet_v1_1.0_224/labels_mobilenet_quant_v1_224.txt");
// Step 2: Read image from STDIN
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf).unwrap();
// Step 3: Resize the input image for the tensorflow model
let flat_img = wasmedge_tensorflow_interface::load_jpg_image_to_rgb8(&buf, 224, 224);
// Step 4: AI inference
let mut session = wasmedge_tensorflow_interface::Session::new(&model_data, wasmedge_tensorflow_interface::ModelType::TensorFlowLite);
session.add_input("input", &flat_img, &[1, 224, 224, 3])
.run();
let res_vec: Vec<u8> = session.get_output("MobilenetV1/Predictions/Reshape_1");
// Step 5: Find the food label that responds to the highest probability in res_vec
// ... ...
let mut label_lines = labels.lines();
for _i in 0..max_index {
label_lines.next();
}
// Step 6: Generate the output text
let class_name = label_lines.next().unwrap().to_string();
if max_value > 50 {
println!("It {} a <a href='https://www.google.com/search?q={}'>{}</a> in the picture", confidence.to_string(), class_name, class_name);
} else {
println!("It does not appears to be any food item in the picture.");
}
}
使用 cargo 工具将 Rust 程序构建为 WebAssembly 字节码或原生代码。
$ cd api/functions/image-classification/
$ cargo build --release --target wasm32-wasi
将 build artifacts 复制到 api 文件夹中
$ cp target/wasm32-wasi/release/classify.wasm ../../
同样, api/pre.sh 脚本在此应用程序中安装 WasmEdge runtime 及其 Tensorflow 依赖项。同时在部署时,它将 classify.wasm 字节码程序编译为 classify.so 本地共享库。
api/hello.js 脚本加载 WasmEdge runtime,在 WasmEdge 中启动编译好的 WebAssembly 程序,并通过 STDIN 传递已上传的图像数据。 注意 api/hello.js 运行由 api/pre.sh 生成的编译好的 classify.so 文件,以达到更好的性能。
const fs = require('fs');
const { spawn } = require('child_process');
const path = require('path');
module.exports = (req, res) => {
const wasmedge = spawn(
path.join(__dirname, 'wasmedge-tensorflow-lite'),
[path.join(__dirname, 'classify.so')],
{env: {'LD_LIBRARY_PATH': __dirname}}
);
let d = [];
wasmedge.stdout.on('data', (data) => {
d.push(data);
});
wasmedge.on('close', (code) => {
res.setHeader('Content-Type', `text/plain`);
res.send(d.join(''));
});
wasmedge.stdin.write(req.body);
wasmedge.stdin.end('');
}
现在可以将你fork 的 repo 部署到 Netlify 上,并得到一个可以进行物体识别的 web 应用。
接下来呢?
在 Netlify 目前的 serverless 容器中运行 WasmEdge 是目前将高性能函数添加到 Netlify 应用中的简单方式。未来更好的方法是将WasmEdge作为容器本身使用,这样就无须 Docker 与 Node.js,我们可以以更高的效率运行 serverless 函数。 WasmEdge 已经与 Docker 工具兼容。如果你有兴趣加入 WasmEdge 和 CNCF 进行这个激动人心的工作,欢迎加入我们的 channel!
相关推荐
- Vue 技术栈(全家桶)(vue technology)
-
Vue技术栈(全家桶)尚硅谷前端研究院第1章:Vue核心Vue简介官网英文官网:https://vuejs.org/中文官网:https://cn.vuejs.org/...
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
-
前言《vue基础》系列是再次回炉vue记的笔记,除了官网那部分知识点外,还会加入自己的一些理解。(里面会有部分和官网相同的文案,有经验的同学择感兴趣的阅读)在开发时,是不是遇到过这样的场景,响应...
- vue3 组件初始化流程(vue组件初始化顺序)
-
学习完成响应式系统后,咋们来看看vue3组件的初始化流程既然是看vue组件的初始化流程,咋们先来创建基本的代码,跑跑流程(在app.vue中写入以下内容,来跑流程)...
- vue3优雅的设置element-plus的table自动滚动到底部
-
场景我是需要在table最后添加一行数据,然后把滚动条滚动到最后。查网上的解决方案都是读取html结构,暴力的去获取,虽能解决问题,但是不喜欢这种打补丁的解决方案,我想着官方应该有相关的定义,于是就去...
- Vue3为什么推荐使用ref而不是reactive
-
为什么推荐使用ref而不是reactivereactive本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代optionapi的data的替...
- 9、echarts 在 vue 中怎么引用?(必会)
-
首先我们初始化一个vue项目,执行vueinitwebpackechart,接着我们进入初始化的项目下。安装echarts,npminstallecharts-S//或...
- 无所不能,将 Vue 渲染到嵌入式液晶屏
-
该文章转载自公众号@前端时刻,https://mp.weixin.qq.com/s/WDHW36zhfNFVFVv4jO2vrA前言...
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
-
此篇幅比较长,涉及到的小知识点也比较多,一定要耐心看完,记住学东西没有耐心可不行!!!一、添加和修改注:添加和编辑用到了同一个组件,也就是此篇文章你能学会如何封装组件及引用组件;第二能学会async和...
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
-
前言本文整理了...
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
-
今天给大家分享的是Vue3聊天实例中的朋友圈的实现及登录验证和倒计时操作。先上效果图这个是最新开发的vue3.x网页端聊天项目中的朋友圈模块。用到了ElementPlus...
- 不来看看这些 VUE 的生命周期钩子函数?| 原力计划
-
作者|huangfuyk责编|王晓曼出品|CSDN博客VUE的生命周期钩子函数:就是指在一个组件从创建到销毁的过程自动执行的函数,包含组件的变化。可以分为:创建、挂载、更新、销毁四个模块...
- Vue3.5正式上线,父传子props用法更丝滑简洁
-
前言Vue3.5在2024-09-03正式上线,目前在Vue官网显最新版本已经是Vue3.5,其中主要包含了几个小改动,我留意到日常最常用的改动就是props了,肯定是用Vue3的人必用的,所以针对性...
- Vue 3 生命周期完整指南(vue生命周期及使用)
-
Vue2和Vue3中的生命周期钩子的工作方式非常相似,我们仍然可以访问相同的钩子,也希望将它们能用于相同的场景。...
- 救命!这 10 个 Vue3 技巧藏太深了!性能翻倍 + 摸鱼神器全揭秘
-
前端打工人集合!是不是经常遇到这些崩溃瞬间:Vue3项目越写越卡,组件通信像走迷宫,复杂逻辑写得脑壳疼?别慌!作为在一线摸爬滚打多年的老前端,今天直接甩出10个超实用的Vue3实战技巧,手把...
- 怎么在 vue 中使用 form 清除校验状态?
-
在Vue中使用表单验证时,经常需要清除表单的校验状态。下面我将介绍一些方法来清除表单的校验状态。1.使用this.$refs...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Vue 技术栈(全家桶)(vue technology)
- vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)
- vue3 组件初始化流程(vue组件初始化顺序)
- vue3优雅的设置element-plus的table自动滚动到底部
- Vue3为什么推荐使用ref而不是reactive
- 9、echarts 在 vue 中怎么引用?(必会)
- 无所不能,将 Vue 渲染到嵌入式液晶屏
- vue-element-admin 增删改查(五)(vue-element-admin怎么用)
- 最全的 Vue 面试题+详解答案(vue面试题知识点大全)
- 基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)