来自 Play Framework 的 DynamoDB 异步访问

DynamoDB async access from Play Framework

我需要从 Play Framework 应用程序访问和写入 DynamoDB。 已经有几个关于这个主题的问题(here, here and here;但是他们都至少有 3 岁)。

问题的答案通常是使用包装器(AWScala by seratch)或专用于 Play 的库:

然而,包装器只是在后台调用 SDK 的同步版本。如果可能的话,我希望能够在新版本发布后立即更新 AWS SDK,而不是依赖于首先更新使用过的 Scala/Play 库。所以对我来说最好的选择是 aws-scala-sdk wrapper generator by awslabs。异步包装器使用例如 Future<PutItemResult> putItemAsync(PutItemRequest putItemRequest, AsyncHandler<PutItemRequest,PutItemResult> asyncHandler) 方法仍然 returns Java Future,但也可以使用 AsyncHandler 的回调来驱动 Scala 的响应Future:

val promise = scala.concurrent.Promise[PutItemResult]
dynamoDBAsync(request, new com.amazonaws.handlers.AsyncHandler[PutItemRequest, PutItemResult]() {
  override def onSuccess(request: PutItemRequest, result: PutItemResult) = promise.success(Ok)
  override def onError(exception: Exception) = promise.failure(exception)
})
promise.future

像这样的代码是由 aws-scala-sdk 生成器生成的。这种方法可以安全地与 Play 和默认 ExecutionContext 一起使用,还是它仍然会遇到阻塞线程的问题,例如调用 Java 的 Future.get()?

在用 Play 对 DynamoDB 做了很多时间的压力测试后,我相当乐观地使用 com.amazonaws.handlers.AsyncHandler

测试设置:一个服务器实例(类型不同)和几个专用请求程序实例 (m4.large)。每个请求都包含一个写入 DynamoDB 的 JSON 负载(大约 200 字节)。每个请求者实例启动几个处理实际请求的线程。请求在一段时间内均匀分布,每个请求者实例以交错方式增加其线程,因此 DynamoDB 不会限制任何请求(table 具有 10000 个预置写入容量单位,根据云观察)。 ulimit -n(打开的文件数)在服务器实例上增加到 20000,否则服务器会表现得很奇怪(进程不再侦听端口 9000,但一些线程仍然能够分派请求,而其他线程则不能)不)超过大约 3500-4000 个请求者线程。最大 Java 堆大小为 8GB。

我的发现如下:

  • m4.large 服务器实例:低于 2500-2700 requests/s 响应时间平均 <60 毫秒。写入吞吐量上限约为 3700-3800 requests/s。瓶颈是 CPU,持续加载 100%。

  • m4.4xlarge服务器实例:无论请求线程多少,响应时间始终<50ms,平均在10ms左右或更低。我能够最大化 10000 个预置写入容量单位...