具有持久邮箱的Akka无状态参与者
我想创建一个拥有1000名演员的阿克卡集群。每个参与者都会收到一条消息,进行一些计算,并将结果写入一个专用的卡夫卡主题 它应该部署在集群中,例如Kubernetes 我的理解是,如果参与者被终止(JVM崩溃、重新部署或其他原因),那么其邮箱的内容以及当前正在处理的消息都将丢失 这在我的例子中是完全不可接受的,因此我想实现一种拥有持久邮箱的方法。请注意,参与者本身是无状态的,他们不需要重播消息或重建状态。如果演员被终止,我所需要的就是不要丢失消息 问题是:推荐的方法是什么?他们建议实现持久参与者。但就像我说的,我不需要坚持和恢复演员的任何状态。我应该实现一个基于持久存储(如SQL数据库)的自定义邮箱吗具有持久邮箱的Akka无状态参与者,akka,actor,akka-cluster,akka-persistence,Akka,Actor,Akka Cluster,Akka Persistence,我想创建一个拥有1000名演员的阿克卡集群。每个参与者都会收到一条消息,进行一些计算,并将结果写入一个专用的卡夫卡主题 它应该部署在集群中,例如Kubernetes 我的理解是,如果参与者被终止(JVM崩溃、重新部署或其他原因),那么其邮箱的内容以及当前正在处理的消息都将丢失 这在我的例子中是完全不可接受的,因此我想实现一种拥有持久邮箱的方法。请注意,参与者本身是无状态的,他们不需要重播消息或重建状态。如果演员被终止,我所需要的就是不要丢失消息 问题是:推荐的方法是什么?他们建议实现持久参与者。
我还看到,在某些版本之前,Akka支持“持久”邮箱,这似乎是我所需要的。但出于某种原因,他们删除了它,这让人困惑…你可以用卡夫卡来实现你想要的。卡夫卡主题是持久性的(如果您在卡夫卡中将保留设置为“永远”,或者对主题启用日志压缩,那么数据将“一直”保留,或者您可以在卡夫卡之外存储偏移量) 使用Akka Streams,您将在广播您制作的消息(制作主题)后提交您收到的消息(关于接收主题),从而提供“至少一次”传递语义。(对于“恰好一次”,您可以查看Kafka事务) 这是《阿尔帕卡·卡夫卡文件》中的示例:
Consumer.DrainingControl<Done> control =
Consumer.committableSource(consumerSettings, Subscriptions.topics(topic))
.map(
msg ->
ProducerMessage.single(
new ProducerRecord<>(targetTopic, msg.record().key(), msg.record().value()),
msg.committableOffset() // the passThrough
))
.via(Producer.flexiFlow(producerSettings))
.map(m -> m.passThrough())
.toMat(Committer.sink(committerSettings), Keep.both())
.mapMaterializedValue(Consumer::createDrainingControl)
.run(materializer);
如果您有多个参与者可以同时处理消息,那么您还可以增加
mapsync
调用的并行系数。在客户端上使用持久参与者是此类需求的建议。我理解您是说您的接收参与者不需要持久性/sta但是,通过在客户端上使用持久性,您可以在接收参与者终止时重试,也可以使用开箱即用的保证消息传递功能来确保已对其进行处理。本质上,持久性用于(在客户端)持久化所做的请求,以便客户端可以将消息重新发送到“如有必要,重新生成邮箱
使用客户端持久性是:
- 比持久邮箱性能更好
- 针对更多故障场景(如网络层丢弃的消息、应用程序逻辑中的故障)提供保护
- 更灵活,支持更多类型的恢复(例如:仅需要恢复某些消息的场景)
Consumer.DrainingControl<Done> control =
Consumer.committableSource(consumerSettings, Subscriptions.topics(topic))
.mapAsync(1, msg ->
Patterns.ask(actor, msg, Duration.ofSeconds(5))
.thenApply(done ->
ProducerMessage.single(
new ProducerRecord<>(targetTopic, done.key(), done.value()),
msg.committableOffset() // the passThrough
)
)
)
.via(Producer.flexiFlow(producerSettings))
.map(m -> m.passThrough())
.toMat(Committer.sink(committerSettings), Keep.both())
.mapMaterializedValue(Consumer::createDrainingControl)
.run(materializer);