在Scala中使用Akka流读取多个文件

时间:2019-06-25 21:34:36

标签: scala akka akka-stream

我正在尝试使用akka流读取多个文件,并将结果放入列表中。 我可以毫无问题地读取一个文件。返回类型为Future [Seq [String]]。问题是处理Future内部的序列必须放在onComplete {}内部。

我正在尝试以下代码,但显然无法正常工作。 onComplete之外的列表acc为空。但将值保存在inComplete中。我了解这个问题,但我不知道该如何解决。

// works fine  
def readStream(path: String, date: String): Future[Seq[String]] = {
implicit val system = ActorSystem("Sys")
val settings = ActorMaterializerSettings(system)
implicit val materializer = ActorMaterializer(settings)

val result: Future[Seq[String]] =
  FileIO.fromPath(Paths.get(path + "transactions_" + date + 
".data"))
    .via(Framing.delimiter(ByteString("\n"), 256, true))
    .map(_.utf8String)
    .toMat(Sink.seq)(Keep.right)
    .run()
 var aa: List[scala.Array[String]] = Nil
 result.onComplete(x => {
  aa = x.get.map(line => line.split('|')).toList
})
 result
}

//this won't work  
def concatFiles(path : String, date : String, numberOfDays : Int) : 
List[scala.Array[String]] = {
val formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
val formattedDate = LocalDate.parse(date, formatter);
var acc = List[scala.Array[String]]()

for( a <- 0 to numberOfDays){
  val date = formattedDate.minusDays(a).toString().replace("-", "")


  val transactions = readStream(path , date)
  var result: List[scala.Array[String]] = Nil
  transactions.onComplete(x => {
    result = x.get.map(line => line.split('|')).toList 
    acc=  acc ++ result })
}
acc}

1 个答案:

答案 0 :(得分:1)

常规解决方案

鉴于Paths值的迭代器,可以通过组合FileIOflatMapConcat来创建文件行的Source

val lineSourceFromPaths : (() => Iterator[Path]) => Source[String, _] = pathsIterator =>
  Source
    .fromIterator(pathsIterator)
    .flatMapConcat { path =>
      FileIO
        .fromPath(path)
        .via(Framing.delimiter(ByteString("\n"), 256, true))
        .map(_.utf8String)
    }

问题申请

您的List为空的原因是因为Future的值尚未完成,因此可变的列表在函数返回列表之前不会被更新。

有关代码的评论

问题中代码的组织和样式表明与akkaFuture有关的一些误解。我认为您正在尝试一个相当复杂的工作流程,而没有了解要使用的工具的基本原理。

1。不应在每次调用函数时创建一个ActorSystem。通常每个应用程序有1个ActorSystem,并且仅创建一次。

implicit val system = ActorSystem("Sys")
val settings = ActorMaterializerSettings(system)
implicit val materializer = ActorMaterializer(settings)

def readStream(...

2。您应避免使用可变的集合,而应使用具有相应功能的Iterator

def concatFiles(path : String, date : String, numberOfDays : Int) : List[scala.Array[String]] = {

  val formattedDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyyMMdd"))

  val pathsIterator : () => Iterator[Path] = () => 
    Iterator
      .range(0, numberOfDays+1)
      .map(formattedDate.minusDays)
      .map(_.String().replace("-", "")
      .map(path => Paths.get(path + "transactions_" + date + ".data")

  lineSourceFromPaths(pathsIterator)

3。由于您正在与期货交易,因此您不应该等待期货交易完成,而应该将concateFiles的退货类型更改为Future[List[Array[String]]]