如何使用Reactive Streams进行NIO二进制处理?

时间:2015-01-05 02:08:53

标签: scala akka nio reactive-streams

是否有一些代码示例使用org.reactivestreams库来处理使用Java NIO的大数据流(为了获得高性能)?我的目标是分布式处理,所以使用Akka的例子最好,但我可以解决这个问题。

似乎情况似乎是大多数(我希望不是全部)在scala中读取文件的示例依赖Source(非二进制)或直接Java NIO(甚至像Files.readAllBytes之类的东西!)

也许我错过了一个激活模板? (Akka Streams with Scala!接近解决我需要的一切,除了二进制/ NIO端)

2 个答案:

答案 0 :(得分:9)

不要使用scala.collection.immutable.Stream来使用这样的文件,原因是它执行了memoization - 也就是说,它是懒惰的,它会将整个流缓存(memoized)保存在内存中!

当您考虑"流处理文件时,这绝对是你想要的"。 Scala的Stream工作原理是因为在功能设置中它完全有意义 - 你可以避免一次又一次地反复计算fibbonachi数字,例如,有关详细信息,请参阅ScalaDoc。 / p>

Akka Streams提供了Reactive Streams实现,并提供了一个FileIO类,您可以在这里使用它(只有在需要时它才会正确地反压并将数据从文件中提取出来,并且其余的流已准备好消耗它):

import java.io._
import akka.actor.ActorSystem
import akka.stream.scaladsl.{ Sink, Source }

object ExampleApp extends App {


  implicit val sys = ActorSystem()
  implicit val mat = FlowMaterializer()

  FileIO.fromPath(Paths.get("/example/file.txt"))
    .map(c ⇒ { print(c); c })
    .runWith(Sink.onComplete(_ ⇒ { f.close(); sys.shutdown() } ))
}

以下是有关使用IO with Akka Streams的更多文档 请注意,这是针对Akka的当前编写版本,因此2.5.x系列。

希望这有帮助!

答案 1 :(得分:4)

我们实际上使用akka流来处理二进制文件。让事情继续下去有点棘手,因为没有任何相关的文档,但这就是我们提出的:

val binFile = new File(filePath)
val inputStream = new BufferedInputStream(new FileInputStream(binFile))
val binStream = Stream.continually(inputStream.read).takeWhile(-1 != _).map(_.toByte) 
val binSource = Source(binStream)

一旦你拥有binSource,这是一个akka Source[Byte],你就可以开始应用任何流转换(mapflatMaptransform,等...)你想要它。此功能利用Source伴随对象的apply,其Iterable传入scala Stream,该scala val binFile = new File(filePath) val inputStream = new BufferedInputStream(new FileInputStream(binFile)) val binSource = Source(() => binStream(inputStream).iterator) def binStream(in:BufferedInputStream) = Stream.continually(in.read).takeWhile(-1 != _).map(_.toByte) 应该懒惰地读入数据并使其可用于转换。< / p>

修改

正如Konrad在评论部分指出的那样,Stream可能是大文件的一个问题,因为它会对它遇到的元素执行memoization,因为它懒得构建流。如果您不小心,这可能会导致内存不足。但是,如果您查看Stream的文档,可以提示避免在内存中构建内存:

  

必须谨慎记忆;你可以很快吃大了   如果你不小心你的记忆量。原因是这样的   Stream的memoization创建了一个类似的结构   scala.collection.immutable.List。只要有东西坚持下去   头部,头部紧贴尾部,继续   递归。另一方面,如果没有什么可以坚持的话   head(例如我们使用def定义Stream)然后一旦它不再   直接使用它会消失。

因此,考虑到这一点,您可以按如下方式修改我的原始示例:

Stream

所以这里的想法是通过def构建val而不是分配给iterator,然后立即从中获取Source并使用它来初始化阿卡[{1}}。以这种方式设置应该避免模仿的问题。我针对一个大文件运行旧代码,并通过在OutOfMemory上执行foreach来产生Source情况。当我将其切换到新代码时,我能够避免这个问题。