Storlet Engine Overview

在高级层面,storlet 引擎由下面描述的组件构成。请参阅示意图。

Storlet 中间件

Storlet 中间件是一个 Swift WSGI 中间件,用于拦截 storlet 调用请求,并将输入数据和计算输出路由到 Docker 容器中执行 storlet,以及从 Docker 容器中路由出来。此中间件需要在代理服务器和对象服务器的管道中都存在。

Storlet 中间件的编写方式允许扩展引擎以支持其他沙箱技术,而不仅仅是 Docker。这体现在“storlet gateway”API 中,该 API 定义了运行 storlet 所需的沙箱功能。此外,storlet 中间件配置的一部分是加载哪个“storlet gateway”实现类。目前,我们有一个 API 的单一类实现,我们称之为“storlet docker gateway”。

Swift 账户

Storlet 引擎与 Swift 中的账户紧密耦合,具体如下:

  1. 为了在存储在某个 Swift 账户中的数据对象上调用 storlet,必须启用该账户的 storlet 功能。也就是说,必须在账户上设置一个指定的用户定义元数据标志为 true。

  2. 每个 Swift 账户必须具有引擎所需的某些容器。其中一个容器是“storlet”容器,storlet 会被上传到该容器。只要调用用户具有对“storlet”容器的读取权限,就可以在帐户中的任何数据对象上调用上传到此容器的 storlet。

  3. 每个账户都有一个单独的 Docker 镜像(和容器),storlet 在其中执行。属于同一账户的所有正在数据对象上执行的 storlet,都将在同一个 Docker 容器中执行。这有助于为不同的 Swift 账户提供不同的镜像。Docker 镜像名称必须是它所属的账户 ID。

Docker 镜像

如上所述,每个启用了 storlet 的账户都有一个 Docker 镜像。在高级层面,此镜像包含

  1. 一个 Java 运行时环境。当您运行用 Java 编写的 storlet 时,这是必需的。

  2. 一个守护进程工厂。一个 Python 进程,作为 Docker 容器启动的一部分启动。此进程在“storlet docker gateway”的请求下生成“每个 storlet 守护进程”,该网关在 storlet_middleware 的上下文中运行。

  3. 一个 storlet 守护进程。storlet 守护进程是一个通用守护进程,一旦生成,就会加载特定的 storlet 代码并等待调用。不同的 storlet,例如过滤 storlet 和压缩 storlet,被加载到不同的守护进程中。首次需要执行某个 storlet 时,会调用一个守护进程。目前我们有两种类型的守护进程,一个 Java 守护进程用于加载和运行用 Java 编写的 storlet,一个 Python 守护进程用于加载和运行用 Python 编写的 storlet。

  4. Storlet common jar。这是用于用 Java 开发 storlet 的 jar。其中包含 storlet 必须实现的 invoke API 的定义等。

Storlet 总线

Storlet 总线是 Swift 侧的 storlet 中间件与 Docker 容器中的工厂守护进程和 storlet 守护进程之间的通信通道。对于每个 Docker 容器(或 Swift 账户),都有一个与该容器的 storlet 工厂的通信通道。对于容器中的每个 storlet 守护进程,都有一个通信通道,它监听调用。这些通道基于 Unix 域套接字。

Storlet 引擎组件示意图

alternate text

流程

为了将所有内容联系起来,我们说明一个端到端场景。

编写和部署 Storlet

流程从编写 storlet 开始,然后是部署它。编写和部署 storlet 在 编写和部署 storlet 指南 中介绍。

调用 Storlet

Storlet 可以在对象下载、上传或复制操作(分别为 GET、PUT 和 COPY)上调用。为了描述流程,我们假设希望在对象下载上调用 storlet。这涉及执行 Swift GET 请求,并带有额外的标头“X-Run-Storlet”,该标头指定要调用的 storlet,例如“X-Run-Storlet: compress-1.0.jar”。

在代理服务器上处理请求

看到“X-Run-Storlet”标头,代理上的 storlert_middleware 拦截请求并对用户指定的 storlet 执行 HEAD 操作。此 HEAD 操作有助于

  1. 强制执行执行权限:访问 storlet 容器意味着用户允许调用 storlet。如果 HEAD 失败,则引擎返回 HTTP Unauthorized。

  2. 获取 storlet 元数据。稍后将使用此元数据来验证正在执行的实际代码是否是最新代码。

一旦 HEAD 成功,storlet 中间件将 storlet 元数据添加到请求中,并让请求继续通过代理管道流动。管道最终将请求路由到持有 GET uri 中指定的对象的副本的对象服务器。

在对象服务器上处理请求

看到“X-Run-Storlet”标头,对象服务器上的 storlert_middleware 拦截请求并执行以下两阶段流程

第一阶段

第一阶段是确保在 Docker 容器中为请求 uri 中出现的相应账户运行本地 storlet 守护进程。在此阶段,中间件执行以下操作

  1. 检查是否存在请求 uri 中出现的账户的正在运行的 Docker 容器。如果不存在,则中间件尝试启动它。

  2. 检查是否存在要执行的所需 storlet 的本地更新副本。如果没有本地副本或副本不是最新的,则中间件启动内部 GET 请求以从“storlet”容器中获取它。

  3. 如果本地副本已更新,则中间件检查容器中是否存在该 storlet 的正在运行的守护进程。这是通过查询命名管道“factory pipe”上的 storlet 守护进程来完成的。

  4. 如果没有正在运行的守护进程,则中间件要求工厂为其生成一个。生成后,守护进程开始监听指定的命名管道以进行调用。

第二阶段

在第二阶段,中间件实际通过请求调用 storlet。一旦有守护进程运行,中间件将按如下方式进行

  1. 中间件让请求继续通过对象服务器管道流动,直到获得响应。响应携带一个描述符,通过该描述符可以访问对象数据。

  2. 中间件使用 storlet 守护进程命名管道来执行 storlet 的实际调用。实际调用是通过管道传递一个携带对象数据的描述符,以及一个用于 storlet 写入输出的描述符,以及另一个用于 storlet 日志的描述符来完成的。

  3. 一旦 storlet 开始将结果写入输出描述符,sotler_middleware 就会返回一个响应,该响应携带 storlet 的输出描述符,以便可以将输出流式传输回代理和用户。

注意

以上是对 storlet 引擎所做主要工作的一个简化。

注意

在某些情况下,storlet 在代理上执行。其中一种情况是 PUT 请求。在代理上执行 storlet 涉及与上述描述的几乎相同的步骤。