何时使用Actor而不是像WebSphere MQ或Tibco Rendezvous这样的消息传递解决方案?

115

我已经阅读了关于什么设计决策会更有利于Scala的Actor而不是JMS?的问题和答案。

通常,我们使用已经存在多年的消息传递解决方案:使用JMS实现,如WebSphere MQ或Apache ActiveMQ用于点对点通信,或使用Tibco Rendevous进行多播消息传递。

它们非常稳定、可靠,并提供高可用性和性能。然而,与Akka相比,配置和设置似乎要复杂得多。

在哪些用例中应该使用Akka,以及何时和为什么应该使用Akka,而之前成功使用的产品——WebSphere MQ或ActiveMQ呢?为什么我应该考虑在我的未来项目中使用Akka,而不是WebSphere MQ或Tibco RV?

那么,什么情况下应该避免使用Akka呢?它是否提供与其他解决方案相同的高可用性和性能?或者将Akka与其他消息中间件进行比较是一个坏主意吗?

也许在JVM环境中还有另一种消息传递解决方案,除了JMS(点对点)、TibcoRV(多播)和Akka,我应该考虑使用它吗?


2
这个链接 https://dev59.com/4m445IYBdhLWcg3w5OEH#4648843 可能会有用。 - srnm
4个回答

96
首先,“旧版”消息系统(MQ)在实现上比较老,但是它们是一种新的工程思想:事务性持久队列。Scala Actors和Akka可能是较新的实现,但是它们建立在旧的Actor并发模型之上,因此两个模型在实践中非常相似:请参见我的回答RabbitMQ vs Akka
如果你只为JVM编码,那么Akka可能是一个不错的选择。否则,我会使用RabbitMQ。
另外,如果你是Scala开发人员,那么Akka应该是一个再简单不过的选择。然而,Akka的Java绑定不太像Java,并且由于Scala的类型系统需要进行强制转换。
此外,在Java中,人们通常不会创建不可变对象,但是我建议您对消息进行不可变性处理。因此,在Java中,很容易无意间使用Akka做一些不可扩展的事情(例如使用可变对象来传递消息,依赖奇怪的闭包回调状态)。使用MQ这不是一个问题,因为消息总是以速度为代价进行序列化。在Akka中,它们通常不会这样做。

Akka在与大量消费者一起使用时比大多数MQ更具扩展性。这是因为对于大多数MQ(JMS、AMQP)客户端,每个队列连接都需要一个线程...因此,许多队列==许多永久运行的线程。虽然这主要是客户端问题,但我认为ActiveMQ Apollo有一个非阻塞调度程序,据称可以解决AMQP的这个问题。RabbitMQ客户端具有允许您组合多个消费者的通道,但仍存在大量消费者可能导致死锁或连接中断的问题,因此通常会添加更多线程以避免此问题。

话虽如此,Akka的远程传输相当新,并且可能仍未提供传统消息队列提供的所有可靠消息保证和QoS(但这正在每天改变)。它通常也是点对点的,但我认为它支持服务器对等,这通常是大多数MQ系统所做的(即单点故障),但也有MQ系统是点对点的(RabbitMQ是服务器对等)。

最后,RabbitMQ和Akka实际上是很好的搭档。您可以使用Akka作为RabbitMQ的包装器,特别是因为RabbitMQ不会帮助您处理消息的消费和本地路由(在单个JVM中)。

何时选择Akka

  • 有大量的消费者(数以百万计)。
  • 需要低延迟。
  • 愿意采用Actor并发模型。

示例系统:一个交互式实时聊天系统

何时选择MQ

  • 需要与许多不同的系统集成(即非JVM)。
  • 消息可靠性比延迟更重要。
  • 希望拥有更多的工具和管理UI。
  • 由于前面的原因,更适合长时间运行的任务。
  • 希望使用不同于Actor的并发模型。

示例系统:一个定时事务批处理系统

基于关注的评论进行编辑

我做了一个假设,即原帖作者关心分布式处理,Akka和消息队列都可以处理。也就是说,我假设他在谈论分布式Akka使用Akka进行本地并发与大多数消息队列的比较是不恰当的。我之所以这么说是因为你可以将消息队列模型作为本地并发模型应用(例如主题、队列、交换),Reactor库和simple-react都可以做到这一点。
选择正确的并发模型/库对于低延迟应用程序非常重要。像消息队列这样的分布式处理解决方案通常不是理想的选择,因为路由几乎总是在网络上完成的,这显然比应用内慢,因此Akka将是更优秀的选择。但是我相信一些专有的MQ技术允许本地路由。另外,正如我之前提到的,大多数MQ客户端对线程处理非常愚蠢,并且不依赖于非阻塞IO,并且每个连接/队列/通道都有一个线程...具有讽刺意味的是,非阻塞IO并不总是低延迟的,但通常更加资源高效。

正如您所看到的,分布式编程和并发编程的主题相当广泛,而且每天都在变化,因此我的初衷不是混淆,而是专注于分布式消息处理的特定领域,这是我认为OP关心的问题。在并发方面,人们可能希望将搜索焦点放在“反应式”编程(RFP /流)上,这是一种与演员模型和消息队列模型类似的“新”模型,所有这些模型通常可以结合使用,因为它们都是基于事件的。


