Akka HTTP Source Streaming与常规请求处理

时间:2018-03-19 16:42:45

标签: akka-http http-streaming http-pipelining

使用Source Streaming与常规处理请求的方式有什么好处?我的理解是在两种情况下

  1. 将重用TCP连接
  2. 将在客户端和服务器之间应用背压
  3. 我可以看到源流的唯一优势是,如果有非常大的响应,并且客户端更喜欢以较小的块使用它。

    我的用例是我有一个很长的用户列表(数百万),我需要调用一个对用户执行一些过滤的服务,并返回一个子集。

    目前,在服务器端我公开了一个批处理API,在客户端上,我只是将用户分成1000个块,并使用Akka HTTP Host API并行进行X批量调用。

    我正在考虑切换到HTTP流式传输,但无法弄清楚值是什么

1 个答案:

答案 0 :(得分:1)

您缺少另一个巨大的好处:内存效率。通过拥有流式管道,客户端/服务器/客户端,所有各方都可以安全地处理数据,而不会冒着耗尽内存分配的风险。这在服务器端特别有用,你总是必须假设客户端可能做恶意的事情......

创建客户请求

假设您的数百万用户的最终来源是一个文件。您可以从此文件创建流源:

val userFilePath : java.nio.file.Path = ???

val userFileSource = akka.stream.scaladsl.FileIO(userFilePath)

您可以使用此源创建您的http请求,该请求会将用户流式传输到服务:

import akka.http.scaladsl.model.HttpEntity.{Chunked, ChunkStreamPart}
import akka.http.scaladsl.model.{RequestEntity, ContentTypes, HttpRequest}

val httpRequest : HttpRequest = 
  HttpRequest(uri = "http://filterService.io", 
              entity = Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, userFileSource))

此请求现在将用户流式传输到服务而不会将整个文件都消耗到内存中。一次只缓存数据块,因此,您可以发送可能无限的请求用户和您的客户数量都会很好。

服务器请求处理

同样,您的服务器可以设计为接受具有可能无限长度的实体的请求。

您的问题是服务将过滤用户,假设我们有过滤功能:

val isValidUser : (String) => Boolean = ???

这可用于过滤传入的请求实体并创建一个响应实体,该实体将提供响应:

import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.model.HttpEntity.Chunked

val route = extractDataBytes { userSource =>
  val responseSource : Source[ByteString, _] = 
    userSource
      .map(_.utf8String)
      .filter(isValidUser)
      .map(ByteString.apply)

  complete(HttpResponse(entity=Chunked.fromData(ContentTypes.`text/plain(UTF-8)`, 
                                                responseSource)))
}

客户响应处理

客户端可以类似地处理过滤后的用户,而无需将其全部读入内存。例如,我们可以调度请求并将所有有效用户发送到控制台:

import akka.http.scaladsl.Http

Http()
  .singleRequest(httpRequest)
  .map { response =>
    response
      .entity
      .dataBytes
      .map(_.utf8String)
      .foreach(System.out.println)
  }