Storlet 编写和部署指南

Storlet 可以用 Java 或 Python 编写。本指南是编写和部署 storlet 的语言无关的起点。 JavaPython 专用指南补充了本指南,并且应该在本指南之后阅读。

编写 storlet 涉及实现一个名为 invoke 的单个函数(确切名称因语言而异)。

通常,invoke 操作会获取一个输入流和一个输出流。输入流将包含要上传(如果 storlet 在上传期间被调用)或要下载(如果调用在下载期间完成)的数据。输入流伴随着反映 Swift 用户定义元数据的元数据。输出流提供了一种方式来写回 storlet 输出数据,以及元数据。输入和输出流以对所用语言而言是原生方式暴露给 storlet 代码。

一旦 storlet 被编写和测试,就可以将其作为对象上传到指定的容器(默认情况下称为“storlet”)。Storlet 代码的外部依赖项,例如 Java 库或 Python 模块,也可以上传。但是,假设 storlet 依赖项相对较小(数量级为几 MB)。较重的依赖项应作为 Docker 镜像的一部分。部署 storlet 和依赖项如下所述。

StorletSamples/java 和 StorletSamples/python 目录中存在各种 storlet 实现。这些 storlet 被引擎的功能测试使用。

接下来的两个部分描述了与所用语言无关的 storlet 编写和部署指南。

Storlet 编写指南

无论使用哪种语言编写 storlet,都应遵循一些指南。其中一些是必须遵守的,而另一些是建议。

建议

  1. Storlet 专为流处理而设计,即它们在读取时处理输入并产生输出。换句话说,“对象内容的归并排序”不是一个好的 storlet 示例,因为它需要将所有内容读取到内存中(由于输入作为流提供,因此无法进行随机读取)。虽然我们目前没有对 storlet 的 CPU 使用量或内存消耗施加任何限制,但将大型对象读取到内存或进行非常密集的计算会对整体系统性能产生影响。

  2. 虽然这可能显而易见,但在部署之前请务必对您的 storlet 进行单元测试。

必须遵守

  1. storlet 代码必须是线程安全的且可重入的。invoke 方法将被多次调用,并且可能并行调用。

  2. 一旦 storlet 完成写入响应,重要的是要关闭输出流。否则将导致超时。

  3. storlet 必须在调用后的 40 秒内开始响应。否则,Swift 将超时。此外,storlet 必须每 40 秒输出一些内容,以免超时。这是确保 storlet 代码不会卡住的一种机制。请注意,输出空字符串不会在重置 40 秒超时方面起作用。

  4. storlet 必须将元数据写入输出流,并且必须在开始流式传输数据之前执行此操作。典型的实现将读取输入元数据并将其用作要写入元数据的依据。请注意,此处也适用 40 秒超时。

  5. 可以设置的元数据的总大小(序列化为字符串时)不得超过 4096 字节

  6. 虽然 Swift 使用前缀 X-Object-Meta 来指定某个标头反映元数据键,但键本身不应以该前缀开头。更具体地说,storlet 设置的元数据键不应具有该前缀(除非这是键的一部分)

Storlet 部署指南

Storlet 部署基本上是将 storlet 及其依赖项上传到您正在使用的帐户中的指定容器。虽然 storlet 和依赖项是常规 Swift 对象,但它们必须携带 storlet 引擎使用的某些元数据。当首次执行 storlet 时,引擎会从 Swift 获取必要的对象并将它们放入 Docker 容器可访问的目录中。请注意,依赖项应该很小。拥有大量的依赖项或非常大的依赖项可能会导致首次尝试执行 storlet 时超时。如果发生这种情况,只需再次发送请求即可。

我们支持两种类型的依赖项

  1. storlet 语言特有的外部库或模块

  2. storlet 代码可以执行的可执行依赖项。

Storlet 对象元数据

必须将 storlet 上传到指定的容器,默认情况下称为“storlet”。上传的对象必须携带以下元数据。有关更多信息,请参阅特定语言指南。

X-Object-Meta-Storlet-Language - must be 'python' or 'java'
X-Object-Meta-Storlet-Interface-Version - currenltly we have a single version '1.0'
X-Object-Meta-Storlet-Object-Metadata - Currently, not in use, but must appear. Use the value 'no'
X-Object-Meta-Storlet-Main - The name of the class that implements the invoke operation

可选元数据项是

X-Object-Meta-Storlet-Dependency - A comma separated list of dependencies.

如果希望更新 storlet,只需再次上传,引擎就会识别更新并带来更新的代码。

依赖项对象元数据

必须将依赖项上传到指定的容器,默认情况下称为“dependency”。上传的对象必须携带以下元数据。

X-Object-Meta-Storlet-Dependency-Version - While the engine currently does not parse this header, it must appear.

可选元数据项是

X-Object-Meta-Storlet-Dependency-Permissions - The permissions given to the dependency when it is copied to the
Docker container. This is helpful for binary dependencies invoked by the storlet.
For a binary dependency once can specify: '0755'

注意

当前,不识别依赖项更新。

使用 Swift 客户端 cli 部署 Storlet

