Akka 这种阿卡卡夫卡流配置是否得益于阿卡流的背压机制?

Akka 这种阿卡卡夫卡流配置是否得益于阿卡流的背压机制?,akka,akka-stream,akka-kafka,Akka,Akka Stream,Akka Kafka,我们有一个Akka应用程序,它使用Kafka主题并将收到的消息发送给Akka参与者。我不确定我编程的方式是否充分利用了Akka Streams内置背压机制的所有优点 以下是我的配置 val control : Consumer.DrainingControl[Done] Consumer .sourceWitOffsetContext(consumerSettings, Subscriptions.topics("myTopic")) .map(consumerRecor

我们有一个Akka应用程序,它使用Kafka主题并将收到的消息发送给Akka参与者。我不确定我编程的方式是否充分利用了Akka Streams内置背压机制的所有优点

以下是我的配置

val control : Consumer.DrainingControl[Done]
Consumer
 .sourceWitOffsetContext(consumerSettings, Subscriptions.topics("myTopic"))
 .map(consumerRecord =>
     val myAvro = consumerRecord.value().asInstanceOf[MyAvro]
     
     val myActor = AkkaSystem.sharding.getMyActor(myAvro.getId)
     
     myActor ! Update(myAvro)          
 )
 .via(Commiter.flowWithOffsetContext(CommitterSettings(AkkaSystem.system.toClassic)))
 .toMat(Sink.ignore)(Consumer.DrainControl.apply)
 .run()
这与我的商业案例所期望的一样,myActor接收命令更新(MyAvro)

我对背压的技术概念更为恼火,据我所知,背压机制部分由水槽控制,但在这种流配置中,我的水槽只是“水槽。忽略”。所以我的水槽对背压有任何作用

当Akka Kafka Stream提交Kafka主题偏移量时,我还好奇什么?命令发送到MyActor邮箱的那一刻?如果是这样的话,那么我如何处理像ask模式这样的场景呢?在ask模式完成之前,卡夫卡偏移量不应该提交

我看到一些处理手动偏移控制的工厂方法“plainPartitionedManualOffsetSource”、“CommitteablePartitionManualOffsetSource”,但我找不到这些方法的任何示例,我可以用我的业务逻辑来决定手动提交偏移吗

作为一种替代配置,我可以使用这样的配置

val myActor: ActorRef[MyActor.Command] = AkkaSystem.sharding.getMyActor
val (control, result) =
  Consumer
    .plainSource(consumerSettings, Subscriptions.topics("myTopic"))
    .toMat(Sink.actorRef(AkkaSystem.sharding.getMyActor(myAvro.getId), null))(Keep.both)
    .run()
现在我可以访问Sink.actorRef了,我认为背压机制有机会控制背压,当然这个代码不会工作,因为我不知道如何在这个星座下访问“myAvro”


答案为Thx。

在第一个流中,基本上没有背压。偏移提交将在消息发送到
myActor
后很快发生

对于背压,您需要等待目标参与者的响应,正如您所说,ask模式是实现这一点的标准方法。由于来自参与者外部的参与者请求(出于所有意图和目的,流都在参与者外部:参与者执行的阶段是一个实现细节)会导致
未来
,这表明需要调用
mapsync

def askUpdate(m: MyAvro): Future[Response] = ???  // get actorref from cluster sharding, send ask, etc.
然后将原始流中的
映射替换为

.mapAsync(parallelism) { consumerRecord =>
  askUpdate(consumeRecord.value().asInstanceOf[MyAvro])
}

mapsync
将“正在运行”的未来限制为
并行性
。如果存在
parallelism
futures(当然是由它产生的),它将产生反压力。如果衍生的未来完成时出现故障(对于ask本身,这通常是一个超时),它将失败;成功期货的结果(关于传入订单)将被传递(通常情况下,这些结果将是
akka.Done
,尤其是当流中只剩下offset commit和
Sink.ignore
)时)。此语句不正确:

。。。据我所知,背压机制部分由水槽控制,但在这种水流配置中,我的水槽只是“水槽。忽略”。所以我的水槽对背压有任何作用

对于背压,
Sink
s没有什么特别之处。作为流量控制机制的背压将自动用于流中存在异步边界的任何地方。这可能在
Sink
中,但也可能在流中的任何其他地方

在你的情况下,你正在连接你的流与演员交谈。这是您的异步边界,但您这样做的方式是使用
映射
,在该映射内使用
与演员对话。因此没有背压,因为:

  • map
    不是一个异步操作符,它内部调用的任何东西都不能参与背压机制。因此,从Akka流的角度来看,没有引入异步边界
  • 是fire and forget,没有提供关于演员有多忙来实施任何背压的反馈

  • 就像Levi提到的那样,你可以做的是从
    告诉
    改变为
    询问
    互动,并让接受者在工作完成时做出回应。然后可以像Levi描述的那样使用
    mapAsync
    。这里
    map
    mapsync
    之间的区别在于
    mapsync
    的语义使得它只有在返回
    Future
    完成时才会向下游发出。即使并行度
    为1,背压仍然有效。如果您的卡夫卡记录比您的演员处理的速度快得多,
    mapsync
    将在等待
    未来
    完成时向上游反压。在这种情况下,我认为增加并行度没有任何意义,因为所有这些消息都将添加到参与者的收件箱中,所以这样做不会真正加快任何速度。如果交互是REST调用,那么它可以提高总体吞吐量。根据参与者处理消息的方式,为
    mapsync
    增加
    并行度可能会导致吞吐量增加
    paralleslism
    value有效地限制了在背压开始之前允许的最大未完成的
    Future
    s数。

    首先感谢您的回答,如果我理解正确,如果“并行度”不大于1,那么将再次没有背压机制。所以下一个问题是,什么是平行性?经典的“核心数量*2”?哪个最有可能与Dispatcher线程池大小相同?以及它如何与卡夫卡的分区交互,这是卡夫卡创建并行性的方式。因此,如果我使用plainPartitionedManualOffsetSource而不是“sourceWitOffsetContext”,并且使用4个Kafka分区和并行度1,我将得到反压力?如果并行度为1,则在发送ask和接收响应之间的一段时间内仍然会有反压力。分区源和mapAsync(1)仍将背压(请注意,为了提高效率,消费者仍会批量读取卡夫卡的内容,并且