最近,我开始使用Akka,并且正在使用Akka HTTP通过Akka HTTP创建REST API,以上传文件。该文件可以包含数百万条记录,对于每条记录,我需要执行一些验证和业务逻辑。我为actor建模的方式是,根actor接收文件流,将字节转换为String,然后按行分隔符拆分记录。完成此操作后,它将流(逐条记录)发送到另一个参与者以进行处理,后者再根据某些分组将记录分配给其他参与者。要将蒸汽从主要根演员发送到演员进行处理,我正在使用Sink.actorRefWithAck
。
这对于一个小文件来说很好用,但是对于一个大文件,我观察到的是,我得到了多个块,并且第一个块正在处理中。如果我根据负载添加Thread.sleep
几秒钟,则它正在处理整个文件。我想知道是否有什么方法可以知道流是否已被处理角色完全消耗,因此我不必处理Thread.sleep
。这是我使用的代码段:
val AckMessage = DefaultFileUploadProcessActor.Ack
val receiver = context.system.actorOf(
Props(new DefaultFileUploadProcessActor(uuid, sourceId)(self, ackWith = AckMessage)))
// sent from stream to actor to indicate start, end or failure of stream:
val InitMessage = DefaultFileUploadProcessActor.StreamInitialized
val OnCompleteMessage = DefaultFileUploadProcessActor.StreamCompleted
val onErrorMessage = (ex: Throwable) => DefaultFileUploadProcessActor.StreamFailure(ex)
val actorSink = Sink.actorRefWithAck(
receiver,
onInitMessage = InitMessage,
ackMessage = AckMessage,
onCompleteMessage = OnCompleteMessage,
onFailureMessage = onErrorMessage
)
val processStream =
fileStream
.map(byte => byte.utf8String.split(System.lineSeparator()))
.runWith(actorSink)
Thread.sleep(9000)
log.info(s"completed distribution of data to the actors")
sender() ! ActionPerformed(uuid, "Done")
对于我采用的方法的任何专家建议,我们将不胜感激。
答案 0 :(得分:0)
如果您的Source只有一个文件,则可以通过等待从runWith方法返回的Future来等待流完成。
如果您有多个文件的来源,则应编写如下内容:
filesSource
.mapAsync(1)(data => (receiver ? data).mapTo[ProcessingResult])
.mapAsync(1)(processingResult => (resultListener ? processingResult).mapTo[ListenerResponse])
.runWith(Sink.ignore)
答案 1 :(得分:0)
当流成功完成或失败后,receiver
操作者将收到OnCompleteMessage
或onErrorMessage
,因此您应该在receive
块中处理这些消息。接收者DefaultFileUploadProcessActor
演员。
答案 2 :(得分:0)
假设fileStream
是Source[ByteString, Future[IOResult]
,一个想法是保留源的物化值,然后在完成该物化值后触发对sender()
的答复:>
val processStream: Future[IOResult] =
fileStream
.map(_.utf8String.split(System.lineSeparator()))
.to(actorSink)
.run()
processStream.onComplete {
case Success(_) =>
log.info("completed distribution of data to the actors")
sender() ! ActionPerformed(uuid, "Done")
case Failure(t) =>
// ...
}
以上方法可确保在通知发件人之前将整个文件消耗掉。
请注意,Akka Streams具有一个Framing
对象,该对象可以解析ByteString
流中的行:
val processStream: Future[IOResult] =
fileStream
.via(Framing.delimiter(
ByteString(System.lineSeparator()),
maximumFrameLenght = 256,
allowTruncation = true))
.map(_.ut8String)
.to(actorSink) // the actor will have to expect String, not Array[String], messages
.run()