重用Source [ByteString,Any]的任何方法(不将其全部保留在内存中)

时间:2018-07-09 20:22:42

标签: akka-stream akka-http

有什么方法可以使Source可重用?

我有一个akka-http服务器,该服务器接收大文件上传,然后通过HTTP POST将(分块的)数据流传输到订户websocket和其他HTTP服务器。在这两种情况下,都有一个API可以接受Source [ByteString,Any]:

    在HTTP POST中
  • HttpEntity(...,源)
  • Websocket的BinaryMessage(源)

与采用单个ByteString的版本相比,使用这些API具有一些优势(仅需要执行单个HTTP发布,可以重新创建相同的分块消息等)。

那么有没有办法使这种工作(不将所有内容都缓存在内存中)?

val allSinks: Seq[Sink[Source[ByteString, Any], Future[Done]]] = ???

val g = RunnableGraph.fromGraph(GraphDSL.create(allSinks) { implicit builder => sinks =>
  import GraphDSL.Implicits._

  // Broadcast with an output for each subscriber
  val broadcast = builder.add(Broadcast[DataSource](sinks.size))
  Source.single(source) ~> broadcast
  sinks.foreach(broadcast ~> _)
  ClosedShape
})

1 个答案:

答案 0 :(得分:0)

来源不可重用

不幸的是,Source用尽后无法重用。数据的基础“源”可以重复使用以创建单独的Source值,但每个值最多可以在一个流上运行。

持久性

如果需要重播功能,则需要将流式传输的数据存储在持久性机制中,以方便以后重播。这种机制可能是文件系统,数据库,Kafka,...

以下是使用文件系统的模型。

传入的POST邮件正文可以以写模式流式传输到文件:

post {
  path(Segment) { fileName =>
    extractRequestEntity { entity =>
      complete {
        entity
          .dataBytes
          .toMat(FileIO.toPath(Paths.get(fileName), Set(CREATE_NEW, WRITE)))(Keep.Right)
          .run()
          .andThen {
            case Success(ioResult) =>
              StatusCodes.Ok -> s"wrote ${ioResult.count} bytes"
            case Failure(ex) =>
              StatusCodes.InternalServerError -> ex.toString
          } 
        }
      }
    }
  }
}

于是,您无需创建Broadcast集线器,只需使用文件内容响应GET请求:

path(Segment) { fileName =>
  getFromFile(fileName)
}

利用以下事实:大多数操作系统将允许您以字节流的形式写入文件,同时以字节流的形式从文件中读取数据...