3
我认为对一个错误问题的回答不能是正确的。你不能比较消息队列和并发模型。它们被设计来解决完全不同的任务,只有“消息”这个词是它们共同的特点。 - Igor S.
2
嗯,有点是有点不是。Akka支持分布式消息传递,而且你很容易就能基于消息队列范式(Google Spring的Reactor)构建并发模型。现在唯一的区别是RabbitMQ具有耐久性消息..哦,等等,Akka现在也支持这个。虽然标题中可能会说“Actor”,但明确指出了Akka,它与许多基于消息的系统(同时进行和分布式)有重叠之处。 - Adam Gent
4
顺便说一下,通常与消息队列一起使用的并发模型称为SEDA(分阶段事件驱动架构)。除了使用队列、主题和交换机之外,它本身也是一种并发模型(恰好也是分布式模型,就像演员模型一样)。我也非常讨厌有人说“错误的问题”...除了不合适的问题,什么时候问题会是错的?这种说法很刻薄,也很精英主义。 - Adam Gent
1
我从未说过它们是可以互换的。我甚至说它们在一起工作得很好并解释了原因。但显然他在这里谈论的是分布式akka而不是akka actor库。那是我读到的。你可以编辑我的帖子,因为你的观点是正确的,这可能会让其他人被困惑。 - Adam Gent
1
Akka Java API现在非常干净,特别是使用JDK 8 lambdas。我猜想如果/当他们在JDK 10中引入值对象时,它会变得更好。 - Rob Crawford
显示剩余3条评论

4

我不是消息系统的专家,但您可以将它们与Akka结合在应用程序中,获得最佳效果。以下是一个示例,您可能会发现对于尝试Akka和消息系统非常有用,本例中使用的是ZeroMQ:

https://github.com/zcox/akka-zeromq-java


6
ZeroMQ不完全是一个消息系统,它更像是一种改进的套接字。与ZeroMQ相比,完整的消息系统要复杂得多。您链接的项目似乎只是在使用Akka对ZeroMQ进行了轻量级封装。 - Vladimir Matveev

1
Akka-Camel比ZeroMQ更好的示例- ZeroMQ是直接TCP到TCP通信(因此为零-没有消息队列)。
使用AkkaCamel,您可以抽象出队列并直接从Actor生成/消耗消息,而无需处理消息队列消息推送/拉动的任何代码。
您可以放弃akka-zeromq并直接使用远程Akka。我认为akka-zeromq正从核心库中删除,但我们为akka构建了一个很好的zeromq库,称为scala-zeromq( https://github.com/mDialog/scala-zeromq
Akka有几个关键的核心用例:
1)可变状态
通过将其隐藏在Actor中,易于处理共享状态。由于Actor同步处理消息,因此可以在Actor中保存状态并通过Actor API以高一致性公开该字段。
2)分配

并发在Akka中是免费的,所以你可以说它真正解决的是分布式问题。跨机器和核心的分布。Akka内置了用于在网络上发送消息的“位置透明性”。它具有集群和模式相关,可用于扩展单个服务。这使得它成为分布式的非常好的解决方案(例如微服务架构)。

以下是使用Java8的Akka-Camel与ActiveMQ结合使用的示例:

import akka.actor.Props;
import akka.camel.Camel;
import akka.camel.CamelExtension;
import akka.testkit.TestActorRef;
import akka.testkit.TestProbe;
import org.junit.Ignore;
import org.junit.Test;
import akka.camel.javaapi.UntypedProducerActor;
import akka.camel.javaapi.UntypedConsumerActor;
import static com.rogers.totes.TotesTestFixtures.*;
import org.apache.activemq.camel.component.*;

public class MessagingTest {
    @Test @Ignore
    public void itShouldStoreAMessage() throws Exception{
        String amqUrl = "nio://localhost:61616";
        Camel camel = (Camel) CamelExtension.apply(system);
        camel.context().addComponent("activemq", ActiveMQComponent.activeMQComponent(amqUrl));

        TestProbe probe = TestProbe.apply(system);
        TestActorRef producer = TestActorRef.create(system, Props.create((Producer.class)));
        TestActorRef consumer = TestActorRef.create(system, Props.create((Consumer.class)));
        producer.tell("Produce", probe.ref());

        Thread.sleep(1000);
    }
}

class Producer extends UntypedProducerActor{

    @Override
    public String getEndpointUri() {
        return "activemq:foo.bar";
    }
}

class Consumer extends UntypedConsumerActor{

    @Override
    public String getEndpointUri() {
        return "activemq:foo.bar";
    }

    @Override
    public void onReceive(Object message) throws Exception {
        System.out.println("GOT A MESSAGE!" + message);

    }
}

0
我对提到的工具不太了解,但我熟悉底层的软件工程概念。所以我认为关键在于一个决策线索:去中心化。我的意思是除了分布,所有的XXX-MQ和Akka(作为消息传递架构组件)都可以为您的应用程序带来的还有另一种能力,叫做去中心化。但由于在大多数用例中,去中心化和分布可以相互转化,因此对于您的情况,这种质疑的有效性应首先重新检查。然后问问自己,基于单例还是点对点的基础上处理消息传递需求更适合您的需求。
Akka以点对点的方式完成工作,因此提供了一种去中心化的解决方案,但其他提到的工具提供的是集中化的解决方案。举个例子来澄清,想象一下实现一个需要像比特币那样的消息传递架构的网络。Akka能够支持这样的用例,但其他提到的工具将会失败。所有其他提到的工具也支持消息传递架构,但除了Akka之外,没有一个能够支持比特币的去中心化要求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接