Playframework Play Framework 2.8的alpakka s3文件下载问题

Playframework Play Framework 2.8的alpakka s3文件下载问题,playframework,akka-http,alpakka,Playframework,Akka Http,Alpakka,我在Play framework 2.8上下载了一个简单的alpakka s3文件,代码如下: akka { loglevel = DEBUG http { client { connecting-timeout = 5 s idle-timeout = 5 s parsing { max-content-length = 3000m } } server { parsing {

我在Play framework 2.8上下载了一个简单的alpakka s3文件,代码如下:

akka {
  loglevel = DEBUG

  http {
    client {
      connecting-timeout = 5 s
      idle-timeout = 5 s
      parsing {
        max-content-length = 3000m
      }
    }
    server {
      parsing {
        max-content-length = 3000m
      }
    }

    host-connection-pool {
      max-open-requests = 1024
      idle-timeout = 10 s
      client {
        connecting-timeout = 10 s
        idle-timeout = 10 s
      }
    }
  }
}
val AkkaVersion = "2.5.31"
val AkkaHttpVersion = "10.1.12"
libraryDependencies ++= Seq(
  "com.lightbend.akka" %% "akka-stream-alpakka-s3" % "2.0.1",
  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-xml" % AkkaHttpVersion
)
在S3服务中:

def download(link: String): Source[Option[(Source[ByteString, NotUsed], ObjectMetadata)], NotUsed] = {
    S3.download(TraktrainBucket.DOWNLOAD_BUCKET, link)
}
在控制器中:

          val source = s3Service.download(link).map(s => s.map(_._1))
          val trackName = "track name"
          val filename = trackName.replaceAll("[^A-Za-z0-9 \\-.]", "") + (if (track.drumKit) ".zip" else ".mp3")
          val disposition = "attachment; filename=\"" + filename + "\""
          Result(
            header = ResponseHeader(200, Map("Content-Disposition" -> disposition)),
            body = HttpEntity.Streamed(source.flatMapConcat(_.getOrElse(Source.empty)), None, Some("application/octet-stream"))
          )
我还有一个上传功能(它需要一个mp3文件,用ffmpeg处理,然后像这样上传到s3:

def richUploadMp3(extension: String, checkFunction: (String, Option[String]) => Boolean, cannedAcl: CannedAcl, bucket: String) = producerAction(parse.multipartFormData(handleFilePartAsFile)).async { implicit request =>
    val s3Filename = request.user.get.id + "/" + java.util.UUID.randomUUID.toString + "." + extension
    val s3FilenameTagged = request.user.get.id + "/" + java.util.UUID.randomUUID.toString + "." + extension
    val fileOption = request.body.file("file").map {
      case FilePart(key, filename, contentType, file, _, _) =>
        logger.info(s"key = ${key}, filename = ${filename}, contentType = ${contentType}, file = $file")
        if(checkFunction(filename, contentType)) {
          val taggedFile = audioService.putTag(file)
          for {
            mp3 <- FileIO.fromPath(file.toPath).runWith(s3Service.uploadSink(s3Filename, cannedAcl, TraktrainBucket.DOWNLOAD_BUCKET))
            mp3Tagged <- FileIO.fromPath(taggedFile.toPath).runWith(s3Service.uploadSink(s3FilenameTagged, cannedAcl, TraktrainBucket.STREAMING_BUCKET))
          } yield (mp3, mp3Tagged, file, taggedFile)
        } else {
          throw new Exception("Upload failed")
        }
    }
    fileOption match {
      case Some(opt) => opt.map(o => {
        o._3.delete()
        o._4.delete()
        Ok(Json.toJson(Seq(s3Filename, s3FilenameTagged)))
      })
      case _ => Future.successful(BadRequest("ERROR"))
    }
  }
而且它并没有逃脱,似乎阿克卡只是打开请求,而不是关闭它

我的akka http配置如下所示:

akka {
  loglevel = DEBUG

  http {
    client {
      connecting-timeout = 5 s
      idle-timeout = 5 s
      parsing {
        max-content-length = 3000m
      }
    }
    server {
      parsing {
        max-content-length = 3000m
      }
    }

    host-connection-pool {
      max-open-requests = 1024
      idle-timeout = 10 s
      client {
        connecting-timeout = 10 s
        idle-timeout = 10 s
      }
    }
  }
}
val AkkaVersion = "2.5.31"
val AkkaHttpVersion = "10.1.12"
libraryDependencies ++= Seq(
  "com.lightbend.akka" %% "akka-stream-alpakka-s3" % "2.0.1",
  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-xml" % AkkaHttpVersion
)
有时我在日志中看到这样的事情:

Response stream for [GET /free/642241] failed with 'TCP idle-timeout encountered on connection to [s3.us-west-2.amazonaws.com:443], no bytes passed in the last 10 seconds'. Aborting connection.
不关闭连接有什么问题?我如何监控它?我甚至没有找到任何方法来跟踪打开的请求,如何修复它

我的build.sbt中的alpakka如下所示:

akka {
  loglevel = DEBUG

  http {
    client {
      connecting-timeout = 5 s
      idle-timeout = 5 s
      parsing {
        max-content-length = 3000m
      }
    }
    server {
      parsing {
        max-content-length = 3000m
      }
    }

    host-connection-pool {
      max-open-requests = 1024
      idle-timeout = 10 s
      client {
        connecting-timeout = 10 s
        idle-timeout = 10 s
      }
    }
  }
}
val AkkaVersion = "2.5.31"
val AkkaHttpVersion = "10.1.12"
libraryDependencies ++= Seq(
  "com.lightbend.akka" %% "akka-stream-alpakka-s3" % "2.0.1",
  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,
  "com.typesafe.akka" %% "akka-http-xml" % AkkaHttpVersion
)

查看为后端Akka HTTP服务器制作的播放设置中的设置。如果您的
requestTimeout
设置为
infinite
,这是默认设置,请将其更改为应用程序的合理时间限制。当连接出错时,这可能会导致长时间运行的连接。如下所述,这些配置ons覆盖Akka配置

注意:Akka HTTP有许多超时配置,您可以 用于保护应用程序免受攻击或编程错误。 使用中的Akka HTTP服务器将自动识别所有这些 Akka配置。例如,如果您有空闲超时和 请求超时配置如下所示:
akka.http.server.idle-timeout=20s
akka.http.server.request-timeout=30s
它们将被自动识别。请记住该游戏 上面列出的配置将覆盖Akka配置

在Linux中尝试使用
netstat-tnp
命令查看所有当前打开的TCP连接。如果您有一个测试环境,您可以启动clean并测试一个功能(和一个文件)通常,在S3完成文件处理后,大约10秒后,根据您的设置,Akka HTTP将使连接空闲(因此出现了您在帖子中提到的
TCP空闲超时…
消息)如果在多次尝试之后,空闲的工作对于某个特性来说是可行的,那么切换到另一个特性进行测试。如果这对于使用AKK-HTTP的所有特性来说都是很好的,那么您需要考虑<代码>最大打开请求> /COD>对您的生产环境是否足够。这取决于您收到多少请求以及您的速度有多快。可以在给定的时间段内处理

此外,Akka HTTP是一个“一路流”库,因此它需要数据(
HttpEntity
)在发送更多之前被消耗。因此,仅发送200 OK是不够的。缺少200 OK将导致背压,从而导致在最大限制
max open requests
之前建立新连接。这可能是您的情况。请检查此项

池过载的常见原因:

池过载的常见原因如上所述 池过载的解释是传入的请求速率为 高于请求处理速率。这可以有各种 原因(以及括号中的修复提示):

  • 服务器速度太慢(提高服务器性能)
  • 网络速度太慢(提高网络性能)
  • 客户端发出请求的速度太快(如果可能,请减慢请求的创建速度)
  • 客户机和服务器之间的延迟很高(使用更多并发连接来消除并行延迟)
  • 请求速率存在峰值(通过调整客户端应用程序或增加最大打开请求数以缓冲短期峰值来防止峰值)
  • 未读取或丢弃响应实体(请参阅Http实体流性质的含义)
  • 某些请求的速度比其他请求的速度慢,阻止了池中其他请求的连接(见下文)
最后一点可能需要更多 说明。如果某些请求比其他请求慢得多,例如,如果 请求是一个长时间运行的服务器发送事件请求,而不是此请求 长时间阻止池的一个连接。如果存在 同时发生多个此类请求将导致 饥饿和其他请求无法再取得任何进展。请继续 确保在专用连接上运行长时间运行的请求(使用 连接级别客户端API)来防止这种情况

为什么这只发生在Akka Http上,而不发生在[insert other]上 客户]

许多Java HTTP客户机在默认情况下不会为其中一些设置限制 所使用的资源。例如,某些客户端不会对请求排队,但 将只打开与服务器的另一个连接(如果所有池 连接当前正忙。但是,这可能只是移动 从客户端到服务器出现问题。也使用了过多的数字 连接数越多,网络性能越差 连接将争夺带宽

还要查看Akka HTTP的S3示例页面

查看Akka HTTP中的超时

您可以在此处看到每个confid选项的详细说明

例如,下面是说明
请求超时

# Defines the default time period within which the application has to
# produce an HttpResponse for any given HttpRequest it received.
# The timeout begins to run when the *end* of the request has been
# received, so even potentially long uploads can have a short timeout.
# Set to `infinite` to completely disable request timeout checking.
#
# Make sure this timeout is smaller than the idle-timeout, otherwise,
# the idle-timeout will kick in first and reset the TCP connection
# without a response.
#
# If this setting is not `infinite` the HTTP server layer attaches a
# `Timeout-Access` header to the request, which enables programmatic
# customization of the timeout period and timeout response for each
# request individually.
request-timeout = 20 s

现在是什么解决方法?重新启动服务器?重新启动后,您是否看到遇到的
TCP空闲超时
错误?在它卡住之前,您会看到多少这样的错误?您的机器还在做什么?另一件事,关于您附加的错误,有一个文档参考,这可能解释您遇到的一些问题g:是的,只有服务器重新启动。大约有700个“连接时遇到TCP空闲超时”错误,另外大约有250个:[GET/free/556601]的响应流“实体流截断失败。当基础连接意外关闭时,HTTP解析器正在接收实体。”。正在中止连接。akka.HTTP.scaladsl.model.EntityStreamException:实体流trunc