是否有一些代码示例使用org.reactivestreams库来处理使用Java NIO的大数据流(为了获得高性能)?我的目标是分布式处理,所以使用Akka的例子最好,但我可以解决这个问题。
似乎情况似乎是大多数(我希望不是全部)在scala中读取文件的示例依赖Source
(非二进制)或直接Java NIO(甚至像Files.readAllBytes
之类的东西!)
也许我错过了一个激活模板? (Akka Streams with Scala!接近解决我需要的一切,除了二进制/ NIO端)
答案 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]
,你就可以开始应用任何流转换(map
,flatMap
,transform
,等...)你想要它。此功能利用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
情况。当我将其切换到新代码时,我能够避免这个问题。