亚马逊AWS官方博客

推出 Amazon CloudFront KeyValueStore:适用于 CloudFront Functions 的低延迟数据存储

Amazon CloudFront 让您能够以低延迟和高传输速度安全地分发静态和动态内容。借助 CloudFront Functions,您可以每秒对数百万个请求执行延迟敏感型的自定义操作。例如,您可以使用 CloudFront Functions 修改标头、标准化缓存键、重写 URL 或授权请求。

今天,我们推出了 CloudFront KeyValueStore,这是一种安全的全球低延迟键值数据存储,让您能够从 CloudFront Functions 内部进行读取访问,从而在 CloudFront 边缘站点实现高级可自定义逻辑。

以前,您必须在函数代码中嵌入配置数据。例如,用于确定是否应重定向某个 URL 以及要将查看者重定向到哪个 URL 的数据。将配置数据嵌入到函数代码中时,每次对配置进行轻微的更改时都需要更改代码并重新部署函数代码。同时由于每次添加新的查询时都需要更新和部署代码,这将带来无意中更改代码的风险。此外,由于最大函数大小为 10KB,这使得许多时候很难在代码中容纳所有数据。

使用 CloudFront KeyValueStore 后,与函数相关的数据和函数代码现在都可以独立更新。这不仅简化了函数代码,同时也无需部署代码更改即可轻松更新数据。

下面来看看这种新功能的实际应用。

创建 CloudFront 键值存储
CloudFront 控制台的导航窗格中,我选择了函数。在 KeyValueStores 选项卡中,我选择了创建 KeyValueStore

在这里,我可以选择从 Amazon Simple Storage Service(Amazon S3)存储桶中的 JSON 文件中导入键值对。但这次我不会这样做,因为我想从没有任何键开始。我输入一个名称和描述,并完成键值存储的创建。

控制台屏幕截图。

创建键值存储后,我选择了键值对部分中的编辑,然后选择了添加对。对于键,我键入了 hello;对于值,我输入了 Hello World,然后保存更改。我可以添加更多的键和值,不过现在一个键就足够了。

控制台屏幕截图。

当我更新键值存储时,更改会在几秒钟内传播到所有 CloudFront 边缘站点,以便与该键值存储关联的函数能够以低延迟使用。下面来看看它的工作原理。

使用来自 CloudFront Functions 的 CloudFront KeyValueStore
CloudFront 控制台的导航窗格中,我选择了函数,然后选择创建函数。我输入函数的名称,选择 cloudfront-js-2.0 运行时系统,然后完成函数的创建。然后,我使用新选项将键值存储与该函数关联起来。

控制台屏幕截图。

我从控制台复制了键值存储 ID,以便在下面的函数代码中使用:

import cf from 'cloudfront';

const kvsId = '<KEY_VALUE_STORE_ID>';

// This fails if the key value store is not associated with the function
const kvsHandle = cf.kvs(kvsId);

async function handler(event) {
    // Use the first part of the pathname as key, for example http(s)://domain/<key>/something/else
    const key = event.request.uri.split('/')[1]
    let value = "Not found" // Default value
    try {
        value = await kvsHandle.get(key);
    } catch (err) {
        console.log(`Kvs key lookup failed for ${key}: ${err}`);
    }
    var response = {
        statusCode: 200,
        statusDescription: 'OK',
        body: {
            encoding: 'text',
            data: `Key: ${key} Value: ${value}\n`
        }
    };
    return response;
}

此函数将请求路径的第一部分作为键,并用键的名称和值进行响应。

我保存了更改并发布了该函数。在该函数的发布选项卡中,我将该函数与我之前创建的一个 CloudFront 分配关联起来。我使用查看者请求事件类型和默认(*) 缓存行为来拦截所有对分配发出的请求。

在控制台中,我返回函数列表并等待函数完成部署。然后,我在命令行中使用 curl 命令从该分配下载内容,然后测试函数的结果。

首先,我尝试使用多条路径来调用该函数并查找我之前创建的键(hello):

curl https://distribution-domain.cloudfront.net/hello
Key: hello Value: Hello World

curl https://distribution-domain.cloudfront.net/hello/world
Key: hello Value: Hello World

