【问题标题】:How to scale S3 to thousands of requests per second?如何将 S3 扩展到每秒数千个请求?
【发布时间】:2019-08-21 12:19:06
【问题描述】:

AWS S3 文档状态 (https://docs.aws.amazon.com/AmazonS3/latest/dev/request-rate-perf-considerations.html):

Amazon S3 自动扩展到高请求率。例如,您的应用程序在存储桶中的每个前缀每秒可以实现至少 3,500 个 PUT/POST/DELETE 和 5,500 个 GET 请求。

为了测试这一点,我有以下 NodeJS 代码(使用 aws-sdk),它异步启动 1000 次零字节上传(因此,只需将空条目添加到存储桶中)。有一个计时器可以测量吞吐量:

var t0 = new Date().getTime()
for (var i = 0; i < 1000; i++) {
  var s3 = new AWS.S3()
  var id = uuid()
  console.log('Uploading ' + id)
  s3.upload({
      Bucket: bucket,
      Body : '',
      Key : "test/" + id
    },
    function (err, data) {
      if (data) console.log('Uploaded ' + id + ' ' + (new Date().getTime() - t0))
      else console.log('Error')
    })
}

完成所有上传请求大约需要 25 秒。这显然与声称的每秒 3500 个请求相去甚远,而是大约每秒 40 个请求。

我有大约 1MB 的网络上传速度,网络统计数据显示,在大多数情况下,带宽只有大约 25% 饱和。同样,CPU 利用率也很低。

所以问题是:

如何扩展 S3 上传吞吐量以实现接近每秒 3500 个请求的速度?

编辑:

我这样修改代码:

var t0 = new Date().getTime()
for (var i = 0; i < 1000; i++) {
  var s3 = new AWS.S3()
  var id = String.fromCharCode('a'.charCodeAt(0) + (i % 26)) + uuid()
  console.log('Uploading ' + id)
  s3.upload({
      Bucket: bucket,
      Body: '',
      Key: id
    },
    function (err, data) {
      if (data) console.log('Uploaded ' + id + ' ' + (new Date().getTime() - t0))
      else console.log('Error')
    })
}

这使用了 26 个不同的前缀,AWS 文档声称它们应该将吞吐量扩大 26 倍。

“以指数方式提高读取或写入性能很简单。例如,如果您在 Amazon S3 存储桶中创建 10 个前缀来并行读取,则可以将读取性能扩展到每秒 55,000 个读取请求。”

但是,吞吐量没有明显差异。行为存在某种差异,因此请求似乎以更并行的方式完成,而不是按顺序完成 - 但完成时间几乎相同。

最后,我尝试在 x4 个单独的 bash 线程(4 个线程、4 个内核、4x1000 个请求)中运行应用程序。尽管使用多核增加了并行性,但总执行时间约为 80 秒,因此无法扩展。

for i in {0..3}; do node index.js & done

我想知道 S3 是否会限制单个客户端/IP(尽管这似乎没有记录在案)?

【问题讨论】:

  • 您的代码在哪里运行?如果您在与您调用的 S3 端点位于同一区域的 EC2 主机上运行它(以排除您和 AWS 之间的任何瓶颈),看看您会得到什么会很有趣。另外——我注意到您正在为每个请求创建一个单独的AWS.S3 实例。我对 Node AWS SDK 不是很熟悉,但我的直觉认为那是不必要的昂贵,并且您可以通过创建单个实例、使用同步请求“预热”然后进行 1000 来获得更好的性能实际测试的异步请求。
  • 它在我的家庭网络上的本地机器上运行。我可以在大约 2 秒内启动所有 1000 个请求,因此 S3 实例不是我认为的瓶颈(另外,如果您使用单个实例,SDK 会警告过多的侦听器)。它似乎是 S3 方面的东西。不过,在 EC2 实例上尝试它会很有趣。
  • 有时,所有上传都在大约 25 秒内完成,最后一小部分需要大约 70 秒才能完成。就好像 S3 正在重新平衡最后几个索引或其他东西。
  • 您的应用程序是单线程的。这不是对访问 S3 的成百上千用户的真实模拟。您的模拟应该从多个来源运行并行测试以获得真正的测量。您是否真的有需要这种吞吐量的应用程序,或者您只是将其作为学术练习?
  • 我有一个应用程序。这是一个在线备份应用程序,可以直接从用户的机器将数百万个小对象添加到 S3。所以吞吐量是个问题。是的,它是单线程的,但它也是异步的,大部分时间都花在等待网络响应上,这意味着 CPU 负载并不是真正的问题。

标签: node.js amazon-web-services amazon-s3


【解决方案1】:

在直接回答您的问题之前,我有几件事要提一下。

首先,我做了一个实验,在大约 25 分钟内完成了 200000 个PUT/DELETE 请求,也就是每秒 130 多个请求。我上传的对象每个大约 10 kB。 (我在同一时间跨度内也有 ~125000 个GET 请求,所以我敢肯定,如果我只做PUTs,我可以达到更高的PUT 吞吐量。)我在一个m4.4xlarge 实例,它有 16 个 vCPU 和 64GB RAM,与 S3 存储桶在同一 AWS 区域中运行。

要获得更高的吞吐量,请使用更强大的硬件并尽量减少网络跃点的数量以及您与 S3 之间的潜在瓶颈。

S3 是一个分布式系统。 (他们的文档说数据被复制到多个 AZ。)它旨在同时处理来自多个客户端的请求(这就是为什么它非常适合托管静态 Web 资产)。

实际上,如果您想测试 S3 的限制,您也需要通过启动一组 EC2 实例或将测试作为 Lambda 函数运行来进行分布式。

编辑: S3 不保证响应您的请求的延迟。造成这种情况的一个原因可能是因为每个请求可能具有不同的有效负载大小。 (对 10 B 对象的 GET 请求将比 10 MB 对象快得多。)

您一直提到处理请求的时间,但这并不一定与每秒的请求数相关。 S3 每秒可以处理数千个请求,但据我所知,没有一台消费类笔记本电脑或商品服务器每秒可以发出数千个单独的网络请求。

此外,总执行时间不一定代表性能,因为当您通过网络发送内容时,总是存在网络延迟和数据包丢失的风险。您可能有一个不幸的请求,其通过网络的路径较慢,或者该请求可能比其他请求经历更多的数据包丢失。

您需要仔细定义要查找的内容,然后仔细确定如何正确测试它。

【讨论】:

  • 最后一位 +1...S3 确实很好,但它不会仅对单个主机具有最大吞吐量。
  • 我同意关于啤酒花的观点。我确信我可以通过将应用程序放在同一区域的 EC2 实例上来提高吞吐量。但是,正如我们都发现的那样,吞吐率仍然至少比引用的数字低一个数量级。分发有帮助吗?也许吧,但我还不相信,因为我相当确定大部分时间都花在等待 S3 端。
  • 我认为您将每秒最大请求数和请求延迟混为一谈。它们在分布式环境中不一定相关。
  • 绝对可能是我的机器每秒无法发出一千个请求 - 绝对如此。 Wireshark 调试似乎实际上支持这个概念 - 显示长时间的握手持续了相当长的执行时间。在测量性能方面,代码会在每次请求完成时产生一个时间输出,因此可以看出是否大部分请求在给定时间内完成,如果少数请求比其他请求花费更长的时间(有时会发生)。通常,请求在 17 秒到 25 秒之间完成,因此可以计算出大致的吞吐量。
  • 这只是你的机器和S3之间的吞吐量,其中限制因素不是S3。不用担心——S3 不会限制您。 S3 会自动进行扩展,但您看不到这一点,因为您的测试只涉及一个客户端。
【解决方案2】:

您应该查看的另一件事是使用的 HTTPS 代理。

AWS 开发工具包使用全局代理的情况过去是(并且可能仍然是)。如果您正在使用将重用连接的代理,它可能是 HTTP/1.1,并且可能出于兼容性原因禁用了流水线。

使用像 Wireshark 这样的数据包嗅探器来检查是否正在建立多个向外的连接。如果只建立一个连接,您可以在httpOptions 中指定代理。

【讨论】:

  • 这是一个很好的观点,但我认为情况并非如此。结果显示了相当明显的并行性,而且我还使用 Kotlin 协程对异步 Java API 进行了同样的尝试,结果是一样的。
  • @chris 你真的检查了吗?过去,我使用 Node.js 为 S3 编写了一些内部实用程序,看到了与您获得的类似性能,然后发现只使用了 1 或 5 个连接。这些天 Node.js 的连接限制默认为无限......但只需使用 Wireshark 进行检查即可。
  • 我刚查了一下,请求肯定是多路复用的。但是,我确实注意到整个 25 秒内持续的网络活动,这可能意味着使用单个客户端存在一些网络堆栈瓶颈。
  • @chris 是的,弄乱代理设置并获得更多并行连接,而不是将所有请求都放在一个连接中。
猜你喜欢
  • 1970-01-01
  • 2018-07-12
  • 2017-03-12
  • 2019-06-30
  • 1970-01-01
  • 2018-07-20
  • 2012-06-23
  • 2018-07-22
  • 1970-01-01
相关资源
最近更新 更多