2017-02-19 132 views
1

我写的应用程序的一部分需要从客户端向服务器传输任意大的(对于此问题,我将假设100-200 GB)的文件。重要的是,接收器(服务器)不存储这个文件 - 它只是读取/检查流并将其发送到下一个点。因为在任何时候我都需要整个文件,但是希望同时进行多次传输,所以我想尽量减少内存使用量并消除磁盘​​使用量。我想以1 MB大块处理文件。通过反应流发送大文件

现在,服务器使用Spring Boot和Akka。

我的第一次尝试是在客户端打开缓冲的文件输入流,以1 MB的块为单位读取它,并以单独的线程在消息中发送它们。它可以工作,但问题在于客户端一个接一个地发送消息,而不必担心服务器是否有缓冲区来存储消息(缺乏背压)。

我的第二个想法是用阿卡流是这样的:

How to use Reactive Streams for NIO binary processing?

利用ActorPublisher的是这样的:

akka-streams with akka-cluster

然而,如前所述这里:

http://doc.akka.io/docs/akka/2.4.16/scala/stream/stream-integrations.html#Implementing_Reactive_Streams_Publisher_or_Subscriber

“Warning ActorPublisher和ActorSubscriber可能会在未来版本的Akka中被弃用。

警告 ActorPublisher和ActorSubscriber不能与远程参与者使用,因为如果反应流协议(例如请求)的信号丢失的流可能死锁。”

它不像好主意。

我不想将它保存在任何存储提供程序(dropbox,谷歌驱动器,...),因为我想动态分析数据。我有弹簧5和Akka在船上,但我可以使用任何其他软件,这将解决此问题。原始插座将缺乏背压和山洪不保证顺序/有序的读写(我需要)。

主要问题是:如何将大文件从客户端传输到服务器,假设服务器无法一次将文件存储在磁盘或ram中?

奖金的问题是:如何计算在这种转移中的“正确”大块大小?

我一直在寻找答案的日子,看起来像我不是唯一一个这样的问题,但没有指出其他适当的替代解决方案没有答案或答案像“不这样做”。

+1

目前尚不清楚您的问题到底是什么。Akka流提供了你所需要的所有工具 - TCP套接字周围有流封装(当然还有背压),并且有'GraphStage'(这是你可以用来实现处理的ActorSubscriber和ActorPublisher的预期替代品)如果没有默认的组合器适合你,则为逻辑。你只需要合并它们。 –

+0

你会如此友善并向我展示一些远程流的最小例子吗? – spam

+1

当然,这是:https://gist.github.com/netvl/1245564b106c02691dd0808fe98d07eb。它非常肮脏(特别是在服务器关机处理时),但它应该传达基本的想法。它使用原始的TCP套接字进行通信;你也可能想使用akka-http,因为它也给你更简单的TLS配置(尽管也可以用原始TCP流来完成)。 TCP流被记录[在这里](http://doc.akka.io/docs/akka/2.4/scala/stream/stream-io.html#streaming-tcp),并且akka-http记录在[这里](http ://doc.akka.io/docs/akka-http/current/scala.html)。 –

回答

3

Akka流特别为此用例提供功能:streaming File IO。从文档:

import akka.stream.scaladsl._ 
val file = Paths.get("example.csv") 

val foreach: Future[IOResult] = 
    FileIO.fromPath(file) 
     .to(Sink.ignore) 
     .run() 

关于你的奖励问题的“大小正确的大块;这高度依赖于您的硬件和软件配置。最好的办法是编写一个测试客户端并调整块大小,直到找到服务器的“最佳位置”。