成功了! 然后,我尝试使用其他路径,来确认在找不到键时是否会返回我在代码中使用的默认值。

curl https://distribution-domain.cloudfront.net/hi
Key: hi Value: Not found

第一个例子已经奏效,下面来尝试一些更高级和更有用的东西。

使用 CloudFront KeyValueStore 中的配置数据重写 URL
让我们构建一个函数,该函数将使用 HTTP 请求中 URL 内容,从而在键值存储中查找 CloudFront 发出实际请求时应使用的自定义路径。此函数有助于管理网站所包含的多种服务。

例如,我想更新我网站使用的博客平台。旧博客的原始路径为 /blog-v1,而新博客的原始路径为 /blog-v2

架构图。

一开始时,我还在使用旧博客。在 CloudFormation 控制台中,我将键 blog 和值 blog-v1 添加到键值存储中。

然后,我创建了以下函数,并使用查看者请求事件和默认(*)缓存行为将其与该分配关联起来,用于拦截对该分配发出的所有请求。

import cf from 'cloudfront';

const kvsId = "<KEY_VALUE_STORE_ID>";

// This fails if the key value store is not associated with the function
const kvsHandle = cf.kvs(kvsId);

async function handler(event) {
    const request = event.request;
    // Use the first segment of the pathname as key
    // For example http(s)://domain/<key>/something/else
    const pathSegments = request.uri.split('/')
    const key = pathSegments[1]
    try {
        // Replace the first path of the pathname with the value of the key
        // For example http(s)://domain/<value>/something/else
        pathSegments[1] = await kvsHandle.get(key);
        const newUri = pathSegments.join('/');
        console.log(`${request.uri} -> ${newUri}`)
        request.uri = newUri;
    } catch (err) {
        // No change to the pathname if the key is not found
        console.log(`${request.uri} | ${err}`);
    }
    return request;
}

现在,当我在 URL 路径的开头键入 blog 时,该请求实际上会转到 blog-v1 路径。CloudFront 将向旧博客发出 HTTP 请求,因为 blog-v1 是旧博客使用的原始路径。

例如,假设我在浏览器中键入 https://distribution-domain.cloudfront.net/blog/index.html ,我会看到旧博客(V1)。

显示博客 V1 的浏览器屏幕截图。

在控制台中,我使用值 blog-v2 更新了 blog 键。几秒钟后我键入相同的 URL,这次我访问的是新博客(V2)。

显示博客 V2 的浏览器屏幕截图。

可以看出,公共 URL 是相同的,但内容已经更改。更笼统地说,此函数假定两个博客版本的 URL 不会发生变化。

我现在可以为我网站中的不同服务(博客、支持、帮助、电子商务等)添加更多键,并分别设置键值以使用正确的 URL 路径。当我为其中一个添加新版本时(例如,我迁移到新的电子商务平台),我可以配置新源并更新相应的键,从而使用新的原始路径。

该例说明了将配置数据与代码分开时可获得的灵活性。如果您已经在使用 CloudFront Functions,就可以使用 CloudFront KeyValueStore 来简化代码。

注意事项
CloudFront KeyValueStore 现已在全球所有边缘站点开放。使用 CloudFront KeyValueStore 时,您只需为公有 API 的读/写操作和 CloudFront Functions 中的读取操作使用的资源付费。如需了解更多信息,请参阅 CloudFront 定价

您可以通过 AWS 管理控制台AWS 命令行界面(AWS CLI)AWS SDK 来管理键值存储。对 AWS CloudFormation 的支持即将推出。最大键值存储大小为 5MB,并且每个函数只可以关联单个键值存储。最大键大小为 512 字节。最大值大小为 1KB。创建键值存储时,您可以在创建过程中使用 Amazon S3 上具有以下 JSON 结构的源文件导入键/值数据:

{
  "data":[
    {
      "key":"key1",
      "value":"val1"
    },
    {
      "key":"key2",
      "value":"val2"
    }
  ]
}

通过在创建时导入键/值数据,有助于自动设置新环境(例如测试或开发),以及轻松地将配置从一个环境复制到另一个环境(例如从预生产环境复制到生产环境)。

使用 CloudFront KeyValueStore 简化在边缘站点添加自定义逻辑的方式。

Danilo