【问题标题】:How to using Akka Stream with Akk-Http to stream the response如何使用 Akka Stream 和 Akk-Http 流式传输响应
【发布时间】:2019-03-18 00:48:22
【问题描述】:

我是 Akka Stream 的新手。我使用以下代码进行 CSV 解析。

class CsvParser(config: Config)(implicit system: ActorSystem) extends LazyLogging with NumberValidation {

  import system.dispatcher

  private val importDirectory = Paths.get(config.getString("importer.import-directory")).toFile
  private val linesToSkip = config.getInt("importer.lines-to-skip")
  private val concurrentFiles = config.getInt("importer.concurrent-files")
  private val concurrentWrites = config.getInt("importer.concurrent-writes")
  private val nonIOParallelism = config.getInt("importer.non-io-parallelism")

  def save(r: ValidReading): Future[Unit] = {
      Future()
  }

  def parseLine(filePath: String)(line: String): Future[Reading] = Future {
    val fields = line.split(";")
    val id = fields(0).toInt
    try {
      val value = fields(1).toDouble
      ValidReading(id, value)
    } catch {
      case t: Throwable =>
        logger.error(s"Unable to parse line in $filePath:\n$line: ${t.getMessage}")
        InvalidReading(id)
    }
  }

  val lineDelimiter: Flow[ByteString, ByteString, NotUsed] =
    Framing.delimiter(ByteString("\n"), 128, allowTruncation = true)

  val parseFile: Flow[File, Reading, NotUsed] =
    Flow[File].flatMapConcat { file =>
      val src = FileSource.fromFile(file).getLines()
      val source : Source[String, NotUsed] = Source.fromIterator(() => src)
      // val gzipInputStream = new GZIPInputStream(new FileInputStream(file))

      source
        .mapAsync(parallelism = nonIOParallelism)(parseLine(file.getPath))
    }

  val computeAverage: Flow[Reading, ValidReading, NotUsed] =
    Flow[Reading].grouped(2).mapAsyncUnordered(parallelism = nonIOParallelism) { readings =>
      Future {
        val validReadings = readings.collect { case r: ValidReading => r }
        val average = if (validReadings.nonEmpty) validReadings.map(_.value).sum / validReadings.size else -1
        ValidReading(readings.head.id, average)
      }
    }

  val storeReadings: Sink[ValidReading, Future[Done]] =
    Flow[ValidReading]
      .mapAsyncUnordered(concurrentWrites)(save)
      .toMat(Sink.ignore)(Keep.right)

  val processSingleFile: Flow[File, ValidReading, NotUsed] =
    Flow[File]
      .via(parseFile)
      .via(computeAverage)

  def importFromFiles = {
    implicit val materializer = ActorMaterializer()

    val files = importDirectory.listFiles.toList
    logger.info(s"Starting import of ${files.size} files from ${importDirectory.getPath}")

    val startTime = System.currentTimeMillis()

    val balancer = GraphDSL.create() { implicit builder =>
      import GraphDSL.Implicits._

      val balance = builder.add(Balance[File](concurrentFiles))
      val merge = builder.add(Merge[ValidReading](concurrentFiles))

      (1 to concurrentFiles).foreach { _ =>
        balance ~> processSingleFile ~> merge
      }

      FlowShape(balance.in, merge.out)
    }

    Source(files)
      .via(balancer)
      .withAttributes(ActorAttributes.supervisionStrategy { e =>
        logger.error("Exception thrown during stream processing", e)
        Supervision.Resume
      })
      .runWith(storeReadings)
      .andThen {
        case Success(_) =>
          val elapsedTime = (System.currentTimeMillis() - startTime) / 1000.0
          logger.info(s"Import finished in ${elapsedTime}s")
        case Failure(e) => logger.error("Import failed", e)
      }
  }
}

我想使用 Akka HTTP,它会提供从 CSV 解析的所有 ValidReading 实体,但我不明白该怎么做。

以上代码从服务器获取文件并解析每一行以生成ValidReading。

如何通过 akka-http 传递/上传 CSV、解析文件并将结果响应流式传输回端点?

【问题讨论】:

  • 您遇到的具体问题是什么?什么不工作?
  • 以上代码没有问题。但我在问如何将结果流式传输到端点
  • 您想使用 Akka-HTTP 将结果流式传输到 HTTP 端点而不是 save?
  • 是的。我想使用 Akka-HTTP 将结果流式传输到 HTTP 端点

标签: scala akka


【解决方案1】:

解决方案的“本质”是这样的:

import akka.http.scaladsl.server.Directives._
val route = fileUpload("csv") {
  case (metadata, byteSource) =>
    val source = byteSource.map(x => x)
    complete(HttpResponse(entity = HttpEntity(ContentTypes.`text/csv(UTF-8)`, source)))
}

您检测到上传的内容是一个多部分形式的数据,其中包含一个名为“csv”的块。你从中得到 byteSource 。进行计算(将您的逻辑插入到.map(x=>x) 部分)。将您的数据转换回ByteString。使用新来源完成请求。这将使您的 endoint 像代理一样。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-06
    • 2023-04-01
    • 2017-11-12
    • 2012-04-02
    • 2013-08-20
    • 2017-12-13
    • 2018-07-08
    • 2018-02-19
    相关资源
    最近更新 更多