插件最佳实践

本页面提供了一些提示,可帮助您编写正确、性能良好且隔离性良好的 Service Extensions 插件。正确性至关重要,因为插件在受限的引擎沙盒中运行,API 表面有限。性能至关重要,因为插件在最终用户请求期间运行,但只有少量资源。在项目级层提供隔离。

使用入门

从示例开始

不妨先浏览我们的插件代码示例。 以下是一些常见的插件模式示例,例如路径或查询解析、标头重写、自定义日志记录和自定义身份验证。

使用受支持的 API

插件必须针对 Proxy-Wasm 二进制接口 (ABI) 进行编译。Service Extensions 支持 Proxy-Wasm ABI 的一个子集,其中包括 HTTP 标头和正文突变、本地响应、自定义日志记录和插件配置。Proxy-Wasm 还支持 WASI 预览版 1 的一小部分,包括用于日志记录的 stdoutstderr,以及 clock_time_getrandom_get

Service Extensions 不支持计时器、自定义指标、共享数据、共享队列或出站网络调用。Service Extensions 也不支持尝试暂停请求处理的插件回调返回值,并且会忽略这些返回值。

运行功能测试和基准测试

为了评估正确性和性能,我们提供了一个本地插件测试工具,可用于运行、测试和对插件进行基准比较。您可以在 Docker 容器中调用此工具,并传递插件二进制文件以及输入和预期结果的文本 proto。请参阅本地测试工具文档测试输入示例

正确做法

不要依赖时钟

出于安全考虑,时钟时间是在创建上下文(针对插件或请求)时设置的,并在插件调用期间保持冻结状态。这意味着 WebAssembly 插件不得休眠;休眠会超时,因为时间不会前进。这也意味着插件无法衡量自身的执行时间,不过此信息可在 Cloud Monitoring 中获取。

将标头名称视为不区分大小写

根据 HTTP 语义,HTTP 标头字段名称必须不区分大小写。插件写入的标头大小写可以在发送到客户端或后端之前更改。

性能

避免插件崩溃

当插件崩溃时,触发请求会收到错误。如果这种情况频繁发生,插件重启会受到限制,从而导致大量错误影响多位用户。

为避免插件崩溃,请尝试以下操作:

  • 避免使用任何类型的断言。请改为配置错误日志记录或向用户提供本地响应。
  • 在 Rust 中,请避免使用可能导致 panic 的方法,例如 unwrap。此外,您还可以使用 panic::set_hook 配置插件以处理崩溃。
  • 使用 Google 提供的插件测试工具测试插件,并确保插件在处理格式有误或为空的输入时不会崩溃。

为提高执行速度而编译

使用 build 选项 -O3(适用于 C++)或 opt-level=3(适用于 Rust)编译 WebAssembly 代码,以实现最佳执行速度。

预编译正则表达式

避免在每个请求路径(在 HTTP 处理程序中)上执行计算密集型操作。 相反,请在插件配置时执行任何与请求无关的工作,并将任何预计算的状态传递到每个 HTTP 请求上下文(也称为流上下文)。

具体而言,在插件配置时预编译任何正则表达式。我们的正则表达式代码示例展示了如何实现这一点。

避免将数据复制到 HTTP 上下文中

在根上下文和 HTTP 上下文之间共享数据时,请避免进行开销较大的复制或克隆调用。而是共享指针或引用。在 C++ 中,可以使用 std::shared_ptr 或原始指针和引用来实现这一点,因为根上下文的生命周期比任何 HTTP 上下文都长。在 Rust 中,可以使用 std::rc::Rc 共享值。在 Go 中,可以通过存储指向值的指针来共享值,这些指针会被垃圾收集器移除。

安全

按项目隔离工作负载

在 Google 的基础设施上,归同一 Google Cloud 项目所有的插件可以在同一安全沙盒中运行。这意味着,WebAssembly 运行时是同一项目中插件之间唯一的安全屏障。

为需要安全隔离的工作负载使用单独的 Google Cloud 项目。这种做法还可实现资源和权限的分离。

限制 Media CDN 上的 Secret

根据设计,媒体 CDN 在 Google 物理控制范围之外的一组硬件上运行。为 Media CDN 编写安全相关插件时,请使用专用主机名或子网域,并使用经常轮换的签名密钥配置插件。