我正在尝试实施定制的Akka Sink,但我无法找到一种方法来处理它内部的未来。
class EventSink(...) {
val in: Inlet[EventEnvelope2] = Inlet("EventSink")
override val shape: SinkShape[EventEnvelope2] = SinkShape(in)
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = {
new GraphStageLogic(shape) {
// This requests one element at the Sink startup.
override def preStart(): Unit = pull(in)
setHandler(in, new InHandler {
override def onPush(): Unit = {
val future = handle(grab(in))
Await.ready(future, Duration.Inf)
/*
future.onComplete {
case Success(_) =>
logger.info("pulling next events")
pull(in)
case Failure(failure) =>
logger.error(failure.getMessage, failure)
throw failure
}*/
pull(in)
}
})
}
}
private def handle(envelope: EventEnvelope2): Future[Unit] = {
val EventEnvelope2(query.Sequence(offset), _/*persistenceId*/, _/*sequenceNr*/, event) = envelope
...
db.run(statements.transactionally)
}
}
此刻我必须阻止未来,这看起来不太好。我注释掉的非阻塞只适用于第一个事件。有人可以帮忙吗?
已更新感谢@ViktorKlang。它似乎现在正在运作。
override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
{
new GraphStageLogic(shape) {
val callback = getAsyncCallback[Try[Unit]] {
case Success(_) =>
//completeStage()
pull(in)
case Failure(error) =>
failStage(error)
}
// This requests one element at the Sink startup.
override def preStart(): Unit = {
pull(in)
}
setHandler(in, new InHandler {
override def onPush(): Unit = {
val future = handle(grab(in))
future.onComplete { result =>
callback.invoke(result)
}
}
})
}
}
我正在尝试实现与ReadJournal.eventsByTag连接的Rational DB事件接收器。所以这是一个连续的流,除非出现错误,否则永远不会结束 - 这就是我想要的。我的方法是否正确?
还有两个问题:
除非我手动调用completeStage或failStage,否则GraphStage是否永远不会结束?
在preStart方法之外声明回调是正确还是正常?在这种情况下,我是否可以在preStart中调用pull(in)?
谢谢, 程
答案 0 :(得分:0)
避免自定义阶段
一般情况下,您应该尝试使用库Source
,Flow
和Sink
的给定方法来消耗所有可能性。自定义阶段几乎不需要,并且使代码难以维护。
写你的"自定义"使用标准方法的阶段
根据您问题的示例代码的详细信息,我没有看到您开始使用自定义Sink
的原因。
根据您的handle
方法,您可以稍微修改它以执行您在问题中指定的日志记录:
val loggedHandle : (EventEnvelope2) => Future[Unit] =
handle(_) transform {
case Success(_) => {
logger.info("pulling next events")
Success(Unit)
}
case Failure(failure) => {
logger.error(failure.getMessage, failure)
Failure(failure)
}
}
然后只需使用Sink.foreachParallel
来处理信封:
val createEventEnvelope2Sink : (Int) => Sink[EventEnvelope2, Future[Done]] =
(parallelism) =>
Sink[EventEnvelope2].foreachParallel(parallelism)(handle _)
现在,即使您希望将每个EventEnvelope2
发送到数据库,也可以使用1
进行并行操作:
val inOrderDBInsertSink : Sink[EventEnvelope2, Future[Done]] =
createEventEnvelope2Sink(1)
此外,如果数据库抛出异常,您仍可在foreachParallel
完成时获取它:
val someEnvelopeSource : Source[EventEnvelope2, _] = ???
someEnvelopeSource
.to(createEventEnvelope2Sink(1))
.run()
.andThen {
case Failure(throwable) => { /*deal with db exception*/ }
case Success(_) => { /*all inserts succeeded*/ }
}