scala Play框架转发请求/响应,具有较大的主体

dtcbnfnu  于 2023-10-18  发布在  Scala
关注(0)|答案(1)|浏览(113)

我正在使用Play框架将请求转发到一些内部服务。其中一个服务返回相当大的有效负载(5MB json数据)。当我直接调用服务时,处理和返回请求大约需要5秒(正在进行一些处理)。当我使用play转发请求并返回响应时,需要3倍的时间。我还得到了一些java.lang.OutOfMemoryError: Java heap space at play.shaded.ahc.org.asynchttpclient.netty.NettyResponse.getResponseBodyAsByteBuffer错误,这是怀疑来自解析json体,这是不必要的。有没有办法转发请求并返回响应而不解析它?
目前我的代码看起来像这样:

def process = Authorized().async { request =>
  request.body.asJson.map { body =>
    ws.url("internal-service/process")
      .addHttpHeaders("Accept-Encoding" -> "gzip, deflate")
      .post(body).map { res =>
      if (res.status >= 200 && res.status < 300) {
        Ok(res.json)
      } else {
        throw SimpleServiceException(s"Failed to process datasets. Reason ${res.status}: ${res.body}")
      }
    }
  }.getOrElse(Future.successful(BadRequest))
}
7jmck4yq

7jmck4yq1#

首先,让我们了解是什么导致了代码中的问题。
当你调用res.json时,你告诉Play完全使用响应,将其解析为JSON,将其保存在内存中,然后能够将其发送回来。
这不是你想做的。
相反,您可以将响应解析为Source(Akka流),以便延迟/渐进地使用它。
请执行以下操作:

ws
  .url(...)
  .withMethod("POST")
  .stream()
  .map { response =>
    Ok.chunked(response.bodyAsSource)
  }

最重要的两件事是:

  • 使用.withMethod(...).stream()而不是.post()来获取流响应
  • 使用Ok.chunked(...)流回客户端

有关详细信息,请参阅此有用的文档。它甚至包含了一个代码示例,用于您想要做的事情:
响应体的另一个常见目的地是将它们从控制器的Action流回:

def downloadFile = Action.async {   // Make the request  
  ws.url(url).withMethod("GET").stream().map { response =>
    // Check that the response was successful
    if (response.status == 200) {
      // Get the content type
      val contentType = response.headers
        .get("Content-Type")
        .flatMap(_.headOption)
        .getOrElse("application/octet-stream")

      // If there's a content length, send that, otherwise return the body chunked
      response.headers.get("Content-Length") match {
        case Some(Seq(length)) =>
          Ok.sendEntity(HttpEntity.Streamed(response.bodyAsSource, Some(length.toLong), Some(contentType)))
        case _ =>

          Ok.chunked(response.bodyAsSource).as(contentType)
      }
    } else {
      BadGateway
    }  
  }
}

你可能已经注意到了,在调用stream()之前,我们需要通过对请求调用withMethod来设置要使用的HTTP方法。

相关问题