简单方法调用之间消息驱动应用程序的差异

Differences Message Driven Application between simple method calls

我一直在阅读一些有关的东西,发现一些库真的打乱了我的想法,比如 Akka、Quasar、Reactor 和 Disruptor,Akka 和 Quasar 实现了 Actor 模式,而 Disruptor 是一个线程间消息传递库,而 Reactor 基于 .那么与简单方法调用相比,使用消息驱动架构的优势和用例是什么?

给定一个 RabbitMQ 队列侦听器,我从该方法接收消息,确定 RabbitMQ 消息是哪种类型(NewOrderPayment,...)。

我可以使用消息驱动库。

伪代码:

actor.tell('decider-mailbox',message)

这基本上是说 "I'm putting this message here, when you guys can handle it, do it") 等等,直到它被保存。 演员再次准备好接收另一条消息

但是像messageHandler.handle(message)这样直接调用方法,不是更好更抽象吗?

不同之处在于处理发生在不同的线程中,因此当前线程已准备好接收和转发下一条消息。当您从当前线程调用处理程序时,它会被阻塞,直到处理完成。

在某种程度上,这只是定义抽象的问题。有人说最初的面向对象编程实际上应该是基于消息传递的,并且在对象上调用方法将具有向它发送消息的语义(具有与 actor 中类似的异步非阻塞行为)。

我们在大多数流行语言中实现 OO 的方式是这样的,它变成了今天的样子 - "synchronous blocking order" 到一个对象,受控并且 运行 来自相同的执行上下文(thread/process) 作为调用者。这很好,因为它很容易理解,但在设计并发系统时有其局限性。

理论上,您可以创建一种语法与 Java 相似的语言,但赋予它不同的语义 - 实际上在内部使 object.method(arg)类似于 actor.tell(msg)。有很多习语试图将异步调用和消息传递隐藏在简单的方法调用之后,但一如既往,这取决于用例。

Akka 提供了一个很好的新语法,它清楚地表明我们正在做的事情与在对象上调用方法完全不同,部分原因是为了减少混淆并使消息传递更加明确。最后,您说的是同一件事 - 您正在向系统中的参与者发送消息,但与直接调用其方法之一相比,您这样做的限制更少。

Actors Model looks a lot like people working together; it is based on message-passing but there is much more to it and I'd say that not all message-passing models are the same, for example Quasar 实际上不仅支持类似 Erlang 的 actor,还支持类似 Go 的通道,这些通道更简单但不提供容错模型(顺便说一句,还有光纤,就像线程一样,但很多更轻量级,即使根本没有任何消息传递也可以使用。

Methods/functions 遵循严格的、可嵌套的调用-return(因此是请求-响应)规则并且通常不涉及任何并发(至少在命令式和非纯函数式语言中)。

消息传递相反,从广义上讲,允许更松散的耦合,因为不强制执行请求-响应规则并允许通信各方并发执行,这也有助于隔离故障、热升级和一般维护(例如,Actor 模型提供这些功能)。通常消息传递还允许更宽松的 数据契约 通过对消息使用更动态的类型(这对于每一方的 Actors 模型尤其如此,或 actor, 有一个传入通道,即他的邮箱)。 除此之外,细节在很大程度上取决于您正在考虑的消息 model/solution,例如,通信渠道可以 同步 交互部分或具有 limited/unlimited 缓冲,允许多个来源 and/or 多个生产者和消费者等

请注意 RPC 实际上是消息传递,但具有严格的请求-响应通信规则。

这意味着,根据情况,一个或另一个可能更适合你:methods/functions当你在通话中时更好-return discipline and/or 你只是让你的顺序代码更加模块化。当您需要一个潜在并发的、自治的 "agents" 网络进行通信但不一定在请求-响应规则中时,消息传递会更好。

至于 Actor 模型 我认为你可以建立更多关于它的洞察力,例如 by reading the first part of this blog post (notice: I'm the main author of the post and I'm part of the Parallel Universe - 和 Quasar - 开发团队):

The actor model is a design pattern for fault-tolerant and highly scalable systems. Actors are independent worker-modules that communicate with other actors only through message-passing, can fail in isolation from other actors but can monitor other actors for failure and take some recovery measures when that happens. Actors are simple, isolated yet coordinated, concurrent workers.

Actor-based design brings many benefits:

  • Adaptive behaviour: interacting only through a message-queue makes actors loosely coupled and allows them to:
    • Isolate faults: mailboxes are decoupling message queues that allow actor restart without service disruption.
    • Manage evolution: they enable actor replacement without service disruption.
    • Regulate concurrency: receiving messages very often and discarding overflow or, alternatively, increasing mailbox size can maximize concurrency at the expense of reliability or memory usage respectively.
    • Regulate load: reducing the frequency of receive calls and using small mailboxes reduces concurrency and increases latencies, applying back-pressure through the boundaries of the actor system.
  • Maximum concurrency capacity:
    • Actors are extremely lightweight both in memory consumption and management overhead, so it’s possible to spawn even millions in a single box.
    • Because actors do not share state, they can safely run in parallel.
  • Low complexity:
    • Each actor can implements stateful behaviour by mutating its private state without worrying about concurrent modification.
    • Actors can simplify their state transition logic by selectively receiving messages from the mailbox in logical, rather than arrival order.