是一个用于开发和托管应用程序的平台。事实上,这是Yes的创始人在2015年创造的一个词。也是主要的组织者。
一个应用程序由一个静态 UI(HTML 和 )和一系列函数组成。动态 UI 元素是通过从函数中获取数据生成的。有很多好处,但最重要的好处之一是出色的性能。由于不再从中央服务器生成 UI,因此服务器上的负载要少得多,我们可以通过 CDN 等边缘网络部署 UI。
但是 Edge CDN 只解决了分发静态 UI 文件的问题。后端功能可能仍然很慢。事实上,流行的平台都有众所周知的性能问题,例如冷启动缓慢,尤其是对于交互式应用程序。在这方面,可以做很多事情。
使用 CNCF 托管的云原生,开发人员可以编写部署在公共云或边缘计算节点上的高性能函数。在本文中,我们将探讨如何使用 Rust 编写的函数来支持应用程序后端。
为什么使用实现函数
该平台已经有一个非常易于使用的框架来部署功能。如上所述,使用 as well 是为了进一步提高性能。用 C/C++、Rust 和 Swift 编写的高性能函数可以很容易地编译成。这些函数比函数中常用的函数要快得多。
但是,如果原始性能是唯一目标,为什么不直接将这些函数编译为机器原生可执行文件(原生客户端或 NaCl)?这是因为这些功能已经在 AWS 中安全运行。
我们对未来的愿景是在云原生基础架构中作为轻量级并排运行。与类似容器或 . 但目前,它只支持在 .
与运行容器化 NaCl 程序相比,在 NaCl 中运行函数有许多优势。
首先,它为独立功能提供了细粒度的隔离。一个微服务可以有多种功能并支持在其中运行的服务。它可以使微服务更加安全和稳定。
其次,字节码是可移植的。开发人员只需构建一次,无需担心未来对底层的更改或更新。它还允许开发人员在其他云环境中重用相同的功能。
第三,应用程序易于部署和管理。与 NaCl 动态库和可执行文件相比,它们具有更少的平台依赖性和复杂性。
最后,API 提供了最符合 Rust 的方式来执行模型。安装正确的依赖库组合,为开发者提供统一的API。
概念和解释已经说了很多,趁热打铁,我们来看看示例应用吧!
准备好工作了
由于我们的演示函数是用 Rust 编写的,因此您需要安装一个 Rust 编译器。确保按如下方式安装 -wasi 编译器目标以生成字节码。
$ rustup target add wasm32-wasi
演示应用程序的前端使用 Next.js 编写并部署在 . 我们假设您已经具备使用 Next.js 和 .
示例 1:图像处理
我们的第一个演示应用程序让用户上传图像,然后调用一个函数将其转换为黑白图像。在开始之前,您可以试用部署在 .
首先 fork 演示应用程序的 repo。要将应用程序部署到,只需将您的存储库添加到 .
这个 repo 是平台的标准 Next.js 应用程序。后端函数位于 api// 文件夹中。src/main.rs 文件包含 Rust 程序的源代码。Rust 程序从 STDIN 读取图像数据并将黑白图像输出到 .
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 程序构建为字节码或本机代码。
$ cd api/functions/image-grayscale/
$ cargo build --release --target wasm32-wasi
将构建复制到 api 文件夹。
$ cp target/wasm32-wasi/release/grayscale.wasm ../../
该函数在设置函数时运行 api/pre.sh。这将安装字节码程序并将其编译到本机 so 库中,以便更快地执行。
api/hello.js 文本被加载,编译后的程序在 中启动,上传的图片数据通过 STDIN 传递。注意这里 api/hello.js 会运行 api/pre.sh 生成的编译后的 .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('');
}
这个完成了。接下来c语言实现web服务器,将 repo 部署到应用程序。该应用程序具有基于 Rust 和 .
示例 2:人工智能推理
第二个演示应用程序允许用户上传图像,然后调用一个函数来识别图像中的主要对象。
它与前面的示例在同一个仓库中,但在一个分支中。图像识别的后端函数在这个分支的 api//image- 文件夹中。src/main.rs 文件包含 Rust 程序的源代码。Rust 程序从 STDIN 读取图像数据,然后将文本输出输出到 . 它使用 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 = 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 {} 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 程序构建为字节码或本机代码。
$ cd api/functions/image-classification/
$ cargo build --release --target wasm32-wasi
将构建复制到 api 文件夹中
$ cp target/wasm32-wasi/release/classify.wasm ../../
同样,api/pre.sh 脚本在此应用程序中安装其依赖项。同样在部署时c语言实现web服务器,它会将 .wasm 字节码程序编译成 .so 本机共享库。
api/hello.js 脚本加载,启动编译后的程序,通过STDIN传递上传的图片数据。请注意,api/hello.js 运行由 api/pre.sh 生成的已编译 .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('');
}
您现在可以将您的分叉存储库部署到它并获得一个可以进行对象识别的 Web 应用程序。
下一步是什么?
在当前容器中运行是一种向当今应用程序添加高性能功能的简单方法。未来更好的做法是把它作为容器本身使用,这样我们就可以更高效地运行函数,而不必与 Node.js 打交道。已经与该工具兼容。如果您有兴趣加入 CNCF 从事这项激动人心的工作,欢迎加入我们!
文章来源:http://www.toutiao.com/a6995492003425403396/