我们下面展示了如何使用 Swift 客户端 cli 部署 storlet。示例使用 Java storlet。从部署 Python storlet 的差异很小,我们将在需要时突出显示它们。

在使用 Swift 客户端时,需要提供凭据以及身份验证 URI。凭据可以通过环境变量或命令行参数提供。为了使命令更具可读性,我们使用如下所示的环境变量。实际值与开发环境安装 说明 对齐

export OS_USERNAME=tester
export OS_PASSWORD=testing
export OS_PROJECT_NAME=test
export OS_AUTH_URL=http://127.0.0.1/identity/v3

这是用于上传 storlet 的 Swift 客户端命令。一些说明

  1. 我们使用 swift cli 的 upload 选项。

  2. 容器名称是 upload 命令的第一个参数,即“storlet”

  3. 对象名称和要上传的本地文件是“identitystorlet-1.0-jar” IMPORTANT:当从另一个目录上传文件时,该参数将是“bin/identitystorlet-1.0-jar”形式,在这种情况下,出现在 storlet 容器中的对象名称将是“bin/identitystorlet-1.0-jar”,这对于引擎来说将不起作用。

  4. 需要伴随 storlet 对象的元数据作为标头提供。

eranr@lnx-ccs8:~/workspace/Storlets/StorletSamples/IdentityStorlet/bin$ swift upload storlet identitystorlet-1.0.jar \
-H "X-Object-Meta-Storlet-Language:Java" \
-H "X-Object-Meta-Storlet-Interface-Version:1.0" \
-H "X-Object-Meta-Storlet-Object-Metadata:no" \
-H "X-Object-Meta-Storlet-Main:org.openstack.storlet.identity.IdentityStorlet" \
-H "X-Object-Meta-Storlet-Dependency:get42"

注意

当部署 Python storlet 时,对象名称(上述 identitystorlet-1.0.jar)具有不同的格式。否则,“X-Object-Meta-Storlet-Language”是“Python”,并且“X-Object-Meta-Storlet-Main”具有不同的格式。请参阅 Python 以获取确切的详细信息。

这是用于上传 get42 依赖项的 Swift 客户端命令。再次,一些说明

  1. 此处使用的容器名称是 upload 命令的第一个参数,即“dependency”。

  2. 我们使用可选的权限标头,因为这是一个二进制文件。

eranr@lnx-ccs8:~/workspace/Storlets/StorletSamples/IdentityStorlet/bin$ swift upload dependency get42 \
-H "X-Object-Meta-Storlet-Dependency-Version:1.0" \
-H "X-Object-Meta-Storlet-Dependency-Permissions:0755"

使用 Python Swift 客户端部署 Storlet

下面是一个上传 storlet 以及依赖项的代码片段。该代码假定 v2 身份验证,并针对具有以下内容的 Swift 集群进行了测试

  1. 使用“test”帐户配置的 Keystone,具有用户“tester”,其密码为“testing”

  2. 在服务帐户下,已经存在“storlet”和“dependency”容器。

示例使用 Java storlet。与部署 Python storlet 的差异很小,与部署使用 Swift 客户端部分上面突出显示的差异相同。

from swiftclient import client

def put_storlet_object(url, token, storlet_name, local_path_to_storlet, main_class_name, dependencies):
    # Delete previous storlet
    resp = dict()

    metadata = {'X-Object-Meta-Storlet-Language':'Java',
                'X-Object-Meta-Storlet-Interface-Version':'1.0',
                'X-Object-Meta-Storlet-Dependency': dependencies,
                'X-Object-Meta-Storlet-Object-Metadata':'no',
                'X-Object-Meta-Storlet-Main': main_class_name}
    f = open('%s/%s' % (local_path_to_storlet, storlet_name), 'rb')
    content_length = None
    response = dict()
    client.put_object(url, token, 'storlet', storlet_name, f,
                      content_length, None, None,
                      "application/octet-stream",
                      metadata,
                      None, None, None,
                      response)
    print response
    f.close()

def put_storlet_dependency(url, token, dependency_name, local_path_to_dependency):
    metadata = {'X-Object-Meta-Storlet-Dependency-Version': '1'}
    # for an executable dependency
    # metadata['X-Object-Meta-Storlet-Dependency-Permissions'] = '0755'
    f = open('%s/%s'% (local_path_to_dependency, dependency_name), 'rb')
    content_length = None
    response = dict()
    client.put_object(url, token, 'dependency', dependency_name, f,
                      content_length, None, None,
                      "application/octet-stream",
                      metadata,
                      None, None, None,
                      response)
    print response
    f.close()
    status = response.get('status')
    assert (status == 200 or status == 201)

AUTH_IP = '127.0.0.1'
ACCOUNT = 'test'
USER_NAME = 'tester'
PASSWORD = 'testing'
os_options = {'project_name': ACCOUNT}

url, token = client.get_auth("http://" + AUTH_IP + "/identity/v3", USER_NAME,
                             PASSWORD,
                             os_options = os_options,
                             auth_version="3")
put_storlet_object(url, token,'identitystorlet-1.0.jar','/tmp',
                   'org.openstack.storlet.identity.IdentityStorlet',
                   'get42')
put_storlet_dependency(url, token,'get42','/tmp')