嗨,我正在与akka streams
和akka-stream-kafka
一起工作。我正在使用以下设置来设置Stream:
Source (Kafka) --> | Akka Actor Flow | --> Sink (MongoDB)
Actor Flow
基本上是由Actor处理数据,以下是层次结构:
System
|
Master Actor
/ \
URLTypeHandler SerializedTypeHandler
/ \ |
Type1Handler Type2Handler SomeOtherHandler
Kafka收到消息后,我写下了使用者并以atMostOnceSource
配置运行并使用
Consumer.Control control =
Consumer.atMostOnceSource(consumerSettings, Subscriptions.topics(TOPIC))
.mapAsyncUnordered(10, record -> processAccessLog(rootHandler, record.value()))
.to(Sink.foreach(it -> System.out.println("FinalReturnedString--> " + it)))
.run(materializer);
我最初使用打印作为接收器,只是为了使流程运行。
和processAccessLog
定义为:
private static CompletionStage<String> processAccessLog(ActorRef handler, byte[] value) {
handler.tell(value, ActorRef.noSender());
return CompletableFuture.completedFuture("");
}
现在,当演员期望响应时,必须使用定义ask
,在这种情况下,这很有意义,因为我想返回要写入接收器中的值。
但是每个人(包括文档)都提到要避免使用ask
,而要使用tell
和forward
,Don't Ask, Tell上写着一个很棒的博客。
他在博客中提到,如果角色嵌套,则对第一条消息使用tell
,然后对消息使用forward
到达目的地,然后在处理后直接将消息发送回根演员。
现在这是问题所在,
答案 0 :(得分:1)
ask
仍然是正确的模式
在链接的博客文章中,ask
的一个“缺点”是:
阻止演员本身,直到演员不能选择任何新消息 响应到达并完成处理。
但是,在akka-stream
中,这正是我们正在寻找的确切功能,也就是“背压”。如果Flow
或Sink
花费很长时间来处理数据,那么我们希望Source
变慢。
作为一个旁注,我认为博客文章中声称附加监听器Actor
导致实现“数十倍重”的实现是夸张的。显然,中间的Actor增加了一些延迟开销,但没有增加12x
。
消除背压
您所寻找的任何实现都可以有效消除背压。仅使用tell
的中间流将连续将需求传播回源,而不管您的处理逻辑是否在处理程序Actors中以与源生成数据相同的速度完成其计算。
考虑一个极端的例子:如果您的Source每秒可以产生100万条消息,但是Actor通过tell
接收到这些消息,则每秒只能处理1条消息。该Actor的邮箱会发生什么?
通过using the ask pattern in an intermediate Flow,您有目的地链接处理程序的速度和Source生成数据的速度。
如果您想删除从接收器到源的反压信号,那么您最好也不要使用akka流。您可以使用反压力消息,也可以使用非阻塞消息,但不能同时使用。
答案 1 :(得分:1)
Ramon J Romero和Vigil是正确的,但我会尝试扩大答复。
1)我认为“不问,不说”教条主要用于Actor系统架构。在这里,您需要返回一个Future,以便流可以解析处理后的结果,您有两个选择:
getSender
方法,以便D可以将响应发送给A)。无法在消息中发送Promise或Future(不可序列化),因此无法避免这种短暂的演员的创建。最后,您的操作几乎相同...
2)最好使用一个空的接收器来完成流的确定(实际上akka提供了Sink.ignore()
方法)。
似乎您正在丢失使用流的原因,它们是很酷的抽象,可提供可组合性,并发性和背压。另一方面,演员无法构成,很难应对背压。如果您不需要此功能,并且演员可以轻松完成工作,则不应该首先使用Akka流。